/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Runner of the restful tests. Subclass of opensocialtestrunner.
 */

/**
 * Creates a new OpenSocial Test Runner. It takes TestSuiteDirectory as input.
 * If TestSuite is given as input it converts it into TestSuiteDirectory for
 * backward compatibility with existing cases.
 * @param {TestSuiteDirectory/TestSuite} object The directory or test suite to
 *     test.
 * @constructor
 */
function RestfulTestRunner(object) {
  /**
   * The test suite directory that will be run
   * @type {TestSuiteDirectory}
   */
  this.testSuiteDirectory = null;

  if (object.suites) {
    this.testSuiteDirectory = object;
  } else {
    var directory = new TestSuiteDirectory(object.id, object.name);
    directory.suites.push(object);
    this.testSuiteDirectory = directory;
  }

  /**
   * Logger used for displaying test report
   * @type {ComplianceHtmlLogger}
   */
  this.logger = null;

  /**
   * Context in which we are running the tests
   * @type {Context}
   */
  this.context = null;

  /**
   * flag to check whether to skip set up tests or not
   * @type {boolean}
   */
  this.skipSetupTests = false;
}


/**
 * Inherit testRunner
 */
RestfulTestRunner.prototype = new TestRunner;

/**
 * Setups this TestRunner objects preparing it to run the tests and verifies
 * the existence of viewer and owner objects as well as its correctness.
 * @param {Function()} finishCallback The callback to be invoked when the setup
 *     is finished.
 * @override
 */
RestfulTestRunner.prototype.setupTests = function(finishCallback) {
  this.renderTestGrid("Running restful API test", finishCallback);
};

/**
 * Prints a preview table to monitor test execution.
 * @param {string} header An optional header to be printed at the top of the
 *     test output.
 */
RestfulTestRunner.prototype.renderTestGrid = function(header, finishCallback) {
  this.logger = new HtmlLogger(
      document.getElementById('testResults'), header, this.testSuiteDirectory);
  this.logger.restfulPreTestDialog(finishCallback);
};

/**
 * Gets the context object for the tests.
 * @return {object} The context generated for the tests.
 * @override
 */
RestfulTestRunner.prototype.getContext = function() {
  return this.context;
};

/**
 * Gets the golden results for the tests to compare their results against it.
 * @return {object} The golden results to compare against the test results.
 * @override
 */
RestfulTestRunner.prototype.getGoldenResults = function() {
  return this.goldenResults;
};

/**
 * Saves the golden results and then prints the output on the gadget canvas when
 * all tests have finished.
 * @param {Array.<ResultSet>} resultSets The result sets of the test suites.
 * @override
 */
RestfulTestRunner.prototype.onAllFinish = function(resultSets) {
  var runner = new Helper.FunctionRunner();
  runner.add(this.logger.updateSummaryHeader, [resultSets], this.logger);
  runner.add(this.logger.updateResultBoxes,
      [resultSets, this.goldenResults], this.logger);
  runner.run();
};

/**
 * Updates suite header when suite has finished
 * all tests have finished.
 * @param {Array.<ResultSet>} resultSets The result sets of the test suites.
 * @override
 */
RestfulTestRunner.prototype.onSuiteFinish = function(resultSets) {
  var runner = new Helper.FunctionRunner();
  runner.add(this.logger.updateSuitesHeader, [resultSets], this.logger);
  runner.run();
};

/**
 * Change the status of a particular test when it finishes its execution
 * changing the displayed value to the right of the id.
 * @param {ResultGroup} result The result of the test.
 * @param {Test} test The test that generated the result.
 * @override
 */
RestfulTestRunner.prototype.onTestFinish = function(result, test) {
  var runner = new Helper.FunctionRunner();
  runner.add(this.logger.updateTestResult, [result, test], this.logger);
  runner.add(this.logger.updateSuiteSummary, [result, test], this.logger);
  runner.run();
};

/**
 * Executes all the tests in the suite.
 */
RestfulTestRunner.prototype.executeTests = function() {
  var validator = new Validator(this.getContext(), this.testSuiteDirectory);
  validator.goldenResults = this.getGoldenResults();
  validator.testFinishFunction = this.onTestFinish.bind(this);
  validator.suiteFinishFunction = this.onSuiteFinish.bind(this);
  validator.runManualTest = this.runManualTest;
  validator.runTests(this.onAllFinish.bind(this));
};

/**
 * Clears the test suite directory array and adds only the specified test
 * as suite to it.
 * @param {String} testId Id of the test to add.
 */
RestfulTestRunner.prototype.setSingleTestAsDirectory = function(testId) {
  var directory = this.testSuiteDirectory;
  for (var i = 0; i < directory.suites.length; i++) {
    var testSuite = directory.suites[i];
    for (var j = 0; j < testSuite.tests.length; j++) {
      if (testSuite.tests[j].id == testId) {
        this.testSuiteDirectory.suites = [];
        var suite = new TestSuite();
        suite.tests.push(testSuite.tests[j]);
        suite.id = testSuite.id;
        suite.name = testSuite.name;
        this.testSuiteDirectory.suites.push(suite);
        return;
      }
    }
  }
};

/**
 * Re-runs only the specified test suite.
 * @param {String} suiteId Id of the testSuite to re-run.
 */
RestfulTestRunner.prototype.reRunSuite = function(suiteId) {
  this.setSingleSuiteAsDirectory(suiteId);
  this.runManualTest = true;
  this.run();
};


/**
 * Clears the test suite array and adds only the specified test to it.
 * @param {String} suiteId Id of the test to add.
 */
RestfulTestRunner.prototype.setSingleSuiteAsDirectory = function(suiteId) {
  var directory = this.testSuiteDirectory;
  for (var i = 0; i < directory.suites.length; i++) {
    if (directory.suites[i].id == suiteId) {
        this.testSuiteDirectory.suites = [directory.suites[i]];
        return;
    }
  }
};
