gobblin.util.ParallelRunnerTest.java Source code

Java tutorial

Introduction

Here is the source code for gobblin.util.ParallelRunnerTest.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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 gobblin.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import com.google.common.base.Optional;
import com.google.common.collect.Queues;
import com.google.common.io.Closer;
import com.google.gson.Gson;
import com.google.gson.JsonElement;

import gobblin.configuration.WorkUnitState;
import gobblin.source.extractor.Watermark;
import gobblin.source.extractor.WatermarkSerializerHelper;
import gobblin.source.workunit.WorkUnit;
import gobblin.util.io.SeekableFSInputStream;

/**
 * Unit tests for {@link ParallelRunner}.
 *
 * @author Yinan Li
 */
@Test(groups = { "gobblin.util" })
public class ParallelRunnerTest {

    private FileSystem fs;
    private Path outputPath;

    @BeforeClass
    public void setUp() throws IOException {
        this.fs = FileSystem.getLocal(new Configuration());
        this.outputPath = new Path(ParallelRunnerTest.class.getSimpleName());
    }

    @Test
    public void testSerializeToFile() throws IOException {
        try (ParallelRunner parallelRunner = new ParallelRunner(2, this.fs)) {
            WorkUnit workUnit1 = WorkUnit.createEmpty();
            workUnit1.setProp("foo", "bar");
            workUnit1.setProp("a", 10);
            parallelRunner.serializeToFile(workUnit1, new Path(this.outputPath, "wu1"));

            WorkUnit workUnit2 = WorkUnit.createEmpty();
            workUnit2.setProp("foo", "baz");
            workUnit2.setProp("b", 20);
            parallelRunner.serializeToFile(workUnit2, new Path(this.outputPath, "wu2"));
        }
    }

    @Test(dependsOnMethods = "testSerializeToFile")
    public void testDeserializeFromFile() throws IOException {
        WorkUnit workUnit1 = WorkUnit.createEmpty();
        WorkUnit workUnit2 = WorkUnit.createEmpty();

        try (ParallelRunner parallelRunner = new ParallelRunner(2, this.fs)) {
            parallelRunner.deserializeFromFile(workUnit1, new Path(this.outputPath, "wu1"));
            parallelRunner.deserializeFromFile(workUnit2, new Path(this.outputPath, "wu2"));
        }

        Assert.assertEquals(workUnit1.getPropertyNames().size(), 2);
        Assert.assertEquals(workUnit1.getProp("foo"), "bar");
        Assert.assertEquals(workUnit1.getPropAsInt("a"), 10);

        Assert.assertEquals(workUnit2.getPropertyNames().size(), 2);
        Assert.assertEquals(workUnit2.getProp("foo"), "baz");
        Assert.assertEquals(workUnit2.getPropAsInt("b"), 20);
    }

    @Test
    @SuppressWarnings("deprecation")
    public void testSerializeToSequenceFile() throws IOException {
        Closer closer = Closer.create();
        Configuration conf = new Configuration();
        WritableShimSerialization.addToHadoopConfiguration(conf);
        try {
            SequenceFile.Writer writer1 = closer.register(SequenceFile.createWriter(this.fs, conf,
                    new Path(this.outputPath, "seq1"), Text.class, WorkUnitState.class));

            Text key = new Text();
            WorkUnitState workUnitState = new WorkUnitState();
            TestWatermark watermark = new TestWatermark();

            watermark.setLongWatermark(10L);
            workUnitState.setActualHighWatermark(watermark);
            writer1.append(key, workUnitState);

            SequenceFile.Writer writer2 = closer.register(SequenceFile.createWriter(this.fs, conf,
                    new Path(this.outputPath, "seq2"), Text.class, WorkUnitState.class));

            watermark.setLongWatermark(100L);
            workUnitState.setActualHighWatermark(watermark);
            writer2.append(key, workUnitState);
        } catch (Throwable t) {
            throw closer.rethrow(t);
        } finally {
            closer.close();
        }
    }

    @Test(dependsOnMethods = "testSerializeToSequenceFile")
    public void testDeserializeFromSequenceFile() throws IOException {
        Queue<WorkUnitState> workUnitStates = Queues.newConcurrentLinkedQueue();

        Path seqPath1 = new Path(this.outputPath, "seq1");
        Path seqPath2 = new Path(this.outputPath, "seq2");

        try (ParallelRunner parallelRunner = new ParallelRunner(2, this.fs)) {
            parallelRunner.deserializeFromSequenceFile(Text.class, WorkUnitState.class, seqPath1, workUnitStates,
                    true);
            parallelRunner.deserializeFromSequenceFile(Text.class, WorkUnitState.class, seqPath2, workUnitStates,
                    true);
        }

        Assert.assertFalse(this.fs.exists(seqPath1));
        Assert.assertFalse(this.fs.exists(seqPath2));

        Assert.assertEquals(workUnitStates.size(), 2);

        for (WorkUnitState workUnitState : workUnitStates) {
            TestWatermark watermark = new Gson().fromJson(workUnitState.getActualHighWatermark(),
                    TestWatermark.class);
            Assert.assertTrue(watermark.getLongWatermark() == 10L || watermark.getLongWatermark() == 100L);
        }
    }

