// Copyright 2012 The Closure Library Authors. All Rights Reserved.
// Use of this source code is governed by the Apache License, Version 2.0.

goog.provide('goog.result.combineTest');
goog.setTestOnly('goog.result.combineTest');

goog.require('goog.Timer');
goog.require('goog.array');
goog.require('goog.result');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.recordFunction');

var result1, result2, result3, result4, resultCallback;
var combinedResult, successCombinedResult, mockClock;

function setUpPage() {
  mockClock = new goog.testing.MockClock();
  mockClock.install();
}

function tearDownPage() {
  goog.dispose(mockClock);
}

function setUp() {
  mockClock.reset();
  result1 = new goog.result.SimpleResult();
  result2 = new goog.result.SimpleResult();
  result3 = new goog.result.SimpleResult();
  result4 = new goog.result.SimpleResult();

  combinedResult = goog.result.combine(result1, result2, result3, result4);

  successCombinedResult =
      goog.result.combineOnSuccess(result1, result2, result3, result4);

  resultCallback = goog.testing.recordFunction();
}

function tearDown() {
  result1 = result2 = result3 = result4 = resultCallback = null;
  combinedResult = successCombinedResult = null;
}

function testSynchronousCombine() {
  resolveAllGivenResultsToSuccess();

  var newCombinedResult =
      goog.result.combine(result1, result2, result3, result4);

  goog.result.wait(newCombinedResult, resultCallback);

  assertSuccessCall(newCombinedResult, resultCallback);
}

function testCombineWhenAllResultsSuccess() {
  goog.result.wait(combinedResult, resultCallback);

  resolveAllGivenResultsToSuccess();

  assertSuccessCall(combinedResult, resultCallback);
}

function testAsyncCombineWhenAllResultsSuccess() {
  goog.result.wait(combinedResult, resultCallback);

  goog.Timer.callOnce(function() { resolveAllGivenResultsToSuccess(); });
  mockClock.tick();

  assertSuccessCall(combinedResult, resultCallback);
}

function testCombineWhenAllResultsFail() {
  goog.result.wait(combinedResult, resultCallback);

  resolveAllGivenResultsToError();

  assertSuccessCall(combinedResult, resultCallback);
}

function testAsyncCombineWhenAllResultsFail() {
  goog.result.wait(combinedResult, resultCallback);

  goog.Timer.callOnce(function() { resolveAllGivenResultsToError(); });
  mockClock.tick();

  assertSuccessCall(combinedResult, resultCallback);
}

function testCombineWhenSomeResultsSuccess() {
  goog.result.wait(combinedResult, resultCallback);

  resolveSomeGivenResultsToSuccess();

  assertSuccessCall(combinedResult, resultCallback);
}

function testAsyncCombineWhenSomeResultsSuccess() {
  goog.result.wait(combinedResult, resultCallback);

  goog.Timer.callOnce(function() { resolveSomeGivenResultsToSuccess(); });
  mockClock.tick();

  assertSuccessCall(combinedResult, resultCallback);
}

function testCombineOnSuccessWhenAllResultsSuccess() {
  goog.result.wait(successCombinedResult, resultCallback);

  resolveAllGivenResultsToSuccess();

  assertSuccessCall(successCombinedResult, resultCallback);
}

function testAsyncCombineOnSuccessWhenAllResultsSuccess() {
  goog.result.wait(successCombinedResult, resultCallback);

  goog.Timer.callOnce(function() { resolveAllGivenResultsToSuccess(); });
  mockClock.tick();

  assertSuccessCall(successCombinedResult, resultCallback);
}

function testCombineOnSuccessWhenAllResultsFail() {
  goog.result.wait(successCombinedResult, resultCallback);

  resolveAllGivenResultsToError();

  assertErrorCall(successCombinedResult, resultCallback);
}

function testAsyncCombineOnSuccessWhenAllResultsFail() {
  goog.result.wait(successCombinedResult, resultCallback);

  goog.Timer.callOnce(function() { resolveAllGivenResultsToError(); });
  mockClock.tick();

  assertErrorCall(successCombinedResult, resultCallback);
}

function testCombineOnSuccessWhenSomeResultsSuccess() {
  goog.result.wait(successCombinedResult, resultCallback);

  resolveSomeGivenResultsToSuccess();

  assertErrorCall(successCombinedResult, resultCallback);
}

function testAsyncCombineOnSuccessWhenSomeResultsSuccess() {
  goog.result.wait(successCombinedResult, resultCallback);

  goog.Timer.callOnce(function() { resolveSomeGivenResultsToSuccess(); });
  mockClock.tick();

  assertErrorCall(successCombinedResult, resultCallback);
}

function testCancelParentResults() {
  goog.result.wait(combinedResult, resultCallback);

  goog.result.cancelParentResults(combinedResult);

  assertArgumentContainsGivenResults(combinedResult.getValue())
  goog.array.forEach([result1, result2, result3, result4],
      function(result) {
        assertTrue(result.isCanceled());
      });
}

function assertSuccessCall(combinedResult, resultCallback) {
  assertEquals(goog.result.Result.State.SUCCESS, combinedResult.getState());
  assertEquals(1, resultCallback.getCallCount());

  var result = resultCallback.popLastCall().getArgument(0);
  assertEquals(combinedResult, result);
  assertArgumentContainsGivenResults(result.getValue());
}

function assertErrorCall(combinedResult, resultCallback) {
  assertEquals(goog.result.Result.State.ERROR,
                 combinedResult.getState());
  assertEquals(1, resultCallback.getCallCount());

  var result = resultCallback.popLastCall().getArgument(0);
  assertEquals(combinedResult, result);
  assertArgumentContainsGivenResults(combinedResult.getError());
}

function assertArgumentContainsGivenResults(resultsArray) {
  assertEquals(4, resultsArray.length);

  goog.array.forEach([result1, result2, result3, result4], function(res) {
      assertTrue(goog.array.contains(resultsArray, res));
  });
}

function resolveAllGivenResultsToSuccess() {
  goog.array.forEach([result1, result2, result3, result4], function(res) {
      res.setValue(1);
  });
}

function resolveAllGivenResultsToError() {
  goog.array.forEach([result1, result2, result3, result4], function(res) {
      res.setError();
  });
}

function resolveSomeGivenResultsToSuccess() {
  goog.array.forEach([result2, result3, result4], function(res) {
      res.setValue(1);
  });
  result1.setError();
}
