From 49b1106fc78396c43de490f148a7bb15234f9b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 22 Feb 2017 19:18:56 +0100 Subject: [PATCH] Tests: Add unit test for InternalScriptedMetric Relates to #22278 --- .../scripted/InternalScriptedMetric.java | 13 +++ .../InternalAggregationTestCase.java | 24 ++-- .../scripted/InternalScriptedMetricTests.java | 110 ++++++++++++++++++ 3 files changed, 139 insertions(+), 8 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetric.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetric.java index bb8e1ac48d399..73975b25778df 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetric.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetric.java @@ -35,6 +35,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; public class InternalScriptedMetric extends InternalAggregation implements ScriptedMetric { private final Script reduceScript; @@ -126,4 +127,16 @@ public XContentBuilder doXContentBody(XContentBuilder builder, Params params) th return builder.field("value", aggregation()); } + @Override + protected boolean doEquals(Object obj) { + InternalScriptedMetric other = (InternalScriptedMetric) obj; + return Objects.equals(reduceScript, other.reduceScript) && + Objects.equals(aggregation, other.aggregation); + } + + @Override + protected int doHashCode() { + return Objects.hash(reduceScript, aggregation); + } + } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index 4e2ab0188c98e..32e9e4c6c7c01 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.SearchModule; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.AbstractWireSerializingTestCase; @@ -36,9 +37,7 @@ public abstract class InternalAggregationTestCase private final NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry( new SearchModule(Settings.EMPTY, false, emptyList()).getNamedWriteables()); - protected abstract T createTestInstance(String name, - List pipelineAggregators, - Map metaData); + protected abstract T createTestInstance(String name, List pipelineAggregators, Map metaData); /** Return an instance on an unmapped field. */ protected T createUnmappedInstance(String name, @@ -57,21 +56,30 @@ public final void testReduceRandom() { inputs.add(t); toReduce.add(t); } - if (randomBoolean()) { - // we leave at least one in the list - List internalAggregations = randomSubsetOf(randomIntBetween(1, toReduceSize), toReduce); - InternalAggregation.ReduceContext context = new InternalAggregation.ReduceContext(null, null, false); + ScriptService mockScriptService = mockScriptService(); + if (randomBoolean() && toReduce.size() > 1) { + // we leave at least the first element in the list + List internalAggregations = randomSubsetOf(randomIntBetween(1, toReduceSize - 1), + toReduce.subList(1, toReduceSize)); + InternalAggregation.ReduceContext context = new InternalAggregation.ReduceContext(null, mockScriptService, false); @SuppressWarnings("unchecked") T reduced = (T) inputs.get(0).reduce(internalAggregations, context); toReduce.removeAll(internalAggregations); toReduce.add(reduced); } - InternalAggregation.ReduceContext context = new InternalAggregation.ReduceContext(null, null, true); + InternalAggregation.ReduceContext context = new InternalAggregation.ReduceContext(null, mockScriptService, true); @SuppressWarnings("unchecked") T reduced = (T) inputs.get(0).reduce(toReduce, context); assertReduced(reduced, inputs); } + /** + * overwrite in tests that need it + */ + protected ScriptService mockScriptService() { + return null; + } + protected abstract void assertReduced(T reduced, List inputs); @Override diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java new file mode 100644 index 0000000000000..b82e822f6b04c --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/scripted/InternalScriptedMetricTests.java @@ -0,0 +1,110 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.aggregations.metrics.scripted; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.script.MockScriptEngine; +import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptContextRegistry; +import org.elasticsearch.script.ScriptEngineRegistry; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.script.ScriptSettings; +import org.elasticsearch.script.ScriptType; +import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class InternalScriptedMetricTests extends InternalAggregationTestCase { + + private static final String REDUCE_SCRIPT_NAME = "reduceScript"; + // randomized only once so that any random test instance has the same value + private boolean hasReduceScript = randomBoolean(); + + @Override + protected InternalScriptedMetric createTestInstance(String name, List pipelineAggregators, + Map metaData) { + Map params = new HashMap<>(); + if (randomBoolean()) { + params.put(randomAsciiOfLength(5), randomAsciiOfLength(5)); + } + Script reduceScript = null; + if (hasReduceScript) { + reduceScript = new Script(ScriptType.INLINE, MockScriptEngine.NAME, REDUCE_SCRIPT_NAME, params); + } + return new InternalScriptedMetric(name, randomAsciiOfLength(5), reduceScript, pipelineAggregators, metaData); + } + + /** + * Mock of the script service. The script that is run looks at the + * "_aggs" parameter visible when executing the script and simply returns the count. + * This should be equal to the number of input InternalScriptedMetrics that are reduced + * in total. + */ + @Override + protected ScriptService mockScriptService() { + Settings settings = Settings.builder() + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) + // no file watching, so we don't need a ResourceWatcherService + .put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING.getKey(), "false") + .build(); + // mock script always retuns the size of the input aggs list as result + @SuppressWarnings("unchecked") + MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME, + Collections.singletonMap(REDUCE_SCRIPT_NAME, script -> { + return ((List) script.get("_aggs")).size(); + })); + ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Collections.singletonList(scriptEngine)); + ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.emptyList()); + ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry); + try { + return new ScriptService(settings, new Environment(settings), null, scriptEngineRegistry, scriptContextRegistry, + scriptSettings); + } catch (IOException e) { + throw new ElasticsearchException(e); + } + } + + @Override + protected void assertReduced(InternalScriptedMetric reduced, List inputs) { + InternalScriptedMetric firstAgg = inputs.get(0); + assertEquals(firstAgg.getName(), reduced.getName()); + assertEquals(firstAgg.pipelineAggregators(), reduced.pipelineAggregators()); + assertEquals(firstAgg.getMetaData(), reduced.getMetaData()); + if (hasReduceScript) { + assertEquals(inputs.size(), reduced.aggregation()); + } else { + assertEquals(inputs.size(), ((List) reduced.aggregation()).size()); + } + } + + @Override + protected Reader instanceReader() { + return InternalScriptedMetric::new; + } + +}