Java tutorial
/* * This file is a component of thundr, a software library from 3wks. * Read more: http://3wks.github.io/thundr/ * Copyright (C) 2014 3wks, <thundr@3wks.com.au> * * 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 com.threewks.thundr.pipeline; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Arrays; import java.util.HashSet; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class PipelineTest { private String source = "First,Second"; @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void shouldRunBasicPipeline() { // @formatter:off List<String> results = Pipeline.of(new Op<InputStream, String>() { @Override public void process(List<InputStream> in, List<String> into) { InputStream first = in.get(0); String string = readStringFromStream(first); into.addAll(Arrays.asList(StringUtils.split(string, ","))); } }).then(new BaseOp<String, String>() { @Override public String processSingle(String in) { return StringUtils.reverse(in); } }).then(new BaseOp<String, String>() { @Override public String processSingle(String in) { System.out.println(in); return in; } }).list(new ByteArrayInputStream(source.getBytes())); // @formatter:on assertThat(results.size(), is(2)); assertThat(results.get(0), is("tsriF")); assertThat(results.get(1), is("dnoceS")); } @Test public void shouldRunInBatches() { List<Integer> inputs = Pipeline.of(new GenerateIntegerInputs(500)).list(); BatchSizeCounter<Integer> batchSizeCounter1 = new BatchSizeCounter<>(); // @formatter:off Pipeline.of(batchSizeCounter1).batchSize(200).list(inputs); // @formatter:on assertThat(batchSizeCounter1.batches, is(3)); assertThat(batchSizeCounter1.maxBatchSize, is(200)); assertThat(batchSizeCounter1.minBatchSize, is(100)); } @Test public void shouldRunBatchesAllowingDynamicIncreaseInBatchSizes() { BatchSizeCounter<Integer> batchSizeCounter1 = new BatchSizeCounter<>(); BatchSizeCounter<Integer> batchSizeCounter2 = new BatchSizeCounter<>(); // @formatter:off Pipeline.of(new GenerateIntegerInputs(500)).then(batchSizeCounter1).then(new Duplicator<Integer>(3)) .then(batchSizeCounter2).batchSize(200).list(); // @formatter:on assertThat(batchSizeCounter1.batches, is(3)); assertThat(batchSizeCounter1.maxBatchSize, is(200)); assertThat(batchSizeCounter1.minBatchSize, is(100)); assertThat(batchSizeCounter2.batches, is(8)); assertThat(batchSizeCounter2.maxBatchSize, is(200)); assertThat(batchSizeCounter2.minBatchSize, is(100)); } @Test public void shouldRunBatchesAllowingDynamicDecreaseInBatchSizes() { BatchSizeCounter<Integer> batchSizeCounter1 = new BatchSizeCounter<>(); BatchSizeCounter<Integer> batchSizeCounter2 = new BatchSizeCounter<>(); // @formatter:off Pipeline.of(new GenerateIntegerInputs(11)).then(new Duplicator<Integer>(3)).then(batchSizeCounter1) .then(new Unique<Integer>()).then(batchSizeCounter2).batchSize(10).list(); // @formatter:on assertThat(batchSizeCounter1.minBatchSize, is(3)); assertThat(batchSizeCounter1.maxBatchSize, is(10)); assertThat(batchSizeCounter1.total, is(33)); assertThat(batchSizeCounter1.batches, is(4)); // Because Unique only affects unique items **within** a batch, these // numbers don't match the full logical set (i.e. there are boundaries of batches) assertThat(batchSizeCounter2.minBatchSize, is(3)); // optimially 1 assertThat(batchSizeCounter2.maxBatchSize, is(10)); assertThat(batchSizeCounter2.total, is(13)); // optimially 11 assertThat(batchSizeCounter2.batches, is(2)); } @Test public void shouldCreateOpFromClassType() { // @formatter:off Pipeline.of(new GenerateIntegerInputs(111)).then(new Duplicator<Integer>(3)) .then(Unique.class, Integer.class).batchSize(30).list(); // @formatter:on } @Test public void shouldTypeCorrectlyWhenOpResultTypeIsSpecified() { // @formatter:off Pipeline<Void, Integer> p = Pipeline.of(new GenerateIntegerInputs(3)) // variant one of defining the output class type .then(IntToString.class, String.class) // variant two of defining the output class type .<Integer>then(StringToInt.class); assertThat(p.list(), hasItems(0, 1, 2)); // @formatter:on } @Test public void shouldTypeCorrectlyWhenPipelineIsCastUsingAs() { // @formatter:off Pipeline<Void, Integer> p = Pipeline.of(new GenerateIntegerInputs(3)).then(IntToString.class, Object.class) .then(StringToInt.class, Object.class).as(Void.class, Integer.class); assertThat(p.list(), hasItems(0, 1, 2)); // @formatter:on } @Test public void shouldCreatePipelineFromClass() { Pipeline<String, String> pipeline = Pipeline.of(Unique.class); List<String> results = pipeline.list("1", "1", "2"); assertThat(results, is(hasItems("1", "2"))); assertThat(results.size(), is(2)); } @Test public void shouldThrowUsefulExceptionWhenOpCannotBeInstantiated() { thrown.expect(RuntimeException.class); thrown.expectMessage( "Failed to create an instance of 'Uninstantiable' - make sure it is has a publicly accessible no-args constructor."); thrown.expectMessage( "To do this you need a public class or a public static inner class, with a public constructor that takes no arguments."); thrown.expectMessage("Private classes, non-static inner classes and anonymous inner classes won't work."); Pipeline.of(Uninstantiable.class).list("In"); } @Ignore @Test public void shouldRunHeapsAndHeapsOfDataForBenchmarking() { BatchSizeCounter<Integer> batchSizeCounter1 = new BatchSizeCounter<>(); // @formatter:off Pipeline<Void, Integer> p = Pipeline.of(new GenerateIntegerInputs(123123)).then(new Duplicator<Integer>(2)) .then(new Duplicator<Integer>(3)).then(new Duplicator<Integer>(4)).then(new Duplicator<Integer>(5)) .then(batchSizeCounter1).then(new Unique<Integer>()).batchSize(50); // @formatter:on long warmUp = 10; long time = 0; long count = 0; for (int i = 0; i < 10000; i++) { long start = System.currentTimeMillis(); p.list(); long end = System.currentTimeMillis(); long avg = 0; long duration = end - start; if (i > warmUp) { time += duration; count++; avg = time / count; } System.out.println("Run:\t" + i + "\tDuration:\t" + (duration) + "ms\tAverage:\t" + avg + "ms"); } } public static class GenerateIntegerInputs implements Op<Void, Integer> { private int count; public GenerateIntegerInputs(int count) { this.count = count; } @Override public void process(List<Void> in, List<Integer> into) { for (int i = 0; i < count; i++) { into.add(i); } } } public static class Duplicator<T> implements Op<T, T> { private int count; public Duplicator(int count) { this.count = count; } @Override public void process(List<T> in, List<T> into) { for (T object : in) { for (int i = 0; i < count; i++) { into.add(object); } } } } public static class Unique<T> implements Op<T, T> { @Override public void process(List<T> in, List<T> into) { into.addAll(new HashSet<T>(in)); } } public static class BatchSizeCounter<T> implements Op<T, T> { public int total = 0; public int maxBatchSize = 0; public int minBatchSize = Integer.MAX_VALUE; public int batches = 0; @Override public void process(List<T> in, List<T> into) { maxBatchSize = Math.max(in.size(), maxBatchSize); minBatchSize = Math.min(in.size(), minBatchSize); batches++; total += in.size(); into.addAll(in); } } public static class IntToString extends BaseOp<Integer, String> { @Override public String processSingle(Integer in) { return Integer.toString(in); } } public static class StringToInt extends BaseOp<String, Integer> { @Override public Integer processSingle(String in) { return Integer.parseInt(in); } } private static class Uninstantiable implements Op<String, String> { @Override public void process(List<String> in, List<String> into) { into.addAll(in); } } // this is a bit of a quick hack to convert an input stream to a string private static String readStringFromStream(InputStream first) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(first)); return reader.readLine(); } catch (Exception e) { throw new RuntimeException(e); } } }