Exemplo n.º 1
0
  /** tests {@link PartialMultiplyMapper} */
  @Test
  public void testPartialMultiplyMapper() throws Exception {

    Vector similarityColumn = new RandomAccessSparseVector(Integer.MAX_VALUE, 100);
    similarityColumn.set(3, 0.5);
    similarityColumn.set(7, 0.8);

    Mapper<VarIntWritable, VectorAndPrefsWritable, VarLongWritable, PrefAndSimilarityColumnWritable>
            .Context
        context = EasyMock.createMock(Mapper.Context.class);

    PrefAndSimilarityColumnWritable one = new PrefAndSimilarityColumnWritable();
    PrefAndSimilarityColumnWritable two = new PrefAndSimilarityColumnWritable();
    one.set(1.0f, similarityColumn);
    two.set(3.0f, similarityColumn);

    context.write(EasyMock.eq(new VarLongWritable(123L)), EasyMock.eq(one));
    context.write(EasyMock.eq(new VarLongWritable(456L)), EasyMock.eq(two));

    EasyMock.replay(context);

    VectorAndPrefsWritable vectorAndPrefs =
        new VectorAndPrefsWritable(
            similarityColumn, Arrays.asList(123L, 456L), Arrays.asList(1.0f, 3.0f));

    new PartialMultiplyMapper().map(new VarIntWritable(1), vectorAndPrefs, context);

    EasyMock.verify(context);
  }
Exemplo n.º 2
0
  /**
   * tests {@link UserVectorSplitterMapper} in the special case that some userIDs shall be excluded
   */
  @Test
  public void testUserVectorSplitterMapperUserExclusion() throws Exception {
    Mapper<VarLongWritable, VectorWritable, VarIntWritable, VectorOrPrefWritable>.Context context =
        EasyMock.createMock(Mapper.Context.class);

    context.write(
        EasyMock.eq(new VarIntWritable(34)), prefOfVectorOrPrefWritableMatches(123L, 0.5f));
    context.write(
        EasyMock.eq(new VarIntWritable(56)), prefOfVectorOrPrefWritableMatches(123L, 0.7f));

    EasyMock.replay(context);

    FastIDSet usersToRecommendFor = new FastIDSet();
    usersToRecommendFor.add(123L);

    UserVectorSplitterMapper mapper = new UserVectorSplitterMapper();
    setField(mapper, "maxPrefsPerUserConsidered", 10);
    setField(mapper, "usersToRecommendFor", usersToRecommendFor);

    RandomAccessSparseVector vector = new RandomAccessSparseVector(Integer.MAX_VALUE, 100);
    vector.set(34, 0.5);
    vector.set(56, 0.7);

    mapper.map(new VarLongWritable(123L), new VectorWritable(vector), context);
    mapper.map(new VarLongWritable(456L), new VectorWritable(vector), context);

    EasyMock.verify(context);
  }
 private void mockLoadNullInitOffset(
     final String topic, final String group, final Partition partition) {
   EasyMock.expect(
           this.offsetStorage.load(
               EasyMock.eq(topic), EasyMock.contains(group), EasyMock.eq(partition)))
       .andReturn(null);
   this.offsetStorage.initOffset(
       EasyMock.eq(topic), EasyMock.contains(group), EasyMock.eq(partition), EasyMock.eq(0L));
   EasyMock.expectLastCall();
 }
  /** tests {@link CountUsersMapper} */
  public void testCountUsersMapper() throws Exception {
    Mapper<LongWritable, Text, CountUsersKeyWritable, VarLongWritable>.Context context =
        EasyMock.createMock(Mapper.Context.class);
    context.write(keyForUserID(12L), EasyMock.eq(new VarLongWritable(12L)));
    context.write(keyForUserID(35L), EasyMock.eq(new VarLongWritable(35L)));
    EasyMock.replay(context);

    CountUsersMapper mapper = new CountUsersMapper();
    mapper.map(null, new Text("12,100,1.3"), context);
    mapper.map(null, new Text("35,100,3.0"), context);

    EasyMock.verify(context);
  }