    @Test
    public void testMovePath() throws IOException, URISyntaxException {
        String expected = "test";
        ByteArrayOutputStream actual = new ByteArrayOutputStream();

        Path src = new Path("/src/file.txt");
        Path dst = new Path("/dst/file.txt");
        FileSystem fs1 = Mockito.mock(FileSystem.class);
        Mockito.when(fs1.exists(src)).thenReturn(true);
        Mockito.when(fs1.isFile(src)).thenReturn(true);
        Mockito.when(fs1.getUri()).thenReturn(new URI("fs1:////"));
        Mockito.when(fs1.getFileStatus(src)).thenReturn(new FileStatus(1, false, 1, 1, 1, src));
        Mockito.when(fs1.open(src)).thenReturn(
                new FSDataInputStream(new SeekableFSInputStream(new ByteArrayInputStream(expected.getBytes()))));
        Mockito.when(fs1.delete(src, true)).thenReturn(true);

        FileSystem fs2 = Mockito.mock(FileSystem.class);
        Mockito.when(fs2.exists(dst)).thenReturn(false);
        Mockito.when(fs2.getUri()).thenReturn(new URI("fs2:////"));
        Mockito.when(fs2.getConf()).thenReturn(new Configuration());
        Mockito.when(fs2.create(dst, false)).thenReturn(new FSDataOutputStream(actual, null));

        try (ParallelRunner parallelRunner = new ParallelRunner(1, fs1)) {
            parallelRunner.movePath(src, fs2, dst, Optional.<String>absent());
        }

        Assert.assertEquals(actual.toString(), expected);
    }

    @Test(groups = { "ignore" })
    public void testWaitsForFuturesWhenClosing() throws IOException, InterruptedException {
        final AtomicBoolean flag = new AtomicBoolean();
        final CountDownLatch latch1 = new CountDownLatch(1);
        final CountDownLatch latch2 = new CountDownLatch(1);
        Path src1 = new Path("/src/file1.txt");
        Path src2 = new Path("/src/file2.txt");
        FileSystem fs = Mockito.mock(FileSystem.class);
        Mockito.when(fs.exists(src1)).thenReturn(true);
        Mockito.when(fs.delete(src1, true)).thenAnswer(new Answer<Boolean>() {
            @Override
            public Boolean answer(InvocationOnMock invocation) throws Throwable {
                latch1.countDown();
                return false;
            }
        });
        Mockito.when(fs.exists(src2)).thenReturn(true);
        Mockito.when(fs.delete(src2, true)).thenAnswer(new Answer<Boolean>() {
            @Override
            public Boolean answer(InvocationOnMock invocation) throws Throwable {
                latch1.await();
                long end = System.currentTimeMillis() + 70000;
                while (System.currentTimeMillis() < end) {
                    try {
                        Thread.sleep(Math.max(1, end - System.currentTimeMillis()));
                    } catch (Exception ignored) {
                    }
                }
                flag.set(true);
                latch2.countDown();
                return true;
            }
        });

        boolean caughtException = false;
        ParallelRunner parallelRunner = new ParallelRunner(2, fs);
        try {
            parallelRunner.deletePath(src1, true);
            parallelRunner.deletePath(src2, true);
            System.out.println(System.currentTimeMillis() + ": START - ParallelRunner.close()");
            parallelRunner.close();
        } catch (IOException e) {
            caughtException = true;
        }
        Assert.assertTrue(caughtException);
        System.out.println(System.currentTimeMillis() + ": END   - ParallelRunner.close()");
        System.out.println(System.currentTimeMillis() + ": Waiting for unkillable task to finish...");
        latch2.await();
        System.out.println(System.currentTimeMillis() + ": Unkillable task completed.");
        Assert.assertTrue(flag.get());
    }

    @AfterClass
    public void tearDown() throws IOException {
        if (this.fs != null && this.outputPath != null) {
            this.fs.delete(this.outputPath, true);
        }
    }

    public static class TestWatermark implements Watermark {

        private long watermark = -1;

        @Override
        public JsonElement toJson() {
            return WatermarkSerializerHelper.convertWatermarkToJson(this);
        }

        @Override
        public short calculatePercentCompletion(Watermark lowWatermark, Watermark highWatermark) {
            return 0;
        }

        public void setLongWatermark(long watermark) {
            this.watermark = watermark;
        }

        public long getLongWatermark() {
            return this.watermark;
        }
    }
}