com.threewks.thundr.pipeline.PipelineTest.java Source code

Java tutorial

Introduction

Here is the source code for com.threewks.thundr.pipeline.PipelineTest.java

Source

/*
 * 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);
        }
    }
}