Exemplo n.º 5
0
  /** tests {@link ToVectorAndPrefReducer} */
  @Test
  public void testToVectorAndPrefReducer() throws Exception {
    Reducer<VarIntWritable, VectorOrPrefWritable, VarIntWritable, VectorAndPrefsWritable>.Context
        context = EasyMock.createMock(Reducer.Context.class);

    context.write(
        EasyMock.eq(new VarIntWritable(1)),
        vectorAndPrefsWritableMatches(
            Arrays.asList(123L, 456L),
            Arrays.asList(1.0f, 2.0f),
            MathHelper.elem(3, 0.5),
            MathHelper.elem(7, 0.8)));

    EasyMock.replay(context);

    Vector similarityColumn = new RandomAccessSparseVector(Integer.MAX_VALUE, 100);
    similarityColumn.set(3, 0.5);
    similarityColumn.set(7, 0.8);

    VectorOrPrefWritable itemPref1 = new VectorOrPrefWritable(123L, 1.0f);
    VectorOrPrefWritable itemPref2 = new VectorOrPrefWritable(456L, 2.0f);
    VectorOrPrefWritable similarities = new VectorOrPrefWritable(similarityColumn);

    new ToVectorAndPrefReducer()
        .reduce(new VarIntWritable(1), Arrays.asList(itemPref1, itemPref2, similarities), context);

    EasyMock.verify(context);
  }
  /**
   * Tests when the contents of the file fits into one write buffer.
   *
   * @throws Exception when something goes wrong
   */
  @Test
  public void testWriteSingleBufferFile() throws Exception {
    byte[] data = new byte[] {1, 2, 3, 4};

    AbstractStreamWriteFilter<M> filter = createFilter();
    M message = createMessage(data);

    WriteRequest writeRequest = new DefaultWriteRequest(message, new DummyWriteFuture());

    NextFilter nextFilter = EasyMock.createMock(NextFilter.class);
    /*
     * Record expectations
     */
    nextFilter.filterWrite(
        EasyMock.eq(session), eqWriteRequest(new DefaultWriteRequest(IoBuffer.wrap(data))));
    nextFilter.messageSent(session, writeRequest);

    /*
     * Replay.
     */
    EasyMock.replay(nextFilter);

    filter.filterWrite(nextFilter, session, writeRequest);
    filter.messageSent(nextFilter, session, writeRequest);

    /*
     * Verify.
     */
    EasyMock.verify(nextFilter);

    assertTrue(writeRequest.getFuture().isWritten());
  }
  @Test
  public void testUnsafeAOFileNonExistent() throws Exception {
    expect(mockBeanFactory.getInstance(currentUser)).andReturn(mockI18nHelper).anyTimes();
    expect(mockPermissionManager.hasPermission(Permissions.SYSTEM_ADMIN, currentUser))
        .andReturn(true);
    expect(
            mockI18nHelper.getText(
                EasyMock.eq("admin.errors.could.not.find.file"), EasyMock.<String>anyObject()))
        .andReturn("File does not exist.");

    final File file = File.createTempFile("testUnsafeAOFileNonExistent", "txt");
    file.deleteOnExit();

    try {
      final DataImportParams params =
          new DataImportParams.Builder(null)
              .setUnsafeJiraBackup(file)
              .setUnsafeAOBackup(
                  new File("I.really.really.don't.exist.and.if.i.did.it.would.be.very.unlucky"))
              .build();
      executeTest(params, false, DataImportService.ImportError.NONE);
      fail("Calling doImport with invalid validation result should have thrown an exception!");
    } catch (IllegalStateException e) {
      // yay
    } finally {
      file.delete();
    }
  }
  /**
   * Tests when the contents of the file doesn't fit into one write buffer.
   *
   * @throws Exception when something goes wrong
   */
  @Test
  public void testWriteSeveralBuffersStream() throws Exception {
    AbstractStreamWriteFilter<M> filter = createFilter();
    filter.setWriteBufferSize(4);

    byte[] data = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    byte[] chunk1 = new byte[] {1, 2, 3, 4};
    byte[] chunk2 = new byte[] {5, 6, 7, 8};
    byte[] chunk3 = new byte[] {9, 10};

    M message = createMessage(data);
    WriteRequest writeRequest = new DefaultWriteRequest(message, new DummyWriteFuture());

    WriteRequest chunk1Request = new DefaultWriteRequest(IoBuffer.wrap(chunk1));
    WriteRequest chunk2Request = new DefaultWriteRequest(IoBuffer.wrap(chunk2));
    WriteRequest chunk3Request = new DefaultWriteRequest(IoBuffer.wrap(chunk3));

    NextFilter nextFilter = EasyMock.createMock(NextFilter.class);
    /*
     * Record expectations
     */
    nextFilter.filterWrite(EasyMock.eq(session), eqWriteRequest(chunk1Request));
    nextFilter.filterWrite(EasyMock.eq(session), eqWriteRequest(chunk2Request));
    nextFilter.filterWrite(EasyMock.eq(session), eqWriteRequest(chunk3Request));
    nextFilter.messageSent(EasyMock.eq(session), eqWriteRequest(writeRequest));

    /*
     * Replay.
     */
    EasyMock.replay(nextFilter);

    filter.filterWrite(nextFilter, session, writeRequest);
    filter.messageSent(nextFilter, session, chunk1Request);
    filter.messageSent(nextFilter, session, chunk2Request);
    filter.messageSent(nextFilter, session, chunk3Request);

    /*
     * Verify.
     */
    EasyMock.verify(nextFilter);

    assertTrue(writeRequest.getFuture().isWritten());
  }
  @Test
  public void testExecuteBuildNumberTooOldInXml() throws Exception {
    expect(mockBeanFactory.getInstance(currentUser)).andReturn(mockI18nHelper).anyTimes();
    expect(mockI18nHelper.getText("data.import.parse.xml")).andReturn("Parsing XML");
    expect(
            mockI18nHelper.getText(
                EasyMock.eq("data.import.parse.progress"),
                EasyMock.<String>anyObject(),
                EasyMock.<String>anyObject()))
        .andReturn("Parsing progress");

    // called during validation!
    expect(mockPermissionManager.hasPermission(Permissions.SYSTEM_ADMIN, currentUser))
        .andReturn(true);

    // This is called during the first parse of the XML file.  At this stage nothing should have
    // been created yet!
    final MockGenericValue mockGv = new MockGenericValue("someentity");
    expect(mockOfBizDelegator.makeValue(EasyMock.<String>anyObject())).andReturn(mockGv).anyTimes();
    expect(mockAttachmentPathManager.getDefaultAttachmentPath())
        .andReturn(directories.get(0).getAbsolutePath())
        .anyTimes();
    expect(mockIndexPathManager.getDefaultIndexRootPath())
        .andReturn(directories.get(1).getAbsolutePath())
        .anyTimes();
    expect(
            mockLicenseStringFactory.create(
                EasyMock.<String>anyObject(), EasyMock.<String>anyObject()))
        .andStubReturn("");

    // after the first parse check the build number.
    expect(mockBuildUtilsInfo.getCurrentBuildNumber()).andStubReturn("400");
    expect(mockBuildUtilsInfo.getMinimumUpgradableBuildNumber()).andStubReturn("18");

    // after the first parse we also verify the license is good.
    expect(
            mockJiraLicenseService.validate(
                EasyMock.<I18nHelper>anyObject(), EasyMock.<String>anyObject()))
        .andStubReturn(mockValidationResult);
    expect(mockValidationResult.getLicenseVersion()).andStubReturn(2);
    expect(mockValidationResult.getErrorCollection()).andStubReturn(new SimpleErrorCollection());

    expect(
            mockExternalLinkUtil.getProperty(
                "external.link.jira.confluence.upgrade.guide.for.old.versions"))
        .andReturn("http://www.atlassian.com");

    expect(mockI18nHelper.getText("data.import.error.xml.too.old", "http://www.atlassian.com"))
        .andReturn("Data is too old visit http://www.atlassian.com/");

    final String filePath = getDataFilePath("jira-export-test-too-old.xml");
    final DataImportParams params = new DataImportParams.Builder(filePath).build();
    executeTest(params, false, DataImportService.ImportError.NONE);
  }
  @Test
  public void testVersion1License() throws Exception {
    expect(mockBeanFactory.getInstance(currentUser)).andReturn(mockI18nHelper).anyTimes();
    expect(mockI18nHelper.getText("data.import.parse.xml")).andReturn("Parsing XML");
    expect(
            mockI18nHelper.getText(
                EasyMock.eq("data.import.parse.progress"),
                EasyMock.<String>anyObject(),
                EasyMock.<String>anyObject()))
        .andReturn("Parsing progress");

    // called during validation!
    expect(mockPermissionManager.hasPermission(Permissions.SYSTEM_ADMIN, currentUser))
        .andReturn(true);
    expect(mockJiraLicenseService.validate(mockI18nHelper, "version1license"))
        .andReturn(mockValidationResult);
    expect(mockValidationResult.getLicenseVersion()).andReturn(1);
    expect(mockValidationResult.getErrorCollection()).andReturn(new SimpleErrorCollection());

    // This is called during the first parse of the XML file.  At this stage nothing should have
    // been created yet!
    final MockGenericValue mockGv = new MockGenericValue("someentity");
    expect(mockOfBizDelegator.makeValue(EasyMock.<String>anyObject())).andReturn(mockGv).anyTimes();
    expect(mockAttachmentPathManager.getDefaultAttachmentPath())
        .andReturn(directories.get(0).getAbsolutePath())
        .anyTimes();
    expect(mockIndexPathManager.getDefaultIndexRootPath())
        .andReturn(directories.get(1).getAbsolutePath())
        .anyTimes();
    expect(
            mockLicenseStringFactory.create(
                EasyMock.<String>anyObject(), EasyMock.<String>anyObject()))
        .andStubReturn("");

    // after the first parse check the build number.
    expect(mockBuildUtilsInfo.getCurrentBuildNumber()).andStubReturn("99999999");
    expect(mockBuildUtilsInfo.getMinimumUpgradableBuildNumber()).andStubReturn("0");

    // after the first parse we also verify the license is good.
    expect(
            mockJiraLicenseService.validate(
                EasyMock.<I18nHelper>anyObject(), EasyMock.<String>anyObject()))
        .andStubReturn(mockValidationResult);
    expect(mockValidationResult.getLicenseVersion()).andStubReturn(2);
    expect(mockValidationResult.getErrorCollection()).andStubReturn(new SimpleErrorCollection());

    final String filePath = getDataFilePath("jira-export-test.xml");
    final DataImportParams params =
        new DataImportParams.Builder(filePath).setLicenseString("version1license").build();
    executeTest(params, false, DataImportService.ImportError.V1_LICENSE_EXCEPTION);
  }
  @Test
  public void testWritesWriteRequestQueueWhenFinished() throws Exception {
    AbstractStreamWriteFilter<M> filter = createFilter();
    M message = createMessage(new byte[0]);

    WriteRequest wrs[] =
        new WriteRequest[] {
          new DefaultWriteRequest(new Object(), new DummyWriteFuture()),
          new DefaultWriteRequest(new Object(), new DummyWriteFuture()),
          new DefaultWriteRequest(new Object(), new DummyWriteFuture())
        };
    Queue<WriteRequest> queue = new LinkedList<WriteRequest>();
    queue.add(wrs[0]);
    queue.add(wrs[1]);
    queue.add(wrs[2]);

    /*
     * Make up the situation.
     */
    session.setAttribute(filter.CURRENT_STREAM, message);
    session.setAttribute(filter.CURRENT_WRITE_REQUEST, new DefaultWriteRequest(message));
    session.setAttribute(filter.WRITE_REQUEST_QUEUE, queue);

    /*
     * Record expectations
     */
    NextFilter nextFilter = EasyMock.createMock(NextFilter.class);
    nextFilter.filterWrite(session, wrs[0]);
    nextFilter.filterWrite(session, wrs[1]);
    nextFilter.filterWrite(session, wrs[2]);
    nextFilter.messageSent(EasyMock.eq(session), eqWriteRequest(new DefaultWriteRequest(message)));

    /*
     * Replay.
     */
    EasyMock.replay(nextFilter);

    filter.messageSent(nextFilter, session, new DefaultWriteRequest(new Object()));
    assertEquals(0, queue.size());

    /*
     * Verify.
     */
    EasyMock.verify(nextFilter);
  }
  @Test
  public void testInitializationSecondRunning() throws Exception {
    DateTime expectedStartTime = new DateTime(0);

    EasyMock.expect(mockFlow1.getName()).andReturn("a").once();
    EasyMock.expect(mockFlow2.getName()).andReturn("b").once();

    EasyMock.expect(mockFlow1.getStatus()).andReturn(Status.READY).times(3);
    EasyMock.expect(mockFlow2.getStatus()).andReturn(Status.RUNNING).times(3);

    EasyMock.expect(mockFlow1.getStartTime()).andReturn(null).once();
    EasyMock.expect(mockFlow2.getStartTime()).andReturn(expectedStartTime).once();

    Capture<FlowCallback> callbackCapture = new Capture<FlowCallback>();
    mockFlow2.execute(EasyMock.eq(props), EasyMock.capture(callbackCapture));

    EasyMock.expect(mockFlow1.getName()).andReturn("1").once();
    EasyMock.expect(mockFlow2.getName()).andReturn("2").once();

    EasyMock.expect(mockFlow2.getParentProps()).andReturn(props).once();

    EasyMock.replay(mockFlow1, mockFlow2, props);

    flow = new GroupedExecutableFlow("blah", mockFlow1, mockFlow2);

    Assert.assertEquals(Status.READY, flow.getStatus());
    Assert.assertEquals(expectedStartTime, flow.getStartTime());
    Assert.assertEquals(null, flow.getEndTime());
    Assert.assertEquals(props, flow.getParentProps());

    EasyMock.verify(mockFlow1, mockFlow2, props);
    EasyMock.reset(mockFlow1, mockFlow2, props);

    EasyMock.expect(mockFlow1.getStatus()).andReturn(Status.READY).once();
    EasyMock.expect(mockFlow2.getStatus()).andReturn(Status.SUCCEEDED).once();

    EasyMock.replay(mockFlow1, mockFlow2, props);

    callbackCapture.getValue().completed(Status.SUCCEEDED);

    Assert.assertEquals(Status.READY, flow.getStatus());
    Assert.assertEquals(expectedStartTime, flow.getStartTime());
    Assert.assertEquals(null, flow.getEndTime());
    Assert.assertEquals(emptyExceptions, flow.getExceptions());
  }
