// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui.mappaint.mapcss;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import org.openstreetmap.josm.JOSMFixture;
import org.openstreetmap.josm.PerformanceTestUtils;
import org.openstreetmap.josm.PerformanceTestUtils.PerformanceTestTimer;
import org.openstreetmap.josm.data.osm.OsmDataGenerator;
import org.openstreetmap.josm.data.osm.OsmDataGenerator.KeyValueDataGenerator;
import org.openstreetmap.josm.gui.mappaint.MultiCascade;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Tests how fast {@link MapCSSStyleSource} finds the right style candidates for one object.
* @author Michael Zangl
*/
public class MapCSSStyleSourceFilterTest {
private static final int TEST_RULE_COUNT = 10000;
private static class CssGenerator {
StringBuilder sb = new StringBuilder();
private KeyValueDataGenerator generator;
/**
* Create a new CSS generator.
* @param generator A generator to get the keys from.
*/
CssGenerator(KeyValueDataGenerator generator) {
this.generator = generator;
}
private CssGenerator addKeyValueRules(int count) {
for (int i = 0; i < count; i++) {
String key = generator.randomKey();
String value = generator.randomValue();
addRule("node[\"" + key + "\"=\"" + value + "\"]");
}
return this;
}
private CssGenerator addKeyRegexpRules(int count) {
for (int i = 0; i < count; i++) {
String key = generator.randomKey();
String value = generator.randomValue();
value = value.substring(i % value.length());
addRule("node[\"" + key + "\"=~/.*" + value + ".*/]");
}
return this;
}
public CssGenerator addHasKeyRules(int count) {
for (int i = 0; i < count; i++) {
String key = generator.randomKey();
addRule("node[\"" + key + "\"]");
}
return this;
}
public CssGenerator addIsTrueRules(int count) {
for (int i = 0; i < count; i++) {
String key = generator.randomKey();
addRule("node[\"" + key + "\"?]");
}
return this;
}
private void addRule(String selector) {
sb.append(selector + " {}\n");
}
public String getCss() {
return sb.toString();
}
}
private static final int APPLY_CALLS = 100000;
/**
* Global timeout applied to all test methods.
*/
@Rule
@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public Timeout globalTimeout = Timeout.seconds(15*60);
/**
* Prepare the test.
*/
@BeforeClass
public static void createJOSMFixture() {
JOSMFixture.createPerformanceTestFixture().init(true);
}
/**
* Time how long it takes to evaluate [key=value] rules
*/
@Test
public void testKeyValueRules() {
KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
data.generateDataSet();
CssGenerator css = new CssGenerator(data).addKeyValueRules(TEST_RULE_COUNT);
runTest(data, css, "only key=value rules");
}
/**
* Time how long it takes to evaluate [key] rules
*/
@Test
public void testKeyOnlyRules() {
KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
data.generateDataSet();
CssGenerator css = new CssGenerator(data).addHasKeyRules(TEST_RULE_COUNT);
runTest(data, css, "only has key rules");
}
/**
* Time how long it takes to evaluate [key=~...] rules
*/
@Test
public void testRegularExpressionRules() {
KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
data.generateDataSet();
CssGenerator css = new CssGenerator(data).addKeyRegexpRules(TEST_RULE_COUNT);
runTest(data, css, "regular expressions");
}
/**
* Time how long it takes to evaluate [key?] rules
*/
@Test
public void testIsTrueRules() {
KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
data.generateDataSet();
CssGenerator css = new CssGenerator(data).addIsTrueRules(TEST_RULE_COUNT);
runTest(data, css, "is true");
}
private void runTest(KeyValueDataGenerator data, CssGenerator css, String description) {
MapCSSStyleSource source = new MapCSSStyleSource(css.getCss());
PerformanceTestTimer timer = PerformanceTestUtils.startTimer("MapCSSStyleSource#loadStyleSource(...) for " + description);
source.loadStyleSource();
timer.done();
timer = PerformanceTestUtils.startTimer(APPLY_CALLS + "x MapCSSStyleSource#apply(...) for " + description);
for (int i = 0; i < APPLY_CALLS; i++) {
MultiCascade mc = new MultiCascade();
source.apply(mc, data.randomNode(), 1, false);
}
timer.done();
}
}