http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestActionAttributesBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestActionAttributesBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestActionAttributesBuilder.java
new file mode 100644
index 0000000..ad8d294
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestActionAttributesBuilder.java
@@ -0,0 +1,713 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class TestActionAttributesBuilder {
+    @Rule
+    public final ExpectedException expectedException = 
ExpectedException.none();
+
+    private static final String NAME_NODE = "${nameNode}";
+    private static final String EXAMPLE_DIR = "/path/to/directory";
+    private static final String CONFIG_CLASS = "AnyConfigClass.class";
+    private static final String[] JOB_XMLS = {"jobXml1.xml", "jobXml2.xml", 
"jobXml3.xml", "jobXml4.xml"};
+    private static final String[] FILES = {"file1.xml", "file2.xml", 
"file3.xml", "file4.xml"};
+    private static final String[] ARCHIVES = {"archive1.jar", "archive2.jar", 
"archive3.jar", "archive4.jar"};
+
+    private static final String MAPRED_JOB_QUEUE_NAME = 
"mapred.job.queue.name";
+    private static final String DEFAULT = "default";
+
+    private static final ImmutableMap<String, String> CONFIG_EXAMPLE = 
getConfigExample();
+
+    private static ImmutableMap<String, String> getConfigExample() {
+        final ImmutableMap.Builder<String, String> configExampleBuilder = new 
ImmutableMap.Builder<>();
+
+        final String[] keys = {"mapred.map.tasks", "mapred.input.dir", 
"mapred.output.dir"};
+        final String[] values = {"1", "${inputDir}", "${outputDir}"};
+
+        for (int i = 0; i < keys.length; ++i) {
+            configExampleBuilder.put(keys[i], values[i]);
+        }
+
+        return configExampleBuilder.build();
+    }
+
+    private static final Delete[] DELETES = {new Delete("path0", null),
+            new Delete("path1", true),
+            new Delete("path2", false),
+            new Delete("path3", null)
+    };
+
+    private static final Mkdir[] MKDIRS = {new Mkdir("path0"),
+            new Mkdir("path1"),
+            new Mkdir("path2"),
+            new Mkdir("path3")
+    };
+
+    private static final Move[] MOVES = {new Move("source0", "target0"),
+            new Move("source1", "target1"),
+            new Move("source2", "target2"),
+            new Move("source3", "target3")
+    };
+
+    private static final Chmod[] CHMODS = {new 
ChmodBuilder().withPermissions("711").build(),
+            new ChmodBuilder().withPermissions("511").build(),
+            new ChmodBuilder().withPermissions("551").build(),
+            new ChmodBuilder().withPermissions("755").build()
+    };
+
+    private static final Touchz[] TOUCHZS = {new Touchz("path0"),
+            new Touchz("path1"),
+            new Touchz("path2"),
+            new Touchz("path3")
+    };
+
+    private static final Chgrp[] CHGRPS = {new 
ChgrpBuilder().withGroup("user0").build(),
+            new ChgrpBuilder().withGroup("user1").build(),
+            new ChgrpBuilder().withGroup("user2").build(),
+            new ChgrpBuilder().withGroup("user3").build()
+    };
+
+    private static final String RESOURCE_MANAGER = "${resourceManager}";
+
+    private static final Launcher LAUNCHER = new LauncherBuilder()
+            .withMemoryMb(1024)
+            .withVCores(2)
+            .withQueue(DEFAULT)
+            .withSharelib(DEFAULT)
+            .withViewAcl(DEFAULT)
+            .withModifyAcl(DEFAULT)
+            .build();
+
+    private ActionAttributesBuilder getBuilderInstance() {
+        return ActionAttributesBuilder.create();
+    }
+
+    @Test
+    public void testFromExistingBuilder() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withNameNode(NAME_NODE);
+
+        final ActionAttributes fromExisting = ActionAttributesBuilder
+                .createFromExisting(builder.build())
+                .build();
+
+        assertEquals(NAME_NODE, fromExisting.getNameNode());
+    }
+
+    @Test
+    public void testNameNodeAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withNameNode(NAME_NODE);
+
+        final ActionAttributes attributes = builder.build();
+        assertEquals(NAME_NODE, attributes.getNameNode());
+    }
+
+    @Test
+    public void testNameNodeAddedTwiceThrows() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withNameNode(NAME_NODE);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withNameNode("any_string");
+    }
+
+    @Test
+    public void testPrepareAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withPrepare(new 
PrepareBuilder().withDelete(EXAMPLE_DIR).build());
+
+        final ActionAttributes attributes = builder.build();
+        assertEquals(EXAMPLE_DIR, 
attributes.getPrepare().getDeletes().get(0).getPath());
+    }
+
+    @Test
+    public void testPrepareAddedTwiceThrows() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withPrepare(new 
PrepareBuilder().withDelete(EXAMPLE_DIR).build());
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withPrepare(new 
PrepareBuilder().withDelete("any_directory").build());
+    }
+
+    @Test
+    public void testStreamingAdded() {
+        final Streaming streaming = new 
StreamingBuilder().withMapper("mapper.sh").withReducer("reducer.sh").build();
+
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withStreaming(streaming);
+
+        final ActionAttributes attributes = builder.build();
+        assertEquals(streaming, attributes.getStreaming());
+    }
+
+    @Test
+    public void testStreamingAddedTwiceThrows() {
+        final Streaming streaming1= new 
StreamingBuilder().withMapper("mapper1.sh").withReducer("reducer1.sh").build();
+        final Streaming streaming2 = new 
StreamingBuilder().withMapper("mapper2.sh").withReducer("reducer2.sh").build();
+
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withStreaming(streaming1);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withStreaming(streaming2);
+    }
+
+    @Test
+    public void testPipesAdded() {
+        final Pipes pipes = new 
PipesBuilder().withMap("map").withReduce("reduce").build();
+
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withPipes(pipes);
+
+        final ActionAttributes attributes = builder.build();
+        assertEquals(pipes, attributes.getPipes());
+    }
+
+    @Test
+    public void testPipesAddedTwiceThrows() {
+        final Pipes pipes1 = new 
PipesBuilder().withMap("map1").withReduce("reduce1").build();
+        final Pipes pipes2 = new 
PipesBuilder().withMap("map2").withReduce("reduce2").build();
+
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withPipes(pipes1);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withPipes(pipes2);
+    }
+
+    @Test
+    public void testConfigClassAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withConfigClass(CONFIG_CLASS);
+
+        final ActionAttributes attributes = builder.build();
+        assertEquals(CONFIG_CLASS, attributes.getConfigClass());
+    }
+
+    @Test
+    public void testConfigClassAddedTwiceThrows() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withConfigClass(CONFIG_CLASS);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withConfigClass("AnyClass");
+    }
+
+    @Test
+    public void testSeveralJobXmlsAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final String jobXml : JOB_XMLS) {
+            builder.withJobXml(jobXml);
+        }
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<String> jobXmlsList = attributes.getJobXmls();
+        assertEquals(JOB_XMLS.length, jobXmlsList.size());
+
+        for (int i = 0; i < JOB_XMLS.length; ++i) {
+            assertEquals(JOB_XMLS[i], jobXmlsList.get(i));
+        }
+    }
+
+    @Test
+    public void testWithoutJobXmls() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final String jobXml : JOB_XMLS) {
+            builder.withJobXml(jobXml);
+        }
+
+        builder.withoutJobXml(JOB_XMLS[0]);
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<String> jobXmlsList = attributes.getJobXmls();
+        final String[] remainingJobXmls = Arrays.copyOfRange(JOB_XMLS, 1, 
JOB_XMLS.length);
+        assertEquals(remainingJobXmls.length, jobXmlsList.size());
+
+        for (int i = 0; i < remainingJobXmls.length; ++i) {
+            assertEquals(remainingJobXmls[i], jobXmlsList.get(i));
+        }
+    }
+
+    @Test
+    public void testClearJobXmls() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final String jobXml : JOB_XMLS) {
+            builder.withJobXml(jobXml);
+        }
+
+        builder.clearJobXmls();
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<String> jobXmlsList = attributes.getJobXmls();
+        assertEquals(0, jobXmlsList.size());
+    }
+
+
+
+    @Test
+    public void testConfigPropertyAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        final ActionAttributes attributes = builder.build();
+        assertEquals(DEFAULT, 
attributes.getConfiguration().get(MAPRED_JOB_QUEUE_NAME));
+    }
+
+    @Test
+    public void testSeveralConfigPropertiesAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Map.Entry<String, String> entry : 
CONFIG_EXAMPLE.entrySet()) {
+            builder.withConfigProperty(entry.getKey(), entry.getValue());
+        }
+
+        final ActionAttributes attributes = builder.build();
+
+        for (final Map.Entry<String, String> entry : 
CONFIG_EXAMPLE.entrySet()) {
+            assertEquals(entry.getValue(), 
attributes.getConfiguration().get(entry.getKey()));
+        }
+
+        assertEquals(CONFIG_EXAMPLE, attributes.getConfiguration());
+    }
+
+    @Test
+    public void testSameConfigPropertyAddedTwiceThrows() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+    }
+
+    @Test
+    public void testSeveralFilesAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final String file : FILES) {
+            builder.withFile(file);
+        }
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<String> filesList = attributes.getFiles();
+        assertEquals(FILES.length, filesList.size());
+
+        for (int i = 0; i < FILES.length; ++i) {
+            assertEquals(FILES[i], filesList.get(i));
+        }
+    }
+
+    @Test
+    public void testRemoveFiles() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final String file : FILES) {
+            builder.withFile(file);
+        }
+
+        builder.withoutFile(FILES[0]);
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<String> filesList = attributes.getFiles();
+        final String[] remainingFiles = Arrays.copyOfRange(FILES, 1, 
FILES.length);
+        assertEquals(remainingFiles.length, filesList.size());
+
+        for (int i = 0; i < remainingFiles.length; ++i) {
+            assertEquals(remainingFiles[i], filesList.get(i));
+        }
+    }
+
+    @Test
+    public void testClearFiles() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final String file : FILES) {
+            builder.withFile(file);
+        }
+
+        builder.clearFiles();
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<String> filesList = attributes.getFiles();
+        assertEquals(0, filesList.size());
+    }
+
+    @Test
+    public void testSeveralArchivesAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final String archive : ARCHIVES) {
+            builder.withArchive(archive);
+        }
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<String> filesList = attributes.getArchives();
+        assertEquals(ARCHIVES.length, filesList.size());
+
+        for (int i = 0; i < ARCHIVES.length; ++i) {
+            assertEquals(ARCHIVES[i], filesList.get(i));
+        }
+    }
+
+    @Test
+    public void testRemoveArchives() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final String archive : ARCHIVES) {
+            builder.withArchive(archive);
+        }
+
+        builder.withoutArchive(ARCHIVES[0]);
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<String> archivesList = attributes.getArchives();
+        final String[] remainingArchives = Arrays.copyOfRange(ARCHIVES, 1, 
ARCHIVES.length);
+        assertEquals(remainingArchives.length, archivesList.size());
+
+        for (int i = 0; i < remainingArchives.length; ++i) {
+            assertEquals(remainingArchives[i], archivesList.get(i));
+        }
+    }
+
+    @Test
+    public void testClearArchives() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final String archive : ARCHIVES) {
+            builder.withArchive(archive);
+        }
+
+        builder.clearArchives();
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<String> archivesList = attributes.getArchives();
+        assertEquals(0, archivesList.size());
+    }
+
+    @Test
+    public void testSeveralDeletesAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Delete delete : DELETES) {
+            builder.withDelete(delete);
+        }
+
+        final ActionAttributes attributes = builder.build();
+
+        assertEquals(Arrays.asList(DELETES), attributes.getDeletes());
+    }
+
+    @Test
+    public void testWithoutDelete() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Delete delete : DELETES) {
+            builder.withDelete(delete);
+        }
+
+        builder.withoutDelete(DELETES[0]);
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<Delete> expectedDeletes = Arrays.asList(DELETES).subList(1, 
DELETES.length);
+        assertEquals(expectedDeletes, attributes.getDeletes());
+    }
+
+    @Test
+    public void testClearDeletes() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Delete delete : DELETES) {
+            builder.withDelete(delete);
+        }
+
+        builder.clearDeletes();
+
+        final ActionAttributes attributes = builder.build();
+
+        assertTrue(attributes.getDeletes().isEmpty());
+    }
+
+    @Test
+    public void testSeveralMkdirsAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Mkdir mkdir : MKDIRS) {
+            builder.withMkdir(mkdir);
+        }
+
+        final ActionAttributes attributes = builder.build();
+
+        assertEquals(Arrays.asList(MKDIRS), attributes.getMkdirs());
+    }
+
+    @Test
+    public void testWithoutMkdir() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Mkdir mkdir : MKDIRS) {
+            builder.withMkdir(mkdir);
+        }
+
+        builder.withoutMkdir(MKDIRS[0]);
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<Mkdir> expectedMkdirs = Arrays.asList(MKDIRS).subList(1, 
MKDIRS.length);
+        assertEquals(expectedMkdirs, attributes.getMkdirs());
+    }
+
+    @Test
+    public void testClearMkdirs() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Mkdir mkdir : MKDIRS) {
+            builder.withMkdir(mkdir);
+        }
+
+        builder.clearMkdirs();
+
+        final ActionAttributes attributes = builder.build();
+
+        assertTrue(attributes.getMkdirs().isEmpty());
+    }
+
+    @Test
+    public void testSeveralMovesAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Move move : MOVES) {
+            builder.withMove(move);
+        }
+
+        final ActionAttributes attributes = builder.build();
+
+        assertEquals(Arrays.asList(MOVES), attributes.getMoves());
+    }
+
+    @Test
+    public void testWithoutMove() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Move move : MOVES) {
+            builder.withMove(move);
+        }
+
+        builder.withoutMove(MOVES[0]);
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<Move> expectedMoves = Arrays.asList(MOVES).subList(1, 
MOVES.length);
+        assertEquals(expectedMoves, attributes.getMoves());
+    }
+
+    @Test
+    public void testClearMoves() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Move move : MOVES) {
+            builder.withMove(move);
+        }
+
+        builder.clearMoves();
+
+        final ActionAttributes attributes = builder.build();
+
+        assertTrue(attributes.getMoves().isEmpty());
+    }
+
+    @Test
+    public void testSeveralChmodsAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Chmod chmod : CHMODS) {
+            builder.withChmod(chmod);
+        }
+
+        final ActionAttributes attributes = builder.build();
+
+        assertEquals(Arrays.asList(CHMODS), attributes.getChmods());
+    }
+
+    @Test
+    public void testWithoutChmod() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Chmod chmod : CHMODS) {
+            builder.withChmod(chmod);
+        }
+
+        builder.withoutChmod(CHMODS[0]);
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<Chmod> expectedChmods = Arrays.asList(CHMODS).subList(1, 
CHMODS.length);
+        assertEquals(expectedChmods, attributes.getChmods());
+    }
+
+    @Test
+    public void testClearChmods() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Chmod chmod : CHMODS) {
+            builder.withChmod(chmod);
+        }
+
+        builder.clearChmods();
+
+        final ActionAttributes attributes = builder.build();
+
+        assertTrue(attributes.getChmods().isEmpty());
+    }
+
+    @Test
+    public void testSeveralTouchzsAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Touchz touchz : TOUCHZS) {
+            builder.withTouchz(touchz);
+        }
+
+        final ActionAttributes attributes = builder.build();
+
+        assertEquals(Arrays.asList(TOUCHZS), attributes.getTouchzs());
+    }
+
+    @Test
+    public void testWithoutTouchz() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Touchz touchz : TOUCHZS) {
+            builder.withTouchz(touchz);
+        }
+
+        builder.withoutTouchz(TOUCHZS[0]);
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<Touchz> expectedTouchzs = Arrays.asList(TOUCHZS).subList(1, 
TOUCHZS.length);
+        assertEquals(expectedTouchzs, attributes.getTouchzs());
+    }
+
+    @Test
+    public void testClearTouchzs() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Touchz touchz : TOUCHZS) {
+            builder.withTouchz(touchz);
+        }
+
+        builder.clearTouchzs();
+
+        final ActionAttributes attributes = builder.build();
+
+        assertTrue(attributes.getTouchzs().isEmpty());
+    }
+
+    @Test
+    public void testSeveralChgrpsAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Chgrp chgrp : CHGRPS) {
+            builder.withChgrp(chgrp);
+        }
+
+        final ActionAttributes attributes = builder.build();
+
+        assertEquals(Arrays.asList(CHGRPS), attributes.getChgrps());
+    }
+
+    @Test
+    public void testWithoutChgrp() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Chgrp chgrp : CHGRPS) {
+            builder.withChgrp(chgrp);
+        }
+
+        builder.withoutChgrp(CHGRPS[0]);
+
+        final ActionAttributes attributes = builder.build();
+
+        final List<Chgrp> expectedChgrps = Arrays.asList(CHGRPS).subList(1, 
CHGRPS.length);
+        assertEquals(expectedChgrps, attributes.getChgrps());
+    }
+
+    @Test
+    public void testClearChgrps() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+
+        for (final Chgrp chgrp : CHGRPS) {
+            builder.withChgrp(chgrp);
+        }
+
+        builder.clearChgrps();
+
+        final ActionAttributes attributes = builder.build();
+
+        assertTrue(attributes.getChgrps().isEmpty());
+    }
+
+    @Test
+    public void testResourceManagerAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withResourceManager(RESOURCE_MANAGER);
+
+        final ActionAttributes attributes = builder.build();
+        assertEquals(RESOURCE_MANAGER, attributes.getResourceManager());
+    }
+
+    @Test
+    public void testLauncherAdded() {
+        final ActionAttributesBuilder builder = getBuilderInstance();
+        builder.withLauncher(LAUNCHER);
+
+        final ActionAttributes attributes = builder.build();
+        assertEquals(LAUNCHER.getMemoryMb(), 
attributes.getLauncher().getMemoryMb());
+        assertEquals(LAUNCHER.getVCores(), 
attributes.getLauncher().getVCores());
+        assertEquals(LAUNCHER.getQueue(), attributes.getLauncher().getQueue());
+        assertEquals(LAUNCHER.getSharelib(), 
attributes.getLauncher().getSharelib());
+        assertEquals(LAUNCHER.getViewAcl(), 
attributes.getLauncher().getViewAcl());
+        assertEquals(LAUNCHER.getModifyAcl(), 
attributes.getLauncher().getModifyAcl());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChBaseBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChBaseBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChBaseBuilder.java
new file mode 100644
index 0000000..87b8543
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChBaseBuilder.java
@@ -0,0 +1,127 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.junit.Assert.assertEquals;
+
+public abstract class TestChBaseBuilder<A extends ChFSBase, B extends 
ChFSBaseBuilder<B> & Builder<A>> {
+    @Rule
+    public final ExpectedException expectedException = 
ExpectedException.none();
+
+    protected abstract B getBuilderInstance();
+
+    @Test
+    public void testSetRecursive() {
+        final B builder = getBuilderInstance();
+        builder.setRecursive();
+
+        final A chmod = builder.build();
+        assertEquals(true, chmod.isRecursive());
+    }
+
+    @Test
+    public void testSetRecursiveCalledTwiceThrows() {
+        final B builder = getBuilderInstance();
+        builder.setRecursive();
+
+        expectedException.expect(IllegalStateException.class);
+        builder.setRecursive();
+    }
+
+    @Test
+    public void testSetNonRecursive() {
+        final B builder = getBuilderInstance();
+        builder.setNonRecursive();
+
+        final A chmod = builder.build();
+        assertEquals(false, chmod.isRecursive());
+    }
+
+    @Test
+    public void testSetNonRecursiveCalledTwiceThrows() {
+        final B builder = getBuilderInstance();
+        builder.setNonRecursive();
+
+        expectedException.expect(IllegalStateException.class);
+        builder.setNonRecursive();
+    }
+
+    @Test
+    public void testSetRecursiveCalledAfterSetNonRecursive() {
+        final B builder = getBuilderInstance();
+        builder.setNonRecursive();
+
+        expectedException.expect(IllegalStateException.class);
+        builder.setRecursive();
+    }
+
+    @Test
+    public void testSetNonRecursiveCalledAfterSetRecursive() {
+        final B builder = getBuilderInstance();
+        builder.setRecursive();
+
+        expectedException.expect(IllegalStateException.class);
+        builder.setNonRecursive();
+    }
+
+    @Test
+    public void testWithPath() {
+        final String path = "path";
+
+        final B builder = getBuilderInstance();
+        builder.withPath(path);
+
+        final A chmod = builder.build();
+        assertEquals(path, chmod.getPath());
+    }
+
+    @Test
+    public void testWithPathCalledTwiceThrows() {
+        final String path1 = "path1";
+        final String path2 = "path2";
+
+        final B builder = getBuilderInstance();
+        builder.withPath(path1);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withPath(path2);
+    }
+
+    @Test
+    public void testSetDirFiles() {
+        final B builder = getBuilderInstance();
+        builder.setDirFiles(false);
+
+        final A chmod = builder.build();
+        assertEquals("false", chmod.getDirFiles());
+    }
+
+    @Test
+    public void testSetDirFilesCalledTwiceThrows() {
+        final B builder = getBuilderInstance();
+        builder.setDirFiles(false);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.setDirFiles(true);
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChgrpBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChgrpBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChgrpBuilder.java
new file mode 100644
index 0000000..2f8a0fc
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChgrpBuilder.java
@@ -0,0 +1,53 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestChgrpBuilder extends TestChBaseBuilder<Chgrp, ChgrpBuilder> {
+    @Override
+    protected ChgrpBuilder getBuilderInstance() {
+        return new ChgrpBuilder();
+    }
+
+    @Test
+    public void testWithGroup() {
+        final String group = "root";
+
+        final ChgrpBuilder builder = new ChgrpBuilder();
+        builder.withGroup(group);
+
+        final Chgrp chgrp = builder.build();
+        assertEquals(group, chgrp.getGroup());
+    }
+
+    @Test
+    public void testWithPermissionsCalledTwiceThrows() {
+        final String group1 = "group1";
+        final String group2 = "group1";
+
+        final ChgrpBuilder builder = new ChgrpBuilder();
+        builder.withGroup(group1);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withGroup(group2);
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChmodBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChmodBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChmodBuilder.java
new file mode 100644
index 0000000..084f146
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestChmodBuilder.java
@@ -0,0 +1,53 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestChmodBuilder extends TestChBaseBuilder<Chmod, ChmodBuilder> {
+    @Override
+    protected ChmodBuilder getBuilderInstance() {
+        return new ChmodBuilder();
+    }
+
+    @Test
+    public void testWithPermissions() {
+        final String permission = "-rwxrw-rw-";
+
+        final ChmodBuilder builder = new ChmodBuilder();
+        builder.withPermissions(permission);
+
+        final Chmod chmod = builder.build();
+        assertEquals(permission, chmod.getPermissions());
+    }
+
+    @Test
+    public void testWithPermissionsCalledTwiceThrows() {
+        final String permission1 = "-rwxrw-rw-";
+        final String permission2 = "-rwxrw-r-";
+
+        final ChmodBuilder builder = new ChmodBuilder();
+        builder.withPermissions(permission1);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withPermissions(permission2);
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestDelete.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestDelete.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestDelete.java
new file mode 100644
index 0000000..402e016
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestDelete.java
@@ -0,0 +1,36 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestDelete {
+    @Test
+    public void testPathAndSkipTrashAreCorrect() {
+        final String path = "path/to/location";
+        final Boolean skipTrash = null;
+
+        final Delete delete = new Delete(path, skipTrash);
+
+        assertEquals(path, delete.getPath());
+        assertEquals(skipTrash, delete.getSkipTrash());
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestDistcpActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestDistcpActionBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestDistcpActionBuilder.java
new file mode 100644
index 0000000..b0e8e0c
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestDistcpActionBuilder.java
@@ -0,0 +1,213 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestDistcpActionBuilder extends 
TestNodeBuilderBaseImpl<DistcpAction, DistcpActionBuilder> {
+    private static final String NAME = "distcp-name";
+    private static final String RESOURCE_MANAGER = "${resourceManager}";
+    private static final String NAME_NODE = "${nameNode}";
+    private static final String EXAMPLE_DIR = "/path/to/directory";
+    private static final String[] ARGS = {"arg1", "arg2", "arg3"};
+
+    private static final String MAPRED_JOB_QUEUE_NAME = 
"mapred.job.queue.name";
+    private static final String DEFAULT = "default";
+
+    @Override
+    protected DistcpActionBuilder getBuilderInstance() {
+        return DistcpActionBuilder.create();
+    }
+
+    @Override
+    protected DistcpActionBuilder getBuilderInstance(final DistcpAction 
action) {
+        return DistcpActionBuilder.createFromExistingAction(action);
+    }
+
+    @Test
+    public void testResourceManagerAdded() {
+        final DistcpActionBuilder builder = getBuilderInstance();
+        builder.withResourceManager(RESOURCE_MANAGER);
+
+        final DistcpAction action = builder.build();
+        assertEquals(RESOURCE_MANAGER, action.getResourceManager());
+    }
+
+    @Test
+    public void testNameNodeAdded() {
+        final DistcpActionBuilder builder = getBuilderInstance();
+        builder.withNameNode(NAME_NODE);
+
+        final DistcpAction action = builder.build();
+        assertEquals(NAME_NODE, action.getNameNode());
+    }
+
+    @Test
+    public void testPrepareAdded() {
+        final DistcpActionBuilder builder = getBuilderInstance();
+        builder.withPrepare(new 
PrepareBuilder().withDelete(EXAMPLE_DIR).build());
+
+        final DistcpAction action = builder.build();
+        assertEquals(EXAMPLE_DIR, 
action.getPrepare().getDeletes().get(0).getPath());
+    }
+
+    @Test
+    public void testSameConfigPropertyAddedTwiceThrows() {
+        final DistcpActionBuilder builder = getBuilderInstance();
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+    }
+
+    @Test
+    public void testJavaOptsAdded() {
+        final DistcpActionBuilder builder = getBuilderInstance();
+        builder.withJavaOpts("-Dopt1 -Dopt2");
+
+        final DistcpAction action = builder.build();
+        assertEquals("-Dopt1 -Dopt2", action.getJavaOpts());
+    }
+
+    @Test
+    public void testSeveralArgsAdded() {
+        final DistcpActionBuilder builder = getBuilderInstance();
+
+        for (final String arg : ARGS) {
+            builder.withArg(arg);
+        }
+
+        final DistcpAction action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        assertEquals(ARGS.length, argList.size());
+
+        for (int i = 0; i < ARGS.length; ++i) {
+            assertEquals(ARGS[i], argList.get(i));
+        }
+    }
+
+    @Test
+    public void testRemoveArgs() {
+        final DistcpActionBuilder builder = getBuilderInstance();
+
+        for (final String file : ARGS) {
+            builder.withArg(file);
+        }
+
+        builder.withoutArg(ARGS[0]);
+
+        final DistcpAction action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        final String[] remainingArgs = Arrays.copyOfRange(ARGS, 1, 
ARGS.length);
+        assertEquals(remainingArgs.length, argList.size());
+
+        for (int i = 0; i < remainingArgs.length; ++i) {
+            assertEquals(remainingArgs[i], argList.get(i));
+        }
+    }
+
+    @Test
+    public void testClearArgs() {
+        final DistcpActionBuilder builder = getBuilderInstance();
+
+        for (final String file : ARGS) {
+            builder.withArg(file);
+        }
+
+        builder.clearArgs();
+
+        final DistcpAction mrAction = builder.build();
+
+        final List<String> argList = mrAction.getArgs();
+        assertEquals(0, argList.size());
+    }
+    @Test
+    public void testFromExistingActionDistcpSpecific() {
+        final DistcpActionBuilder builder = getBuilderInstance();
+
+        builder.withName(NAME)
+                .withNameNode(NAME_NODE)
+                .withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT)
+                .withArg(ARGS[0])
+                .withArg(ARGS[1]);
+
+        final DistcpAction action = builder.build();
+
+        final DistcpActionBuilder fromExistingBuilder = 
getBuilderInstance(action);
+
+        final String newName = "fromExisting_" + NAME;
+        fromExistingBuilder.withName(newName)
+                .withoutArg(ARGS[1])
+                .withArg(ARGS[2]);
+
+        final DistcpAction modifiedAction = fromExistingBuilder.build();
+
+        assertEquals(newName, modifiedAction.getName());
+        assertEquals(action.getNameNode(), modifiedAction.getNameNode());
+
+        final Map<String, String> expectedConfiguration = new 
LinkedHashMap<>();
+        expectedConfiguration.put(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+        assertEquals(expectedConfiguration, modifiedAction.getConfiguration());
+
+        assertEquals(Arrays.asList(ARGS[0], ARGS[2]), 
modifiedAction.getArgs());
+    }
+
+    @Test
+    public void testFromOtherAction() {
+        final ShellAction parent = ShellActionBuilder.create()
+                .withName("parent")
+                .withExecutable("echo")
+                .build();
+
+        final ShellAction otherAction = 
ShellActionBuilder.createFromExistingAction(parent)
+                .withName("shell")
+                .withParent(parent)
+                .withNameNode(NAME_NODE)
+                .withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT)
+                .withArgument(ARGS[0])
+                .withArgument(ARGS[1])
+                .build();
+
+        final String newName = "fromExisting_" + NAME;
+        final DistcpAction fromOtherAction = 
DistcpActionBuilder.createFromExistingAction(otherAction)
+                .withName(newName)
+                .withoutArg(ARGS[1])
+                .withArg(ARGS[2])
+                .build();
+
+        assertEquals(newName, fromOtherAction.getName());
+        assertEquals(otherAction.getNameNode(), fromOtherAction.getNameNode());
+        assertEquals(parent, 
fromOtherAction.getParentsWithoutConditions().get(0));
+
+        final Map<String, String> expectedConfiguration = new 
LinkedHashMap<>();
+        expectedConfiguration.put(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+        assertEquals(expectedConfiguration, 
fromOtherAction.getConfiguration());
+
+        assertEquals(Arrays.asList(ARGS[0], ARGS[2]), 
fromOtherAction.getArgs());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestEmailActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestEmailActionBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestEmailActionBuilder.java
new file mode 100644
index 0000000..c3ba356
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestEmailActionBuilder.java
@@ -0,0 +1,197 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestEmailActionBuilder extends 
TestNodeBuilderBaseImpl<EmailAction, EmailActionBuilder> {
+    @Override
+    protected EmailActionBuilder getBuilderInstance() {
+        return EmailActionBuilder.create();
+    }
+
+    @Override
+    protected EmailActionBuilder getBuilderInstance(EmailAction action) {
+        return EmailActionBuilder.createFromExistingAction(action);
+    }
+
+    @Test
+    public void testRecipientAdded() {
+        final String recipient = "[email protected]";
+
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withRecipient(recipient);
+
+        final EmailAction emailAction = builder.build();
+        assertEquals(recipient, emailAction.getRecipient());
+    }
+
+    @Test
+    public void testRecipientAddedTwiceThrows() {
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withRecipient("[email protected]");
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withRecipient("[email protected]");
+    }
+
+    @Test
+    public void testCcAdded() {
+        final String cc = "[email protected]";
+
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withCc(cc);
+
+        final EmailAction emailAction = builder.build();
+        assertEquals(cc, emailAction.getCc());
+    }
+
+    @Test
+    public void testCcAddedTwiceThrows() {
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withCc("[email protected]");
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withCc("[email protected]");
+    }
+
+    @Test
+    public void testBccAdded() {
+        final String bcc = "[email protected]";
+
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withBcc(bcc);
+
+        final EmailAction emailAction = builder.build();
+        assertEquals(bcc, emailAction.getBcc());
+    }
+
+    @Test
+    public void testBccAddedTwiceThrows() {
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withBcc("[email protected]");
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withBcc("[email protected]");
+    }
+
+    @Test
+    public void testSubjectAdded() {
+        final String subject = "Subject";
+
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withSubject(subject);
+
+        final EmailAction emailAction = builder.build();
+        assertEquals(subject, emailAction.getSubject());
+    }
+
+    @Test
+    public void testSubjectAddedTwiceThrows() {
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withSubject("Subject");
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withSubject("Any subject");
+    }
+
+    @Test
+    public void testBodyAdded() {
+        final String body = "Email body.";
+
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withBody(body);
+
+        final EmailAction emailAction = builder.build();
+        assertEquals(body, emailAction.getBody());
+    }
+
+    @Test
+    public void testBodyAddedTwiceThrows() {
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withBody("Email body.");
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withBody("Any email body.");
+    }
+
+    @Test
+    public void testContentTypeAdded() {
+        final String contentType = "content_type";
+
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withContentType(contentType);
+
+        final EmailAction emailAction = builder.build();
+        assertEquals(contentType, emailAction.getContentType());
+    }
+
+    @Test
+    public void testContentTypeAddedTwiceThrows() {
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withContentType("content_type");
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withContentType("any_content_type");
+    }
+
+    @Test
+    public void testAttachmentAdded() {
+        final String attachment = "attachment";
+
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withAttachment(attachment);
+
+        final EmailAction emailAction = builder.build();
+        assertEquals(attachment, emailAction.getAttachment());
+    }
+
+    @Test
+    public void testAttachmentAddedTwiceThrows() {
+        final EmailActionBuilder builder = getBuilderInstance();
+        builder.withAttachment("attachment");
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withAttachment("any_attachment");
+    }
+
+    @Test
+    public void testFromOtherAction() {
+        final ShellAction parent = ShellActionBuilder.create()
+                .withName("parent")
+                .withExecutable("echo")
+                .build();
+
+        final ShellAction otherAction = 
ShellActionBuilder.createFromExistingAction(parent)
+                .withName("shell")
+                .withParent(parent)
+                .build();
+
+        final EmailAction fromOtherAction = 
EmailActionBuilder.createFromExistingAction(otherAction)
+                .withName("email")
+                .withBody("body")
+                .build();
+
+        assertEquals(parent, 
fromOtherAction.getParentsWithoutConditions().get(0));
+        assertEquals("email", fromOtherAction.getName());
+        assertEquals("body", fromOtherAction.getBody());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestErrorHandler.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestErrorHandler.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestErrorHandler.java
new file mode 100644
index 0000000..2fe01a2
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestErrorHandler.java
@@ -0,0 +1,51 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestErrorHandler {
+    @Rule
+    public final ExpectedException expectedException = 
ExpectedException.none();
+
+    @Test
+    public void testNameIsCorrect() {
+        final String name = "error-handler";
+        final Builder<MapReduceAction> handlerBuilder = 
MapReduceActionBuilder.create().withName(name);
+
+        final ErrorHandler errorHandler = 
ErrorHandler.buildAsErrorHandler(handlerBuilder);
+        assertEquals(name, errorHandler.getName());
+    }
+
+    @Test
+    public void testIfThereAreParentsThenThrows() {
+        final String name = "error-handler";
+        final Node parent = 
MapReduceActionBuilder.create().withName("parent").build();
+        final Builder<MapReduceAction> handlerBuilder = 
MapReduceActionBuilder.create()
+                .withName(name)
+                .withParent(parent);
+
+        expectedException.expect(IllegalStateException.class);
+        ErrorHandler.buildAsErrorHandler(handlerBuilder);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestFSActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestFSActionBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestFSActionBuilder.java
new file mode 100644
index 0000000..d83ceed
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestFSActionBuilder.java
@@ -0,0 +1,469 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class TestFSActionBuilder extends TestNodeBuilderBaseImpl<FSAction, 
FSActionBuilder> {
+    private static final String NAME_NODE = "${nameNode}";
+    private static final String[] JOB_XMLS = {"jobXml1.xml", "jobXml2.xml", 
"jobXml3.xml", "jobXml4.xml"};
+    private static final Delete[] DELETES = {new Delete("path0", null),
+                                             new Delete("path1", true),
+                                             new Delete("path2", false),
+                                             new Delete("path3", null)
+                                            };
+
+    private static final Mkdir[] MKDIRS = {new Mkdir("path0"),
+                                           new Mkdir("path1"),
+                                           new Mkdir("path2"),
+                                           new Mkdir("path3")
+                                          };
+
+    private static final Move[] MOVES = {new Move("source0", "target0"),
+                                         new Move("source1", "target1"),
+                                         new Move("source2", "target2"),
+                                         new Move("source3", "target3")
+                                        };
+
+    private static final Chmod[] CHMODS = {new 
ChmodBuilder().withPermissions("711").build(),
+                                           new 
ChmodBuilder().withPermissions("511").build(),
+                                           new 
ChmodBuilder().withPermissions("551").build(),
+                                           new 
ChmodBuilder().withPermissions("755").build()
+                                          };
+
+    private static final Touchz[] TOUCHZS = {new Touchz("path0"),
+                                             new Touchz("path1"),
+                                             new Touchz("path2"),
+                                             new Touchz("path3")
+                                            };
+
+    private static final Chgrp[] CHGRPS = {new 
ChgrpBuilder().withGroup("user0").build(),
+                                           new 
ChgrpBuilder().withGroup("user1").build(),
+                                           new 
ChgrpBuilder().withGroup("user2").build(),
+                                           new 
ChgrpBuilder().withGroup("user3").build()
+                                          };
+
+    private static final String MAPRED_JOB_QUEUE_NAME = 
"mapred.job.queue.name";
+    private static final String DEFAULT = "default";
+
+    @Override
+    protected FSActionBuilder getBuilderInstance() {
+        return FSActionBuilder.create();
+    }
+
+    @Override
+    protected FSActionBuilder getBuilderInstance(FSAction action) {
+        return FSActionBuilder.createFromExistingAction(action);
+    }
+
+    @Test
+    public void testNameNodeAdded() {
+        final FSActionBuilder builder = getBuilderInstance();
+        builder.withNameNode(NAME_NODE);
+
+        final FSAction mrAction = builder.build();
+        assertEquals(NAME_NODE, mrAction.getNameNode());
+    }
+
+    @Test
+    public void testNameNodeAddedTwiceThrows() {
+        final FSActionBuilder builder = getBuilderInstance();
+        builder.withNameNode(NAME_NODE);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withNameNode("any_string");
+    }
+
+    @Test
+    public void testSeveralJobXmlsAdded() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final String jobXml : JOB_XMLS) {
+            builder.withJobXml(jobXml);
+        }
+
+        final FSAction fsAction = builder.build();
+
+        final List<String> jobXmlsList = fsAction.getJobXmls();
+        assertEquals(JOB_XMLS.length, jobXmlsList.size());
+
+        for (int i = 0; i < JOB_XMLS.length; ++i) {
+            assertEquals(JOB_XMLS[i], jobXmlsList.get(i));
+        }
+    }
+
+    @Test
+    public void testWithoutJobXmls() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final String jobXml : JOB_XMLS) {
+            builder.withJobXml(jobXml);
+        }
+
+        builder.withoutJobXml(JOB_XMLS[0]);
+
+        final FSAction fsAction = builder.build();
+
+        final List<String> jobXmlsList = fsAction.getJobXmls();
+        final String[] remainingJobXmls = Arrays.copyOfRange(JOB_XMLS, 1, 
JOB_XMLS.length);
+        assertEquals(remainingJobXmls.length, jobXmlsList.size());
+
+        for (int i = 0; i < remainingJobXmls.length; ++i) {
+            assertEquals(remainingJobXmls[i], jobXmlsList.get(i));
+        }
+    }
+
+    @Test
+    public void testClearJobXmls() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final String jobXml : JOB_XMLS) {
+            builder.withJobXml(jobXml);
+        }
+
+        builder.clearJobXmls();
+
+        final FSAction fsAction = builder.build();
+
+        final List<String> jobXmlsList = fsAction.getJobXmls();
+        assertEquals(0, jobXmlsList.size());
+    }
+
+    @Test
+    public void testSameConfigPropertyAddedTwiceThrows() {
+        final FSActionBuilder builder = getBuilderInstance();
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+    }
+
+    @Test
+    public void testSeveralDeletesAdded() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Delete delete : DELETES) {
+            builder.withDelete(delete);
+        }
+
+        final FSAction fsAction = builder.build();
+
+        assertEquals(Arrays.asList(DELETES), fsAction.getDeletes());
+    }
+
+    @Test
+    public void testWithoutDelete() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Delete delete : DELETES) {
+            builder.withDelete(delete);
+        }
+
+        builder.withoutDelete(DELETES[0]);
+
+        final FSAction fsAction = builder.build();
+
+        final List<Delete> expectedDeletes = Arrays.asList(DELETES).subList(1, 
DELETES.length);
+        assertEquals(expectedDeletes, fsAction.getDeletes());
+    }
+
+    @Test
+    public void testClearDeletes() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Delete delete : DELETES) {
+            builder.withDelete(delete);
+        }
+
+        builder.clearDeletes();
+
+        final FSAction fsAction = builder.build();
+
+        assertTrue(fsAction.getDeletes().isEmpty());
+    }
+
+    @Test
+    public void testSeveralMkdirsAdded() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Mkdir mkdir : MKDIRS) {
+            builder.withMkdir(mkdir);
+        }
+
+        final FSAction fsAction = builder.build();
+
+        assertEquals(Arrays.asList(MKDIRS), fsAction.getMkdirs());
+    }
+
+    @Test
+    public void testWithoutMkdir() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Mkdir mkdir : MKDIRS) {
+            builder.withMkdir(mkdir);
+        }
+
+        builder.withoutMkdir(MKDIRS[0]);
+
+        final FSAction fsAction = builder.build();
+
+        final List<Mkdir> expectedMkdirs = Arrays.asList(MKDIRS).subList(1, 
MKDIRS.length);
+        assertEquals(expectedMkdirs, fsAction.getMkdirs());
+    }
+
+    @Test
+    public void testClearMkdirs() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Mkdir mkdir : MKDIRS) {
+            builder.withMkdir(mkdir);
+        }
+
+        builder.clearMkdirs();
+
+        final FSAction fsAction = builder.build();
+
+        assertTrue(fsAction.getMkdirs().isEmpty());
+    }
+
+    @Test
+    public void testSeveralMovesAdded() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Move move : MOVES) {
+            builder.withMove(move);
+        }
+
+        final FSAction fsAction = builder.build();
+
+        assertEquals(Arrays.asList(MOVES), fsAction.getMoves());
+    }
+
+    @Test
+    public void testWithoutMove() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Move move : MOVES) {
+            builder.withMove(move);
+        }
+
+        builder.withoutMove(MOVES[0]);
+
+        final FSAction fsAction = builder.build();
+
+        final List<Move> expectedMoves = Arrays.asList(MOVES).subList(1, 
MOVES.length);
+        assertEquals(expectedMoves, fsAction.getMoves());
+    }
+
+    @Test
+    public void testClearMoves() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Move move : MOVES) {
+            builder.withMove(move);
+        }
+
+        builder.clearMoves();
+
+        final FSAction fsAction = builder.build();
+
+        assertTrue(fsAction.getMoves().isEmpty());
+    }
+
+    @Test
+    public void testSeveralChmodsAdded() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Chmod chmod : CHMODS) {
+            builder.withChmod(chmod);
+        }
+
+        final FSAction fsAction = builder.build();
+
+        assertEquals(Arrays.asList(CHMODS), fsAction.getChmods());
+    }
+
+    @Test
+    public void testWithoutChmod() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Chmod chmod : CHMODS) {
+            builder.withChmod(chmod);
+        }
+
+        builder.withoutChmod(CHMODS[0]);
+
+        final FSAction fsAction = builder.build();
+
+        final List<Chmod> expectedChmods = Arrays.asList(CHMODS).subList(1, 
CHMODS.length);
+        assertEquals(expectedChmods, fsAction.getChmods());
+    }
+
+    @Test
+    public void testClearChmods() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Chmod chmod : CHMODS) {
+            builder.withChmod(chmod);
+        }
+
+        builder.clearChmods();
+
+        final FSAction fsAction = builder.build();
+
+        assertTrue(fsAction.getChmods().isEmpty());
+    }
+
+    @Test
+    public void testSeveralTouchzsAdded() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Touchz touchz : TOUCHZS) {
+            builder.withTouchz(touchz);
+        }
+
+        final FSAction fsAction = builder.build();
+
+        assertEquals(Arrays.asList(TOUCHZS), fsAction.getTouchzs());
+    }
+
+    @Test
+    public void testWithoutTouchz() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Touchz touchz : TOUCHZS) {
+            builder.withTouchz(touchz);
+        }
+
+        builder.withoutTouchz(TOUCHZS[0]);
+
+        final FSAction fsAction = builder.build();
+
+        final List<Touchz> expectedTouchzs = Arrays.asList(TOUCHZS).subList(1, 
TOUCHZS.length);
+        assertEquals(expectedTouchzs, fsAction.getTouchzs());
+    }
+
+    @Test
+    public void testClearTouchzs() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Touchz touchz : TOUCHZS) {
+            builder.withTouchz(touchz);
+        }
+
+        builder.clearTouchzs();
+
+        final FSAction fsAction = builder.build();
+
+        assertTrue(fsAction.getTouchzs().isEmpty());
+    }
+
+    @Test
+    public void testSeveralChgrpsAdded() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Chgrp chgrp : CHGRPS) {
+            builder.withChgrp(chgrp);
+        }
+
+        final FSAction fsAction = builder.build();
+
+        assertEquals(Arrays.asList(CHGRPS), fsAction.getChgrps());
+    }
+
+    @Test
+    public void testWithoutChgrp() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Chgrp chgrp : CHGRPS) {
+            builder.withChgrp(chgrp);
+        }
+
+        builder.withoutChgrp(CHGRPS[0]);
+
+        final FSAction fsAction = builder.build();
+
+        final List<Chgrp> expectedChgrps = Arrays.asList(CHGRPS).subList(1, 
CHGRPS.length);
+        assertEquals(expectedChgrps, fsAction.getChgrps());
+    }
+
+    @Test
+    public void testClearChgrps() {
+        final FSActionBuilder builder = getBuilderInstance();
+
+        for (final Chgrp chgrp : CHGRPS) {
+            builder.withChgrp(chgrp);
+        }
+
+        builder.clearChgrps();
+
+        final FSAction fsAction = builder.build();
+
+        assertTrue(fsAction.getChgrps().isEmpty());
+    }
+
+    @Test
+    public void testFromExistingFSAction() {
+        final String nameNode = "${nameNode}";
+
+        final FSActionBuilder builder = getBuilderInstance();
+        builder.withNameNode(nameNode)
+                .withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        final FSAction action = builder.build();
+
+        final FSActionBuilder fromExistingBuilder = getBuilderInstance(action);
+
+        final FSAction modifiedAction = fromExistingBuilder.build();
+        assertEquals(nameNode, modifiedAction.getNameNode());
+
+        final Map<String, String> expectedConfiguration = new 
LinkedHashMap<>();
+        expectedConfiguration.put(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+        assertEquals(expectedConfiguration, modifiedAction.getConfiguration());
+    }
+
+    @Test
+    public void testFromEmailAction() {
+        final EmailAction parent = EmailActionBuilder.create()
+                .withName("parent")
+                .build();
+
+        final EmailAction other = 
EmailActionBuilder.createFromExistingAction(parent)
+                .withName("other")
+                .withParent(parent)
+                .build();
+
+        final FSAction fromEmail = 
FSActionBuilder.createFromExistingAction(other)
+                .withName("fs")
+                .withNameNode("${nameNode}")
+                .build();
+
+        assertEquals(parent, fromEmail.getParentsWithoutConditions().get(0));
+        assertEquals("fs", fromEmail.getName());
+        assertEquals("${nameNode}", fromEmail.getNameNode());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestHive2ActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestHive2ActionBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestHive2ActionBuilder.java
new file mode 100644
index 0000000..0893c18
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestHive2ActionBuilder.java
@@ -0,0 +1,224 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestHive2ActionBuilder extends 
TestNodeBuilderBaseImpl<Hive2Action, Hive2ActionBuilder> {
+    private static final String NAME = "hive2-name";
+    private static final String NAME_NODE = "${nameNode}";
+    private static final String EXAMPLE_DIR = "/path/to/directory";
+    private static final String[] ARGS = {"arg1", "arg2", "arg3"};
+    private static final String MAPRED_JOB_QUEUE_NAME = 
"mapred.job.queue.name";
+    private static final String DEFAULT = "default";
+    private static final String RESOURCE_MANAGER = "${resourceManager}";
+    private static final String PATH_TO_DELETE = "/path/to/delete";
+    private static final String PATH_TO_MKDIR = "/path/to/mkdir";
+
+    @Override
+    protected Hive2ActionBuilder getBuilderInstance() {
+        return Hive2ActionBuilder.create();
+    }
+
+    @Override
+    protected Hive2ActionBuilder getBuilderInstance(final Hive2Action action) {
+        return Hive2ActionBuilder.createFromExistingAction(action);
+    }
+
+    @Test
+    public void testResourceManagerAdded() {
+        final Hive2ActionBuilder builder = getBuilderInstance();
+        builder.withResourceManager(RESOURCE_MANAGER);
+
+        final Hive2Action action = builder.build();
+        assertEquals(RESOURCE_MANAGER, action.getResourceManager());
+    }
+
+    @Test
+    public void testNameNodeAdded() {
+        final Hive2ActionBuilder builder = getBuilderInstance();
+        builder.withNameNode(NAME_NODE);
+
+        final Hive2Action action = builder.build();
+        assertEquals(NAME_NODE, action.getNameNode());
+    }
+
+    @Test
+    public void testPrepareAdded() {
+        final Hive2ActionBuilder builder = getBuilderInstance();
+        builder.withPrepare(new 
PrepareBuilder().withDelete(EXAMPLE_DIR).build());
+
+        final Hive2Action action = builder.build();
+        assertEquals(EXAMPLE_DIR, 
action.getPrepare().getDeletes().get(0).getPath());
+    }
+
+    @Test
+    public void testSameConfigPropertyAddedTwiceThrows() {
+        final Hive2ActionBuilder builder = getBuilderInstance();
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+    }
+
+    @Test
+    public void testSeveralArgsAdded() {
+        final Hive2ActionBuilder builder = getBuilderInstance();
+
+        for (final String arg : ARGS) {
+            builder.withArg(arg);
+        }
+
+        final Hive2Action action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        assertEquals(ARGS.length, argList.size());
+
+        for (int i = 0; i < ARGS.length; ++i) {
+            assertEquals(ARGS[i], argList.get(i));
+        }
+    }
+
+    @Test
+    public void testRemoveArgs() {
+        final Hive2ActionBuilder builder = getBuilderInstance();
+
+        for (final String file : ARGS) {
+            builder.withArg(file);
+        }
+
+        builder.withoutArg(ARGS[0]);
+
+        final Hive2Action action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        final String[] remainingArgs = Arrays.copyOfRange(ARGS, 1, 
ARGS.length);
+        assertEquals(remainingArgs.length, argList.size());
+
+        for (int i = 0; i < remainingArgs.length; ++i) {
+            assertEquals(remainingArgs[i], argList.get(i));
+        }
+    }
+
+    @Test
+    public void testClearArgs() {
+        final Hive2ActionBuilder builder = getBuilderInstance();
+
+        for (final String file : ARGS) {
+            builder.withArg(file);
+        }
+
+        builder.clearArgs();
+
+        final Hive2Action action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        assertEquals(0, argList.size());
+    }
+
+    @Test
+    public void testFromExistingHive2Action() {
+        final Hive2ActionBuilder builder = getBuilderInstance();
+
+        builder.withName(NAME)
+                .withResourceManager(RESOURCE_MANAGER)
+                .withNameNode(NAME_NODE)
+                .withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT)
+                .withPrepare(new PrepareBuilder()
+                        .withDelete(PATH_TO_DELETE)
+                        .withMkdir(PATH_TO_MKDIR)
+                        .build())
+                .withLauncher(new LauncherBuilder()
+                        .withMemoryMb(1024L)
+                        .withVCores(2L)
+                        .withQueue(DEFAULT)
+                        .withSharelib(DEFAULT)
+                        .withViewAcl(DEFAULT)
+                        .withModifyAcl(DEFAULT)
+                        .build())
+                .withArg(ARGS[0])
+                .withArg(ARGS[1])
+                .withJdbcUrl(DEFAULT)
+                .withPassword(DEFAULT)
+                .withQuery(DEFAULT)
+                .withArchive(DEFAULT)
+                .withFile(DEFAULT);
+
+        final Hive2Action action = builder.build();
+
+        final Hive2ActionBuilder fromExistingBuilder = 
getBuilderInstance(action);
+
+        final String newName = "fromExisting_" + NAME;
+        fromExistingBuilder.withName(newName)
+                .withoutArg(ARGS[1])
+                .withArg(ARGS[2]);
+
+        final Hive2Action modifiedAction = fromExistingBuilder.build();
+
+        assertEquals(newName, modifiedAction.getName());
+        assertEquals(action.getNameNode(), modifiedAction.getNameNode());
+
+        final Map<String, String> expectedConfiguration = new 
LinkedHashMap<>();
+        expectedConfiguration.put(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+        assertEquals(expectedConfiguration, modifiedAction.getConfiguration());
+
+        assertEquals(Arrays.asList(ARGS[0], ARGS[2]), 
modifiedAction.getArgs());
+
+        assertEquals(PATH_TO_DELETE, 
modifiedAction.getPrepare().getDeletes().get(0).getPath());
+        assertEquals(PATH_TO_MKDIR, 
modifiedAction.getPrepare().getMkdirs().get(0).getPath());
+
+        assertEquals(1024L, modifiedAction.getLauncher().getMemoryMb());
+        assertEquals(2L, modifiedAction.getLauncher().getVCores());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getQueue());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getSharelib());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getViewAcl());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getModifyAcl());
+
+        assertEquals(action.getJdbcUrl(), modifiedAction.getJdbcUrl());
+        assertEquals(action.getPassword(), modifiedAction.getPassword());
+        assertEquals(action.getScript(), modifiedAction.getScript());
+        assertEquals(action.getQuery(), modifiedAction.getQuery());
+    }
+
+    @Test
+    public void testFromOtherAction() {
+        final ShellAction parent = ShellActionBuilder.create()
+                .withName("parent")
+                .build();
+
+        final ShellAction otherAction = 
ShellActionBuilder.createFromExistingAction(parent)
+                .withName("shell")
+                .withParent(parent)
+                .build();
+
+        final Hive2Action fromOtherAction = 
Hive2ActionBuilder.createFromExistingAction(otherAction)
+                .withName("hive2")
+                .build();
+
+        assertEquals("hive2", fromOtherAction.getName());
+        assertEquals(parent, 
fromOtherAction.getParentsWithoutConditions().get(0));
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestHiveActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestHiveActionBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestHiveActionBuilder.java
new file mode 100644
index 0000000..4c9995a
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestHiveActionBuilder.java
@@ -0,0 +1,220 @@
+/**
+ * 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 org.apache.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestHiveActionBuilder extends TestNodeBuilderBaseImpl<HiveAction, 
HiveActionBuilder> {
+    private static final String NAME = "hive-name";
+    private static final String NAME_NODE = "${nameNode}";
+    private static final String EXAMPLE_DIR = "/path/to/directory";
+    private static final String[] ARGS = {"arg1", "arg2", "arg3"};
+    private static final String MAPRED_JOB_QUEUE_NAME = 
"mapred.job.queue.name";
+    private static final String DEFAULT = "default";
+    private static final String RESOURCE_MANAGER = "${resourceManager}";
+    private static final String PATH_TO_DELETE = "/path/to/delete";
+    private static final String PATH_TO_MKDIR = "/path/to/mkdir";
+
+    @Override
+    protected HiveActionBuilder getBuilderInstance() {
+        return HiveActionBuilder.create();
+    }
+
+    @Override
+    protected HiveActionBuilder getBuilderInstance(final HiveAction action) {
+        return HiveActionBuilder.createFromExistingAction(action);
+    }
+
+    @Test
+    public void testResourceManagerAdded() {
+        final HiveActionBuilder builder = getBuilderInstance();
+        builder.withResourceManager(RESOURCE_MANAGER);
+
+        final HiveAction action = builder.build();
+        assertEquals(RESOURCE_MANAGER, action.getResourceManager());
+    }
+
+    @Test
+    public void testNameNodeAdded() {
+        final HiveActionBuilder builder = getBuilderInstance();
+        builder.withNameNode(NAME_NODE);
+
+        final HiveAction action = builder.build();
+        assertEquals(NAME_NODE, action.getNameNode());
+    }
+
+    @Test
+    public void testPrepareAdded() {
+        final HiveActionBuilder builder = getBuilderInstance();
+        builder.withPrepare(new 
PrepareBuilder().withDelete(EXAMPLE_DIR).build());
+
+        final HiveAction action = builder.build();
+        assertEquals(EXAMPLE_DIR, 
action.getPrepare().getDeletes().get(0).getPath());
+    }
+
+    @Test
+    public void testSameConfigPropertyAddedTwiceThrows() {
+        final HiveActionBuilder builder = getBuilderInstance();
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+    }
+
+    @Test
+    public void testSeveralArgsAdded() {
+        final HiveActionBuilder builder = getBuilderInstance();
+
+        for (final String arg : ARGS) {
+            builder.withArg(arg);
+        }
+
+        final HiveAction action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        assertEquals(ARGS.length, argList.size());
+
+        for (int i = 0; i < ARGS.length; ++i) {
+            assertEquals(ARGS[i], argList.get(i));
+        }
+    }
+
+    @Test
+    public void testRemoveArgs() {
+        final HiveActionBuilder builder = getBuilderInstance();
+
+        for (final String file : ARGS) {
+            builder.withArg(file);
+        }
+
+        builder.withoutArg(ARGS[0]);
+
+        final HiveAction action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        final String[] remainingArgs = Arrays.copyOfRange(ARGS, 1, 
ARGS.length);
+        assertEquals(remainingArgs.length, argList.size());
+
+        for (int i = 0; i < remainingArgs.length; ++i) {
+            assertEquals(remainingArgs[i], argList.get(i));
+        }
+    }
+
+    @Test
+    public void testClearArgs() {
+        final HiveActionBuilder builder = getBuilderInstance();
+
+        for (final String file : ARGS) {
+            builder.withArg(file);
+        }
+
+        builder.clearArgs();
+
+        final HiveAction action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        assertEquals(0, argList.size());
+    }
+
+    @Test
+    public void testFromExistingHiveAction() {
+        final HiveActionBuilder builder = getBuilderInstance();
+
+        builder.withName(NAME)
+                .withResourceManager(RESOURCE_MANAGER)
+                .withNameNode(NAME_NODE)
+                .withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT)
+                .withPrepare(new PrepareBuilder()
+                        .withDelete(PATH_TO_DELETE)
+                        .withMkdir(PATH_TO_MKDIR)
+                        .build())
+                .withLauncher(new LauncherBuilder()
+                        .withMemoryMb(1024L)
+                        .withVCores(2L)
+                        .withQueue(DEFAULT)
+                        .withSharelib(DEFAULT)
+                        .withViewAcl(DEFAULT)
+                        .withModifyAcl(DEFAULT)
+                        .build())
+                .withArg(ARGS[0])
+                .withArg(ARGS[1])
+                .withQuery(DEFAULT)
+                .withArchive(DEFAULT)
+                .withFile(DEFAULT);
+
+        final HiveAction action = builder.build();
+
+        final HiveActionBuilder fromExistingBuilder = 
getBuilderInstance(action);
+
+        final String newName = "fromExisting_" + NAME;
+        fromExistingBuilder.withName(newName)
+                .withoutArg(ARGS[1])
+                .withArg(ARGS[2]);
+
+        final HiveAction modifiedAction = fromExistingBuilder.build();
+
+        assertEquals(newName, modifiedAction.getName());
+        assertEquals(action.getNameNode(), modifiedAction.getNameNode());
+
+        final Map<String, String> expectedConfiguration = new 
LinkedHashMap<>();
+        expectedConfiguration.put(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+        assertEquals(expectedConfiguration, modifiedAction.getConfiguration());
+
+        assertEquals(Arrays.asList(ARGS[0], ARGS[2]), 
modifiedAction.getArgs());
+
+        assertEquals(PATH_TO_DELETE, 
modifiedAction.getPrepare().getDeletes().get(0).getPath());
+        assertEquals(PATH_TO_MKDIR, 
modifiedAction.getPrepare().getMkdirs().get(0).getPath());
+
+        assertEquals(1024L, modifiedAction.getLauncher().getMemoryMb());
+        assertEquals(2L, modifiedAction.getLauncher().getVCores());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getQueue());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getSharelib());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getViewAcl());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getModifyAcl());
+
+        assertEquals(action.getScript(), modifiedAction.getScript());
+        assertEquals(action.getQuery(), modifiedAction.getQuery());
+    }
+
+    @Test
+    public void testFromOtherAction() {
+        final ShellAction parent = ShellActionBuilder.create()
+                .withName("parent")
+                .build();
+
+        final ShellAction otherAction = 
ShellActionBuilder.createFromExistingAction(parent)
+                .withName("shell")
+                .withParent(parent)
+                .build();
+
+        final HiveAction fromOtherAction = 
HiveActionBuilder.createFromExistingAction(otherAction)
+                .withName("hive")
+                .build();
+
+        assertEquals("hive", fromOtherAction.getName());
+        assertEquals(parent, 
fromOtherAction.getParentsWithoutConditions().get(0));
+    }
+}
\ No newline at end of file

Reply via email to