Exemplo n.º 13
0
  /** tests {@link AggregateAndRecommendReducer} */
  @Test
  public void testAggregateAndRecommendReducer() throws Exception {
    Reducer<
                VarLongWritable,
                PrefAndSimilarityColumnWritable,
                VarLongWritable,
                RecommendedItemsWritable>
            .Context
        context = EasyMock.createMock(Reducer.Context.class);

    context.write(
        EasyMock.eq(new VarLongWritable(123L)),
        recommendationsMatch(
            new GenericRecommendedItem(1L, 2.8f), new GenericRecommendedItem(2L, 2.0f)));

    EasyMock.replay(context);

    RandomAccessSparseVector similarityColumnOne =
        new RandomAccessSparseVector(Integer.MAX_VALUE, 100);
    similarityColumnOne.set(1, 0.1);
    similarityColumnOne.set(2, 0.5);

    RandomAccessSparseVector similarityColumnTwo =
        new RandomAccessSparseVector(Integer.MAX_VALUE, 100);
    similarityColumnTwo.set(1, 0.9);
    similarityColumnTwo.set(2, 0.5);

    List<PrefAndSimilarityColumnWritable> values =
        Arrays.asList(
            new PrefAndSimilarityColumnWritable(1.0f, similarityColumnOne),
            new PrefAndSimilarityColumnWritable(3.0f, similarityColumnTwo));

    OpenIntLongHashMap indexItemIDMap = new OpenIntLongHashMap();
    indexItemIDMap.put(1, 1L);
    indexItemIDMap.put(2, 2L);

    AggregateAndRecommendReducer reducer = new AggregateAndRecommendReducer();

    setField(reducer, "indexItemIDMap", indexItemIDMap);
    setField(reducer, "recommendationsPerUser", 3);

    reducer.reduce(new VarLongWritable(123L), values, context);

    EasyMock.verify(context);
  }
