/**
 * UnitTest.js - unit test framework for for JavaScript Object.
 *
 * @author R. S. Doiel
 *
 * This file is part of Phiz.
 *
 * copyright (c) 2009 all rights reserved
 *
 *  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *   
 *   * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *   
 *   * Neither the name of the Phiz nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 *   
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *   
 */

/**
 * UnitTest - a general purpose, light weight unit test object for
 * boot strapping JavaScript code.  Serious Unit testing should probably
 * use something link Yahoo's Unit Test framework.
 */
var UnitTest = function () {
  this.message_queue = [];
  this.pass_count = 0;
  this.fail_count = 0;

  /**
   * passed - display the number of tests passed and increment the pass count
   * @param total_only - if true return the total number of passed tests without
   * incrementing the running count.
   * @return the running total of passed tests.
   */
  this.passed = function (total_only) {
    if (this.pass_count === undefined) {
      this.pass_count = 0;
    }
    if (total_only === undefined) {
      this.pass_count += 1;
    }
    return this.pass_count;
  };

  /**
   * failed - increment the failed count or get the total failed count.
   * @param total_only - if true then skip incrementing the failed count.
   * @return running total of failed tests.
   */
  this.failed = function (total_only) {
    if (this.fail_count === undefined) {
      this.fail_count = 0;
    }
    if (total_only === undefined) {
      this.fail_count += 1;
    }
    return this.fail_count;
  };
  
  /**
   * assertTrue - Check to see if something is true and increment
   * the running total of passed or failed tests.
   * @param expression - the expression to be evaluted
   * @param fail_message - the message to return on failure
   * @return true if passed or false otherwise.
   */
  this.assertTrue = function (expression, fail_message) {
    if (expression === true) {
      this.passed();
      return true;
    }
    this.failed();
    this.message("Test Failed: " + fail_message);
    return false;
  };


  /**
   * assertSame - check to items and compare with ===
   * @param item1
   * @param item2
   * @return true if same, false otherwise
   */
  this.assertSame = function (item1, item2, fail_message) {
    return this.assertTrue(item1 === item2, fail_message);
  }


  /**
   * assertNotSame - check to items and compare with !==
   * @param item1
   * @param item2
   * @return true if same, false otherwise
   */
  this.assertNotSame = function (item1, item2, fail_message) {
    return this.assertFalse(item1 === item2, fail_message);
  }


  /**
   * assertFail - fail a test and record the message.
   * @param fail_message - faulure message to return
   * @return false always
   */  
  this.assertFail = function (fail_message) {
    this.failed();
    this.message("Test Failed: " + fail_message);
    return false;
  };
  
  
  /**
   * assertFalse - Check to see if an expression returns false, 
   * and increment success and failure counts.
   * @param expression - test for resolving to false
   * @param fail_message - failure message to return
   * @return true if passed or false otherwise.
   */
  this.assertFalse = function (expression, fail_message) {
    if (expression === false) {
      this.passed();
      return true;
    }
    this.failed();
    this.message("Test Failed: " + fail_message);
    return false;
  };

  
  /**
   * assertNotFalse - Check to see if something !== false.
   * @param item - the thing to check against boolean false.
   * @param fail_message - the failure message to return.
   * @return true if not false, false otherwise
   */
  this.assertNotFalse = function (item, fail_message) {
    return this.assertTrue(item !== false, fail_message);
  };
  

  /**
   * assertNotTrue - Check to see if something !== True.
   * @param item - the thing to check against boolean true.
   * @param fail_message - the failure message to return.
   * @return true if not true, false otherwise
   */
  this.assertNotTrue = function (item, fail_message) {
    return this.assertTrue(item !== true, fail_message);
  };


  /**
   * Run the defined Unit Tests for an object.
   * @return the accumulated text of results.
   */
  this.run = function () {
    var attribute, errors = 0, tests_passed = 0, tests_failed = 0;
    for (attribute in this) {
      if (typeof this[attribute] === 'function' &&
          attribute.indexOf('test') === 0) {
        this.pass_count = 0;
        this.fail_count = 0;
        errors = this.fail_count;
        this.message("Running " + attribute);
        this[attribute]();
        if (errors === this.fail_count) {
          this.message("\t" + attribute + " OK");
          this.message("\t" + this.passed(true) + " assertions passed.");
          tests_passed += 1;
        } else {
          this.message("\t" + attribute + " failed.");
          this.message("\t" + this.passed(true) + " assertions passed.");
          this.message("\t" + this.failed(true) + " assertions failed.");
          tests_failed += 1;
        }
      }
    }
    this.message("\nSummary");
    this.message(tests_passed + " tests passed.");
    this.message(tests_failed + " tests failed.\n");
    return this.message();
  };
  

  /**
   * message - add a message to the message queue or return and 
   * clear the message queue.
   * @param msg
   * @return true, message queue or false if error
   */
  this.message = function (msg) {
    var src;
    
    if (this.message_queue === undefined) {
      this.message_queue = [];
    }
    if (msg === undefined) {
      src = this.message_queue.join("\n");
      delete this.message_queue;
      return src;
    }
    this.message_queue.push(msg);
    return true;
  };
  

  /**
   * messageCount - return the number of messages in message queue
   * @return the number of messages in queue.
   */
  this.messageCount = function () {
    if (this.message_queue === undefined) {
      this.message_queue = [];
    }
    return this.message_queue.length;
  }

  return this;
};