diff --git a/.gitignore b/.gitignore index b977b7b5b60c..2345a7bbbc12 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ node_modules node build cache/ +*.mmdb diff --git a/src/test/java/org/graylog/plugins/map/ConditionalRunner.java b/src/test/java/org/graylog/plugins/map/ConditionalRunner.java new file mode 100644 index 000000000000..c90264daa945 --- /dev/null +++ b/src/test/java/org/graylog/plugins/map/ConditionalRunner.java @@ -0,0 +1,84 @@ +package org.graylog.plugins.map; + +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * JUnit test runner based on {@link BlockJUnit4ClassRunner} that disables tests based on condition annotations. + *

+ * Supported conditions: + *

+ */ +public class ConditionalRunner extends BlockJUnit4ClassRunner { + private static final Logger LOG = LoggerFactory.getLogger(ConditionalRunner.class); + + public ConditionalRunner(Class clazz) throws InitializationError { + super(clazz); + } + + private List> getClassAndSuperclasses(final Class testClass) { + final List> classes = new ArrayList<>(); + + Class clazz = testClass; + while (clazz != null) { + classes.add(clazz); + clazz = clazz.getSuperclass(); + } + + return classes; + } + + private Stream missingResourcesStream(final ResourceExistsCondition condition, final Class clazz) { + if (condition == null) { + return Stream.empty(); + } + + final Stream resources = Arrays.stream(condition.value()); + + // Return all missing resources + return resources.filter(resource -> clazz.getResource(resource) == null); + } + + private Set missingResources(final FrameworkMethod method) { + // Check method class and all its superclasses for annotations + final Stream missingClassResourcesStream = getClassAndSuperclasses(method.getDeclaringClass()).stream() + .flatMap(clazz -> { + final ResourceExistsCondition condition = clazz.getAnnotation(ResourceExistsCondition.class); + + return missingResourcesStream(condition, clazz); + }); + + // Check this method for annotations + final ResourceExistsCondition methodCondition = method.getAnnotation(ResourceExistsCondition.class); + + return Stream.concat( + missingClassResourcesStream, + missingResourcesStream(methodCondition, method.getDeclaringClass()) + ).collect(Collectors.toSet()); + } + + @Override + protected boolean isIgnored(FrameworkMethod child) { + final Set missingResources = missingResources(child); + + if (!missingResources.isEmpty()) { + LOG.warn("Not running test {}#{}() because of missing resources: {}", + child.getDeclaringClass().getCanonicalName(), child.getName(), missingResources); + return true; + } + + return super.isIgnored(child); + } +} diff --git a/src/test/java/org/graylog/plugins/map/ResourceExistsCondition.java b/src/test/java/org/graylog/plugins/map/ResourceExistsCondition.java new file mode 100644 index 000000000000..9a30c16be2f2 --- /dev/null +++ b/src/test/java/org/graylog/plugins/map/ResourceExistsCondition.java @@ -0,0 +1,29 @@ +package org.graylog.plugins.map; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Can be used in conjunction with {@link ConditionalRunner} to disable tests if one or more resources doesn't exist. + *

+ * Example: + *

{@code
+ *    @literal @RunWith(ConditionalRunner.class)
+ *    @literal @ResourceExistsCondition({"/file1.txt", "/file2.txt"})
+ *     public class GeoIpResolverEngineTest {
+ *        @literal @Test
+ *        @literal @ResourceExistsCondition("/file3.txt")
+ *         public void test() {
+ *         }
+ *     }
+ * }
+ */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ResourceExistsCondition { + /** List of resources that must exist to run the tests. */ + String[] value(); +} + diff --git a/src/test/java/org/graylog/plugins/map/geoip/GeoIpResolverEngineTest.java b/src/test/java/org/graylog/plugins/map/geoip/GeoIpResolverEngineTest.java index 139a42645c79..47cb32667998 100644 --- a/src/test/java/org/graylog/plugins/map/geoip/GeoIpResolverEngineTest.java +++ b/src/test/java/org/graylog/plugins/map/geoip/GeoIpResolverEngineTest.java @@ -21,11 +21,14 @@ import com.eaio.uuid.UUID; import com.google.common.collect.Maps; import com.google.common.net.InetAddresses; +import org.graylog.plugins.map.ConditionalRunner; +import org.graylog.plugins.map.ResourceExistsCondition; import org.graylog.plugins.map.config.GeoIpResolverConfig; import org.graylog2.plugin.Message; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import java.net.URISyntaxException; import java.util.Map; @@ -37,7 +40,10 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +@RunWith(ConditionalRunner.class) +@ResourceExistsCondition(GeoIpResolverEngineTest.GEO_LITE2_CITY_MMDB) public class GeoIpResolverEngineTest { + static final String GEO_LITE2_CITY_MMDB = "/GeoLite2-City.mmdb"; private MetricRegistry metricRegistry; private GeoIpResolverConfig config; @@ -55,7 +61,7 @@ public void tearDown() { } private String getTestDatabasePath() throws URISyntaxException { - return this.getClass().getResource("/GeoLite2-City.mmdb").toURI().getPath(); + return this.getClass().getResource(GEO_LITE2_CITY_MMDB).toURI().getPath(); } @Test diff --git a/src/test/java/org/graylog/plugins/map/geoip/MaxmindDataAdapterTest.java b/src/test/java/org/graylog/plugins/map/geoip/MaxmindDataAdapterTest.java index 3c15339f2aeb..6e8db2c18e4c 100644 --- a/src/test/java/org/graylog/plugins/map/geoip/MaxmindDataAdapterTest.java +++ b/src/test/java/org/graylog/plugins/map/geoip/MaxmindDataAdapterTest.java @@ -5,7 +5,8 @@ import com.maxmind.geoip2.DatabaseReader; import com.maxmind.geoip2.model.CityResponse; import com.maxmind.geoip2.model.CountryResponse; -import com.maxmind.geoip2.record.City; +import org.graylog.plugins.map.ConditionalRunner; +import org.graylog.plugins.map.ResourceExistsCondition; import org.graylog.plugins.map.config.DatabaseType; import org.graylog2.plugin.lookup.LookupResult; import org.junit.After; @@ -29,9 +30,12 @@ MaxmindDataAdapterTest.CountryDatabaseTest.class }) public class MaxmindDataAdapterTest { + private static final String GEO_LITE2_CITY_MMDB = "/GeoLite2-City.mmdb"; + private static final String GEO_LITE2_COUNTRY_MMDB = "/GeoLite2-Country.mmdb"; + private static final Map DB_PATH = ImmutableMap.of( - DatabaseType.MAXMIND_CITY, "/GeoLite2-City.mmdb", - DatabaseType.MAXMIND_COUNTRY, "/GeoLite2-Country.mmdb" + DatabaseType.MAXMIND_CITY, GEO_LITE2_CITY_MMDB, + DatabaseType.MAXMIND_COUNTRY, GEO_LITE2_COUNTRY_MMDB ); abstract static class Base { @@ -99,6 +103,8 @@ public void doGetReturnsEmptyResultForInvalidIPAddress() { } } + @RunWith(ConditionalRunner.class) + @ResourceExistsCondition(GEO_LITE2_CITY_MMDB) public static class CityDatabaseTest extends Base { public CityDatabaseTest() { super(DatabaseType.MAXMIND_CITY); @@ -138,6 +144,8 @@ public void doGetReturnsResultIfCityResponseFieldsAreNull() throws Exception { } } + @RunWith(ConditionalRunner.class) + @ResourceExistsCondition(GEO_LITE2_COUNTRY_MMDB) public static class CountryDatabaseTest extends Base { public CountryDatabaseTest() { super(DatabaseType.MAXMIND_COUNTRY); diff --git a/src/test/resources/GeoLite2-City.mmdb b/src/test/resources/GeoLite2-City.mmdb deleted file mode 100644 index 09b200f350c0..000000000000 Binary files a/src/test/resources/GeoLite2-City.mmdb and /dev/null differ diff --git a/src/test/resources/GeoLite2-Country.mmdb b/src/test/resources/GeoLite2-Country.mmdb deleted file mode 100644 index 56e7fdc043b0..000000000000 Binary files a/src/test/resources/GeoLite2-Country.mmdb and /dev/null differ