Exemplo n.º 14
0
  /** tests {@link org.apache.mahout.cf.taste.hadoop.item.ItemFilterAsVectorAndPrefsReducer} */
  @Test
  public void testItemFilterAsVectorAndPrefsReducer() throws Exception {
    Reducer<VarLongWritable, VarLongWritable, VarIntWritable, VectorAndPrefsWritable>.Context
        context = EasyMock.createMock(Reducer.Context.class);

    int itemIDIndex = TasteHadoopUtils.idToIndex(123L);
    context.write(
        EasyMock.eq(new VarIntWritable(itemIDIndex)),
        vectorAndPrefsForFilteringMatches(123L, 456L, 789L));

    EasyMock.replay(context);

    new ItemFilterAsVectorAndPrefsReducer()
        .reduce(
            new VarLongWritable(123L),
            Arrays.asList(new VarLongWritable(456L), new VarLongWritable(789L)),
            context);

    EasyMock.verify(context);
  }
  @Test
  public void testFileNonExistent() throws Exception {
    expect(mockBeanFactory.getInstance(currentUser)).andReturn(mockI18nHelper).anyTimes();
    expect(mockPermissionManager.hasPermission(Permissions.SYSTEM_ADMIN, currentUser))
        .andReturn(true);
    expect(mockJiraHome.getImportDirectory()).andReturn(new File("somewhere")).anyTimes();
    expect(
            mockI18nHelper.getText(
                EasyMock.eq("admin.errors.could.not.find.file"), EasyMock.<String>anyObject()))
        .andReturn("File does not exist.");

    try {
      final DataImportParams params =
          new DataImportParams.Builder("idontexisthopefully.txt").build();
      executeTest(params, false, DataImportService.ImportError.NONE);
      fail("Calling doImport with invalid validation result should have thrown an exception!");
    } catch (IllegalStateException e) {
      // yay
    }
  }
