/*
* Copyright © 2015 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.etl.common;
import co.cask.cdap.etl.api.Emitter;
import co.cask.cdap.etl.api.InvalidEntry;
import co.cask.cdap.etl.api.Transform;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
*/
public class TransformExecutorTest {
@Test
public void testEmptyTransforms() throws Exception {
Map<String, TransformDetail> transformationMap = new HashMap<>();
transformationMap.put("sink", new TransformDetail(new DoubleToString(),
new ArrayList<String>()));
TransformExecutor executor =
new TransformExecutor(transformationMap, ImmutableSet.of("sink"));
TransformResponse transformResponse = executor.runOneIteration(1d);
Map<String, Collection<Object>> sinkResult = transformResponse.getSinksResults();
Assert.assertTrue(sinkResult.containsKey("sink"));
Collection<Object> sinkResultList = sinkResult.get("sink");
Assert.assertEquals(1, sinkResultList.size());
// note : sink transform would have exectued, so the expected is string and not integer
Assert.assertEquals("1.0", sinkResultList.iterator().next());
executor.resetEmitter();
}
@Test
public void testTransforms() throws Exception {
MockMetrics mockMetrics = new MockMetrics();
Map<String, TransformDetail> transformationMap = new HashMap<>();
transformationMap.put("transform1",
new TransformDetail(
new TrackedTransform<>(new IntToDouble(),
new DefaultStageMetrics(mockMetrics, "transform1")),
ImmutableList.of("transform2", "sink1")));
transformationMap.put("transform2",
new TransformDetail(
new TrackedTransform<>(new Filter(100d, Threshold.LOWER),
new DefaultStageMetrics(mockMetrics, "transform2")),
ImmutableList.of("sink2")));
transformationMap.put("sink1",
new TransformDetail(
new TrackedTransform<>(new DoubleToString(),
new DefaultStageMetrics(mockMetrics, "sink1")),
ImmutableList.<String>of()));
transformationMap.put("sink2",
new TransformDetail(
new TrackedTransform<>(new DoubleToString(),
new DefaultStageMetrics(mockMetrics, "sink2")),
ImmutableList.<String>of()));
TransformExecutor<Integer> executor = new TransformExecutor<>(transformationMap, ImmutableSet.of("transform1"));
TransformResponse transformResponse = executor.runOneIteration(1);
assertResults(transformResponse.getSinksResults(), ImmutableMap.of("sink1", 3, "sink2", 0));
assertResults(transformResponse.getMapTransformIdToErrorEmitter(), ImmutableMap.of("transform2", 3));
Assert.assertEquals(3, mockMetrics.getCount("transform1.records.out"));
Assert.assertEquals(0, mockMetrics.getCount("transform2.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("sink1.records.out"));
Assert.assertEquals(0, mockMetrics.getCount("sink2.records.out"));
executor.resetEmitter();
mockMetrics.clearMetrics();
transformResponse = executor.runOneIteration(10);
assertResults(transformResponse.getSinksResults(), ImmutableMap.of("sink1", 3, "sink2", 1));
assertResults(transformResponse.getMapTransformIdToErrorEmitter(), ImmutableMap.of("transform2", 2));
Assert.assertEquals(3, mockMetrics.getCount("transform1.records.out"));
Assert.assertEquals(1, mockMetrics.getCount("transform2.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("sink1.records.out"));
Assert.assertEquals(1, mockMetrics.getCount("sink2.records.out"));
executor.resetEmitter();
mockMetrics.clearMetrics();
transformResponse = executor.runOneIteration(100);
assertResults(transformResponse.getSinksResults(), ImmutableMap.of("sink1", 3, "sink2", 2));
assertResults(transformResponse.getMapTransformIdToErrorEmitter(), ImmutableMap.of("transform2", 1));
Assert.assertEquals(3, mockMetrics.getCount("transform1.records.out"));
Assert.assertEquals(2, mockMetrics.getCount("transform2.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("sink1.records.out"));
Assert.assertEquals(2, mockMetrics.getCount("sink2.records.out"));
executor.resetEmitter();
mockMetrics.clearMetrics();
transformResponse = executor.runOneIteration(2000);
assertResults(transformResponse.getSinksResults(), ImmutableMap.of("sink1", 3, "sink2", 3));
assertResults(transformResponse.getMapTransformIdToErrorEmitter(), new HashMap<String, Integer>());
Assert.assertEquals(3, mockMetrics.getCount("transform1.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("transform2.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("sink1.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("sink2.records.out"));
executor.resetEmitter();
mockMetrics.clearMetrics();
}
@Test
public void testTransformsWithMerge() throws Exception {
MockMetrics mockMetrics = new MockMetrics();
Map<String, TransformDetail> transformationMap = new HashMap<>();
transformationMap.put("conversion",
new TransformDetail(
new TrackedTransform<>(new IntToDouble(),
new DefaultStageMetrics(mockMetrics, "conversion")),
ImmutableList.of("filter1", "filter2")));
transformationMap.put("filter1",
new TransformDetail(
new TrackedTransform<>(new Filter(100d, Threshold.LOWER),
new DefaultStageMetrics(mockMetrics, "filter1")),
ImmutableList.of("limiter1", "sink1")));
transformationMap.put("filter2",
new TransformDetail(
new TrackedTransform<>(new Filter(1000d, Threshold.LOWER),
new DefaultStageMetrics(mockMetrics, "filter2")),
ImmutableList.of("limiter1", "sink2")));
transformationMap.put("limiter1",
new TransformDetail(
new TrackedTransform<>(new Filter(5000d, Threshold.UPPER),
new DefaultStageMetrics(mockMetrics, "limiter1")),
ImmutableList.of("sink3")));
transformationMap.put("sink1",
new TransformDetail(
new TrackedTransform<>(new DoubleToString(),
new DefaultStageMetrics(mockMetrics, "sink1")),
ImmutableList.<String>of()));
transformationMap.put("sink2",
new TransformDetail(
new TrackedTransform<>(new DoubleToString(),
new DefaultStageMetrics(mockMetrics, "sink2")),
ImmutableList.<String>of()));
transformationMap.put("sink3",
new TransformDetail(
new TrackedTransform<>(new DoubleToString(),
new DefaultStageMetrics(mockMetrics, "sink3")),
ImmutableList.<String>of()));
TransformExecutor<Integer> executor = new TransformExecutor<>(transformationMap,
ImmutableSet.of("conversion"));
TransformResponse transformResponse = executor.runOneIteration(200);
assertResults(transformResponse.getSinksResults(), ImmutableMap.of("sink1", 3, "sink2", 2, "sink3", 3));
assertResults(transformResponse.getMapTransformIdToErrorEmitter(), ImmutableMap.of("filter2", 1, "limiter1", 2));
Assert.assertEquals(3, mockMetrics.getCount("filter1.records.in"));
Assert.assertEquals(3, mockMetrics.getCount("filter1.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("filter2.records.in"));
Assert.assertEquals(2, mockMetrics.getCount("filter2.records.out"));
Assert.assertEquals(5, mockMetrics.getCount("limiter1.records.in"));
Assert.assertEquals(3, mockMetrics.getCount("limiter1.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("sink1.records.out"));
Assert.assertEquals(2, mockMetrics.getCount("sink2.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("sink3.records.out"));
}
@Test
public void testTransformsWithMergeWithMultipleStarts() throws Exception {
MockMetrics mockMetrics = new MockMetrics();
Map<String, TransformDetail> transformationMap = new HashMap<>();
transformationMap.put("filter1",
new TransformDetail(
new TrackedTransform<>(new Filter(100d, Threshold.LOWER),
new DefaultStageMetrics(mockMetrics, "filter1")),
ImmutableList.of("limiter1", "sink1")));
transformationMap.put("filter2",
new TransformDetail(
new TrackedTransform<>(new Filter(1000d, Threshold.LOWER),
new DefaultStageMetrics(mockMetrics, "filter2")),
ImmutableList.of("limiter1", "sink2")));
transformationMap.put("limiter1",
new TransformDetail(
new TrackedTransform<>(new Filter(5000d, Threshold.UPPER),
new DefaultStageMetrics(mockMetrics, "limiter1")),
ImmutableList.of("sink3")));
transformationMap.put("sink1",
new TransformDetail(
new TrackedTransform<>(new DoubleToString(),
new DefaultStageMetrics(mockMetrics, "sink1")),
ImmutableList.<String>of()));
transformationMap.put("sink2",
new TransformDetail(
new TrackedTransform<>(new DoubleToString(),
new DefaultStageMetrics(mockMetrics, "sink2")),
ImmutableList.<String>of()));
transformationMap.put("sink3",
new TransformDetail(
new TrackedTransform<>(new DoubleToString(),
new DefaultStageMetrics(mockMetrics, "sink3")),
ImmutableList.<String>of()));
TransformExecutor<Double> executor = new TransformExecutor<>(transformationMap,
ImmutableSet.of("filter1", "filter2"));
executor.runOneIteration(200d);
executor.runOneIteration(2000d);
TransformResponse transformResponse = executor.runOneIteration(20000d);
assertResults(transformResponse.getSinksResults(), ImmutableMap.of("sink1", 3, "sink2", 2, "sink3", 3));
assertResults(transformResponse.getMapTransformIdToErrorEmitter(), ImmutableMap.of("filter2", 1, "limiter1", 2));
Assert.assertEquals(3, mockMetrics.getCount("filter1.records.in"));
Assert.assertEquals(3, mockMetrics.getCount("filter1.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("filter2.records.in"));
Assert.assertEquals(2, mockMetrics.getCount("filter2.records.out"));
Assert.assertEquals(5, mockMetrics.getCount("limiter1.records.in"));
Assert.assertEquals(3, mockMetrics.getCount("limiter1.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("sink1.records.out"));
Assert.assertEquals(2, mockMetrics.getCount("sink2.records.out"));
Assert.assertEquals(3, mockMetrics.getCount("sink3.records.out"));
}
private <T> void assertResults(Map<String, Collection<T>> results, Map<String, Integer> expectedListsSize) {
Assert.assertEquals(expectedListsSize.size(), results.size());
for (Map.Entry<String, Integer> entry : expectedListsSize.entrySet()) {
Assert.assertTrue(results.containsKey(entry.getKey()));
Assert.assertEquals(entry.getValue().intValue(), results.get(entry.getKey()).size());
}
}
private static class IntToDouble extends Transform<Integer, Double> {
@Override
public void transform(Integer input, Emitter<Double> emitter) throws Exception {
emitter.emit(input.doubleValue());
emitter.emit(10 * input.doubleValue());
emitter.emit(100 * input.doubleValue());
}
}
private enum Threshold {
LOWER,
UPPER
}
private static class Filter extends Transform<Double, Double> {
private final Double threshold;
private final Threshold thresholdType;
public Filter(Double threshold, Threshold thresholdType) {
this.threshold = threshold;
this.thresholdType = thresholdType;
}
@Override
public void transform(Double input, Emitter<Double> emitter) throws Exception {
if (thresholdType.equals(Threshold.LOWER)) {
if (input > threshold) {
emitter.emit(input);
} else {
emitter.emitError(new InvalidEntry<>(100, "less than threshold ", input));
}
} else {
if (input < threshold) {
emitter.emit(input);
} else {
emitter.emitError(new InvalidEntry<>(200, "greater than limit ", input));
}
}
}
}
private static class DoubleToString extends Transform<Double, String> {
@Override
public void transform(Double input, Emitter<String> emitter) throws Exception {
emitter.emit(String.valueOf(input));
}
}
}