Exemplo n.º 16
0
  /** tests {@link SimilarityMatrixRowWrapperMapper} */
  @Test
  public void testSimilarityMatrixRowWrapperMapper() throws Exception {
    Mapper<IntWritable, VectorWritable, VarIntWritable, VectorOrPrefWritable>.Context context =
        EasyMock.createMock(Mapper.Context.class);

    context.write(
        EasyMock.eq(new VarIntWritable(12)),
        vectorOfVectorOrPrefWritableMatches(MathHelper.elem(34, 0.5), MathHelper.elem(56, 0.7)));

    EasyMock.replay(context);

    RandomAccessSparseVector vector = new RandomAccessSparseVector(Integer.MAX_VALUE, 100);
    vector.set(12, 1.0);
    vector.set(34, 0.5);
    vector.set(56, 0.7);

    new SimilarityMatrixRowWrapperMapper()
        .map(new IntWritable(12), new VectorWritable(vector), context);

    EasyMock.verify(context);
  }
Exemplo n.º 17
0
  /** tests {@link ToUserVectorReducer} using boolean data */
  @Test
  public void testToUserVectorReducerWithBooleanData() throws Exception {
    Reducer<VarLongWritable, VarLongWritable, VarLongWritable, VectorWritable>.Context context =
        EasyMock.createMock(Reducer.Context.class);

    context.write(
        EasyMock.eq(new VarLongWritable(12L)),
        MathHelper.vectorMatches(
            MathHelper.elem(TasteHadoopUtils.idToIndex(34L), 1.0),
            MathHelper.elem(TasteHadoopUtils.idToIndex(56L), 1.0)));

    EasyMock.replay(context);

    new ToUserVectorReducer()
        .reduce(
            new VarLongWritable(12L),
            Arrays.asList(new VarLongWritable(34L), new VarLongWritable(56L)),
            context);

    EasyMock.verify(context);
  }
Exemplo n.º 18
0
  /** tests {@link ToUserVectorReducer} */
  @Test
  public void testToUserVectorReducer() throws Exception {
    Reducer<VarLongWritable, VarLongWritable, VarLongWritable, VectorWritable>.Context context =
        EasyMock.createMock(Reducer.Context.class);

    context.write(
        EasyMock.eq(new VarLongWritable(12L)),
        MathHelper.vectorMatches(
            MathHelper.elem(TasteHadoopUtils.idToIndex(34L), 1.0),
            MathHelper.elem(TasteHadoopUtils.idToIndex(56L), 2.0)));

    EasyMock.replay(context);

    Collection<VarLongWritable> varLongWritables = new LinkedList<VarLongWritable>();
    varLongWritables.add(new EntityPrefWritable(34L, 1.0f));
    varLongWritables.add(new EntityPrefWritable(56L, 2.0f));

    new ToUserVectorReducer().reduce(new VarLongWritable(12L), varLongWritables, context);

    EasyMock.verify(context);
  }
  @Test
  public void testSanity() throws Exception {
    final AtomicLong numJobsComplete = new AtomicLong(0);

    /** ** Setup mockFlow1 *** */
    final Capture<FlowCallback> flow1Callback = new Capture<FlowCallback>();
    mockFlow1.execute(EasyMock.eq(props), EasyMock.capture(flow1Callback));
    EasyMock.expectLastCall()
        .andAnswer(
            new IAnswer<Void>() {
              @Override
              public Void answer() throws Throwable {
                Assert.assertEquals(Status.RUNNING, flow.getStatus());
                Assert.assertEquals(1, numJobsComplete.incrementAndGet());

                flow1Callback.getValue().completed(Status.SUCCEEDED);

                Assert.assertEquals(Status.RUNNING, flow.getStatus());

                return null;
              }
            })
        .once();

    Props mockFlow1Props = new Props();
    mockFlow1Props.put("1", "1");
    mockFlow1Props.put("2", "1");

    EasyMock.expect(mockFlow1.getStatus()).andReturn(Status.SUCCEEDED).times(2);
    EasyMock.expect(mockFlow1.getReturnProps()).andReturn(mockFlow1Props).once();

    /** ** Setup mockFlow2 *** */
    final Capture<FlowCallback> flow2Callback = new Capture<FlowCallback>();
    mockFlow2.execute(EasyMock.eq(props), EasyMock.capture(flow2Callback));
    EasyMock.expectLastCall()
        .andAnswer(
            new IAnswer<Void>() {
              @Override
              public Void answer() throws Throwable {
                Assert.assertEquals(Status.RUNNING, flow.getStatus());
                Assert.assertEquals(2, numJobsComplete.incrementAndGet());

                flow2Callback.getValue().completed(Status.SUCCEEDED);

                Assert.assertEquals(Status.SUCCEEDED, flow.getStatus());

                return null;
              }
            })
        .once();

    EasyMock.expect(mockFlow2.getStatus())
        .andAnswer(
            new IAnswer<Status>() {
              private volatile AtomicInteger count = new AtomicInteger(0);

              @Override
              public Status answer() throws Throwable {
                switch (count.getAndIncrement()) {
                  case 0:
                    return Status.READY;
                  case 1:
                    return Status.SUCCEEDED;
                  default:
                    Assert.fail("mockFlow2.getStatus() should only be called 2 times.");
                }
                return null;
              }
            })
        .times(2);

    Props mockFlow2Props = new Props();
    mockFlow2Props.put("2", "2");
    mockFlow2Props.put("3", "2");
    EasyMock.expect(mockFlow2.getReturnProps()).andReturn(mockFlow2Props).once();

    EasyMock.expect(props.equalsProps(props)).andReturn(true).once();

    EasyMock.replay(mockFlow1, mockFlow2, props);

    /** ** Start the test *** */
    AtomicBoolean callbackRan = new AtomicBoolean(false);
    flow.execute(
        props,
        new OneCallFlowCallback(callbackRan) {
          @Override
          public void theCallback(Status status) {
            Assert.assertEquals(Status.SUCCEEDED, status);
            Assert.assertEquals(2, numJobsComplete.get());
          }
        });

    Assert.assertTrue("Callback wasn't run.", callbackRan.get());
    Assert.assertEquals(Status.SUCCEEDED, flow.getStatus());
    Assert.assertEquals(emptyExceptions, flow.getExceptions());
    Assert.assertEquals(props, flow.getParentProps());

    callbackRan = new AtomicBoolean(false);
    flow.execute(
        props,
        new OneCallFlowCallback(callbackRan) {
          @Override
          protected void theCallback(Status status) {
            Assert.assertEquals(Status.SUCCEEDED, status);
            Assert.assertEquals(2, numJobsComplete.get());
          }
        });

    Assert.assertTrue("Callback wasn't run.", callbackRan.get());
    Assert.assertEquals(Status.SUCCEEDED, flow.getStatus());
    Assert.assertEquals(emptyExceptions, flow.getExceptions());

    Props retProps = flow.getReturnProps();
    Assert.assertEquals(3, retProps.size());
    Assert.assertEquals("1", retProps.get("1"));
    Assert.assertEquals("2", retProps.get("2"));
    Assert.assertEquals("2", retProps.get("3"));

    EasyMock.verify(props);
    EasyMock.reset(props);

    EasyMock.expect(props.equalsProps(props)).andReturn(false).once();

    EasyMock.replay(props);

    boolean exceptionThrown = false;
    try {
      flow.execute(
          props,
          new FlowCallback() {
            @Override
            public void progressMade() {}

            @Override
            public void completed(Status status) {}
          });
    } catch (IllegalArgumentException e) {
      exceptionThrown = true;
    }

    Assert.assertTrue(
        "Expected an IllegalArgumentException to be thrown because props weren't the same.",
        exceptionThrown);
  }
  @Test
  public void testFailureJob1() throws Exception {

    final AtomicLong numJobsComplete = new AtomicLong(0);

    /** ** Setup mockFlow1 *** */
    final Capture<FlowCallback> flow1Callback = new Capture<FlowCallback>();
    mockFlow1.execute(EasyMock.eq(props), EasyMock.capture(flow1Callback));
    EasyMock.expectLastCall()
        .andAnswer(
            new IAnswer<Void>() {
              @Override
              public Void answer() throws Throwable {
                Assert.assertEquals(Status.RUNNING, flow.getStatus());
                Assert.assertEquals(1, numJobsComplete.incrementAndGet());

                flow1Callback.getValue().completed(Status.FAILED);

                return null;
              }
            })
        .once();

    EasyMock.expect(mockFlow1.getStatus()).andReturn(Status.FAILED).times(1);
    EasyMock.expect(mockFlow1.getExceptions()).andReturn(theExceptions).times(1);

    EasyMock.expect(props.equalsProps(props)).andReturn(true).once();

    /** ** Setup mockFlow2 *** */
    EasyMock.expect(mockFlow2.getExceptions()).andReturn(emptyExceptions).times(1);
    EasyMock.replay(mockFlow1, mockFlow2, props);

    /** ** Start the test *** */
    AtomicBoolean callbackRan = new AtomicBoolean(false);
    flow.execute(
        props,
        new OneCallFlowCallback(callbackRan) {
          @Override
          public void theCallback(Status status) {
            Assert.assertEquals(Status.FAILED, status);
          }
        });

    Assert.assertTrue("Callback wasn't run.", callbackRan.get());
    Assert.assertEquals(Status.FAILED, flow.getStatus());
    Assert.assertEquals(theExceptions, flow.getExceptions());

    callbackRan = new AtomicBoolean(false);
    flow.execute(
        props,
        new OneCallFlowCallback(callbackRan) {
          @Override
          protected void theCallback(Status status) {
            Assert.assertEquals(Status.FAILED, status);
          }
        });

    Assert.assertTrue("Callback wasn't run.", callbackRan.get());
    Assert.assertEquals(Status.FAILED, flow.getStatus());
    Assert.assertEquals(theExceptions, flow.getExceptions());

    Assert.assertTrue("Expected to be able to reset the flow", flow.reset());
    Assert.assertEquals(Status.READY, flow.getStatus());
    Assert.assertEquals(emptyExceptions, flow.getExceptions());
  }
  @Test
  public void testFailureJob2() throws Exception {

    final AtomicLong numJobsComplete = new AtomicLong(0);

    /** ** Setup mockFlow1 *** */
    final Capture<FlowCallback> flow1Callback = new Capture<FlowCallback>();
    mockFlow1.execute(EasyMock.eq(props), EasyMock.capture(flow1Callback));
    EasyMock.expectLastCall()
        .andAnswer(
            new IAnswer<Void>() {
              @Override
              public Void answer() throws Throwable {
                Assert.assertEquals(Status.RUNNING, flow.getStatus());
                Assert.assertEquals(1, numJobsComplete.incrementAndGet());

                flow1Callback.getValue().completed(Status.SUCCEEDED);

                return null;
              }
            })
        .once();

    EasyMock.expect(mockFlow1.getStatus()).andReturn(Status.SUCCEEDED).times(2);
    EasyMock.expect(mockFlow1.getExceptions()).andReturn(emptyExceptions).times(1);

    /** ** Setup mockFlow2 *** */
    final Capture<FlowCallback> flow2Callback = new Capture<FlowCallback>();
    mockFlow2.execute(EasyMock.eq(props), EasyMock.capture(flow2Callback));
    EasyMock.expectLastCall()
        .andAnswer(
            new IAnswer<Void>() {
              @Override
              public Void answer() throws Throwable {
                Assert.assertEquals(Status.RUNNING, flow.getStatus());
                Assert.assertEquals(2, numJobsComplete.incrementAndGet());

                flow2Callback.getValue().completed(Status.FAILED);

                return null;
              }
            })
        .once();

    EasyMock.expect(mockFlow2.getStatus())
        .andAnswer(
            new IAnswer<Status>() {
              private volatile AtomicInteger count = new AtomicInteger(0);

              @Override
              public Status answer() throws Throwable {
                switch (count.getAndIncrement()) {
                  case 0:
                    return Status.READY;
                  case 1:
                    return Status.FAILED;
                  default:
                    Assert.fail("mockFlow2.getStatus() should only be called 2 times.");
                }
                return null;
              }
            })
        .times(2);

    final RuntimeException e1 = new RuntimeException();
    final RuntimeException e2 = new RuntimeException();

    final Map<String, Throwable> e1s = new HashMap<String, Throwable>();
    e1s.put("e1", e1);
    e1s.put("e2", e2);

    EasyMock.expect(mockFlow2.getExceptions()).andReturn(e1s).times(1);
    EasyMock.expect(props.equalsProps(props)).andReturn(true).once();

    EasyMock.replay(mockFlow1, mockFlow2, props);

    /** ** Start the test *** */
    AtomicBoolean callbackRan = new AtomicBoolean(false);
    flow.execute(
        props,
        new OneCallFlowCallback(callbackRan) {
          @Override
          public void theCallback(Status status) {
            Assert.assertEquals(Status.FAILED, status);
          }
        });

    Assert.assertTrue("Callback wasn't run.", callbackRan.get());
    Assert.assertEquals(Status.FAILED, flow.getStatus());
    Assert.assertEquals(e1s, flow.getExceptions());

    callbackRan = new AtomicBoolean(false);
    flow.execute(
        props,
        new OneCallFlowCallback(callbackRan) {
          @Override
          protected void theCallback(Status status) {
            Assert.assertEquals(Status.FAILED, status);
            Assert.assertEquals(2, numJobsComplete.get());
          }
        });

    Assert.assertTrue("Callback wasn't run.", callbackRan.get());
    Assert.assertEquals(Status.FAILED, flow.getStatus());
    Assert.assertEquals(e1s, flow.getExceptions());

    Assert.assertTrue("Expected to be able to reset the flow", flow.reset());
    Assert.assertEquals(Status.READY, flow.getStatus());
    Assert.assertEquals(emptyExceptions, flow.getExceptions());
  }
  @Test
  public void testAllCallbacksCalled() throws Exception {
    final AtomicLong numJobsComplete = new AtomicLong(0);
    final AtomicBoolean executeCallWhileStateWasRunningHadItsCallbackCalled =
        new AtomicBoolean(false);

    /** ** Setup mockFlow1 *** */
    final Capture<FlowCallback> flow1Callback = new Capture<FlowCallback>();
    mockFlow1.execute(EasyMock.eq(props), EasyMock.capture(flow1Callback));
    EasyMock.expectLastCall()
        .andAnswer(
            new IAnswer<Void>() {
              @Override
              public Void answer() throws Throwable {
                Assert.assertEquals(Status.RUNNING, flow.getStatus());
                Assert.assertEquals(1, numJobsComplete.incrementAndGet());

                flow.execute(
                    props,
                    new OneCallFlowCallback(executeCallWhileStateWasRunningHadItsCallbackCalled) {
                      @Override
                      protected void theCallback(Status status) {}
                    });

                flow1Callback.getValue().completed(Status.SUCCEEDED);

                return null;
              }
            })
        .once();

    EasyMock.expect(mockFlow1.getStatus()).andReturn(Status.SUCCEEDED).times(2);

    /** ** Setup mockFlow2 *** */
    final Capture<FlowCallback> flow2Callback = new Capture<FlowCallback>();
    mockFlow2.execute(EasyMock.eq(props), EasyMock.capture(flow2Callback));
    EasyMock.expectLastCall()
        .andAnswer(
            new IAnswer<Void>() {
              @Override
              public Void answer() throws Throwable {
                Assert.assertEquals(Status.RUNNING, flow.getStatus());
                Assert.assertEquals(2, numJobsComplete.incrementAndGet());

                flow2Callback.getValue().completed(Status.SUCCEEDED);

                return null;
              }
            })
        .once();

    EasyMock.expect(mockFlow2.getStatus())
        .andAnswer(
            new IAnswer<Status>() {
              private volatile AtomicInteger count = new AtomicInteger(0);

              @Override
              public Status answer() throws Throwable {
                switch (count.getAndIncrement()) {
                  case 0:
                    return Status.READY;
                  case 1:
                    return Status.SUCCEEDED;
                  default:
                    Assert.fail("mockFlow2.getStatus() should only be called 2 times.");
                }
                return null;
              }
            })
        .times(2);

    EasyMock.expect(mockFlow1.getReturnProps()).andReturn(new Props()).once();
    EasyMock.expect(mockFlow2.getReturnProps()).andReturn(new Props()).once();

    EasyMock.expect(props.equalsProps(props)).andReturn(true).times(2);

    EasyMock.replay(mockFlow1, mockFlow2, props);

    /** ** Start the test *** */
    AtomicBoolean callbackRan = new AtomicBoolean(false);
    flow.execute(
        props,
        new OneCallFlowCallback(callbackRan) {
          @Override
          public void theCallback(Status status) {
            Assert.assertEquals(Status.SUCCEEDED, status);
            Assert.assertEquals(2, numJobsComplete.get());
          }
        });

    Assert.assertTrue("Callback wasn't run.", callbackRan.get());
    Assert.assertEquals(Status.SUCCEEDED, flow.getStatus());
    Assert.assertTrue(
        "mockFlow1, upon completion, sends another execute() call to the flow.  "
            + "The callback from that execute call was apparently not called.",
        executeCallWhileStateWasRunningHadItsCallbackCalled.get());
    Assert.assertEquals(emptyExceptions, flow.getExceptions());

    callbackRan = new AtomicBoolean(false);
    flow.execute(
        props,
        new OneCallFlowCallback(callbackRan) {
          @Override
          protected void theCallback(Status status) {
            Assert.assertEquals(Status.SUCCEEDED, status);
            Assert.assertEquals(2, numJobsComplete.get());
          }
        });

    Assert.assertTrue("Callback wasn't run.", callbackRan.get());
    Assert.assertEquals(Status.SUCCEEDED, flow.getStatus());
    Assert.assertEquals(emptyExceptions, flow.getExceptions());
  }
  @Test
  public void testInitializationBothRunning() throws Exception {
    DateTime expectedStartTime = new DateTime(0);
    DateTime falseStartTime = new DateTime(1);

    EasyMock.expect(mockFlow1.getName()).andReturn("a").once();
    EasyMock.expect(mockFlow2.getName()).andReturn("b").once();

    EasyMock.expect(mockFlow1.getStatus()).andReturn(Status.RUNNING).times(3);
    EasyMock.expect(mockFlow2.getStatus()).andReturn(Status.RUNNING).times(3);

    EasyMock.expect(mockFlow1.getStartTime()).andReturn(falseStartTime).once();
    EasyMock.expect(mockFlow2.getStartTime()).andReturn(expectedStartTime).once();

    Capture<FlowCallback> callbackCapture1 = new Capture<FlowCallback>();
    Capture<FlowCallback> callbackCapture2 = new Capture<FlowCallback>();
    mockFlow1.execute(EasyMock.eq(props), EasyMock.capture(callbackCapture1));
    mockFlow2.execute(EasyMock.eq(props), EasyMock.capture(callbackCapture2));

    EasyMock.expect(mockFlow1.getParentProps()).andReturn(props).once();
    EasyMock.expect(mockFlow2.getParentProps()).andReturn(props).once();
    EasyMock.expect(props.equalsProps(props)).andReturn(true).once();

    EasyMock.expect(mockFlow1.getName()).andReturn("1").once();
    EasyMock.expect(mockFlow2.getName()).andReturn("2").once();

    EasyMock.replay(mockFlow1, mockFlow2, props);

    flow = new GroupedExecutableFlow("blah", mockFlow1, mockFlow2);

    Assert.assertEquals(Status.RUNNING, flow.getStatus());
    Assert.assertEquals(expectedStartTime, flow.getStartTime());
    Assert.assertEquals(null, flow.getEndTime());

    EasyMock.verify(mockFlow1, mockFlow2, props);
    EasyMock.reset(mockFlow1, mockFlow2, props);

    EasyMock.expect(mockFlow1.getStatus()).andReturn(Status.RUNNING).once();
    EasyMock.expect(mockFlow2.getStatus()).andReturn(Status.SUCCEEDED).once();

    EasyMock.replay(mockFlow1, mockFlow2, props);

    Assert.assertSame(callbackCapture1.getValue(), callbackCapture2.getValue());

    callbackCapture1.getValue().completed(Status.SUCCEEDED);

    Assert.assertEquals(Status.RUNNING, flow.getStatus());
    Assert.assertEquals(expectedStartTime, flow.getStartTime());
    Assert.assertEquals(null, flow.getEndTime());
    Assert.assertEquals(emptyExceptions, flow.getExceptions());
    Assert.assertEquals(props, flow.getParentProps());

    EasyMock.verify(mockFlow1, mockFlow2, props);
    EasyMock.reset(mockFlow1, mockFlow2, props);

    EasyMock.expect(mockFlow1.getStatus()).andReturn(Status.SUCCEEDED).once();
    EasyMock.expect(mockFlow2.getStatus()).andReturn(Status.SUCCEEDED).once();

    EasyMock.expect(mockFlow1.getReturnProps()).andReturn(new Props()).once();
    EasyMock.expect(mockFlow2.getReturnProps()).andReturn(new Props()).once();

    EasyMock.replay(mockFlow1, mockFlow2, props);

    DateTime beforeTheEnd = new DateTime();
    callbackCapture2.getValue().completed(Status.SUCCEEDED);

    Assert.assertEquals(Status.SUCCEEDED, flow.getStatus());
    Assert.assertEquals(expectedStartTime, flow.getStartTime());
    Assert.assertFalse(
        String.format(
            "flow's end time[%s] should be after beforeTheEnd[%s]",
            flow.getEndTime(), beforeTheEnd),
        beforeTheEnd.isAfter(flow.getEndTime()));
    Assert.assertEquals(emptyExceptions, flow.getExceptions());
  }