<?php
// Call ProjectReportTest::main() if this source file is executed directly.
if (!defined("PHPUnit_MAIN_METHOD")) {
    define("PHPUnit_MAIN_METHOD", "ProjectReportTest::main");
}

require_once "PHPUnit/Framework/TestCase.php";
require_once "PHPUnit/Framework/TestSuite.php";

require_once "testConf.php";
require_once ROOT_PATH.'/lib/dao/DMLFunctions.php';
require_once ROOT_PATH.'/lib/dao/SQLQBuilder.php';
require_once ROOT_PATH.'/lib/confs/sysConf.php';
require_once ROOT_PATH.'/lib/models/eimadmin/ProjectActivity.php';
require_once ROOT_PATH.'/lib/models/time/Timesheet.php';
require_once ROOT_PATH.'/lib/models/time/ProjectActivityTime.php';
require_once ROOT_PATH.'/lib/models/time/EmployeeActivityTime.php';
require_once ROOT_PATH.'/lib/models/time/ProjectReport.php';

/**
 * Test class for ProjectReport.
 * Generated by PHPUnit_Util_Skeleton on 2007-07-16 at 19:32:46.
 */
class ProjectReportTest extends PHPUnit_Framework_TestCase {

	private $projects;
	private $customerId;
	private $customerName;
	private $activities;

	private $connection;

    /**
     * Runs the test methods of this class.
     *
     * @access public
     * @static
     */
    public static function main() {
        require_once "PHPUnit/TextUI/TestRunner.php";

        $suite  = new PHPUnit_Framework_TestSuite("ProjectReportTest");
        $result = PHPUnit_TextUI_TestRunner::run($suite);
    }

    /**
     * Sets up the fixture, for example, open a network connection.
     * This method is called before a test is executed.
     *
     * @access protected
     */
    protected function setUp() {

    	$conf = new Conf();
    	$this->connection = mysql_connect($conf->dbhost.":".$conf->dbport, $conf->dbuser, $conf->dbpass);
        mysql_select_db($conf->dbname);

		$this->_truncateTables();

		// Insert a customer and projects for use in the test
		$this->customerId = 1;
		$this->customerName = "OrangeHRM";
		$this->projects = array( 1 => "Project A", 2 => "Project B");

		$this->_addCustomer();
		$this->_addProjects();

		// Insert activities
		$this->_addActivity(1, "Programming");
		$this->_addActivity(1, "QA");
		//$this->_addActivity(1, "Misc");
		$this->_addActivity(1, "Support");
		$this->_addActivity(2, "Programming");
		$this->_addActivity(2, "QA");
		$this->_addActivity(2, "Support");
		//$this->_addActivity(2, "Misc");

		// Insert deleted activities
		$this->_addActivity(1, "Deleted Activity", true);
		$this->_addActivity(2, "Deleted Activity", true);

		// Add employees
		$this->_addEmployee(1, "001",  "Rajasinghe", "John");
		$this->_addEmployee(2, "002",  "Wickramasinghe", "Ravi");
		$this->_addEmployee(3, "003",  "Kumarasinghe", "Rasanga");
    }

    /**
     * Tears down the fixture, removed database entries created during test.
     *
     * @access protected
     */
    protected function tearDown() {
		$this->_truncateTables();
    }

    /**
     * test method getProjectActivityTime()
     */
    public function testGetProjectActivityTimeWithNoEvents() {

    	$report = new ProjectReport();

    	// With no events - no days specified
    	$projectId = 1;
    	$results = $report->getProjectActivityTime($projectId);
    	$res = $this->_verifyActivityTime($results, $projectId, array("Programming", "QA", "Support"), array(0, 0, 0));
    	$this->assertTrue($res);

    	//  With no events - with start date specified
    	$results = $report->getProjectActivityTime($projectId, "2001-01-01");
    	$res = $this->_verifyActivityTime($results, $projectId, array("Programming", "QA", "Support"), array(0, 0, 0));
		$this->assertTrue($res);

    	//  With no events - with start date and end date specified
    	$results = $report->getProjectActivityTime($projectId, "2001-01-01", "2007-01-01");
    	$res = $this->_verifyActivityTime($results, $projectId, array("Programming", "QA", "Support"), array(0, 0, 0));
		$this->assertTrue($res);

    }

    /**
     * test method getProjectActivityTime()
     */
    public function testGetProjectActivityTimeWithOneEvent() {

    	$report = new ProjectReport();

		// Add one event.
    	$projectId = 1;
		$this->_addEvent(1, 1, "Programming", "2007-07-12 10:00", 60);
    	$results = $report->getProjectActivityTime($projectId, "2007-07-12", "2007-07-12");
    	$res = $this->_verifyActivityTime($results, $projectId, array("Programming", "QA", "Support"), array(60, 0, 0));
    	$this->assertTrue($res);
    	$results = $report->getProjectActivityTime($projectId, "2007-07-1", "2007-07-13");
    	$res = $this->_verifyActivityTime($results, $projectId, array("Programming", "QA", "Support"), array(60, 0, 0));
    	$this->assertTrue($res);
    }

    /**
     * test method getProjectActivityTime()
     */
    public function testGetProjectActivityTimeWithSpanningEvent() {

    	$report = new ProjectReport();

		// Add one event.
    	$projectId = 1;
		$this->_addEvent(1, 1, "QA", "2007-07-12 23:00", 95);
    	$results = $report->getProjectActivityTime($projectId, "2007-07-12", "2007-07-12");
    	$res = $this->_verifyActivityTime($results, $projectId, array("Programming", "QA", "Support"), array(0, 60, 0));
    	$this->assertTrue($res);
    	$results = $report->getProjectActivityTime($projectId, "2007-07-12", "2007-07-13");
    	$res = $this->_verifyActivityTime($results, $projectId, array("Programming", "QA", "Support"), array(0, 95, 0));
    	$this->assertTrue($res);
    	$results = $report->getProjectActivityTime($projectId, "2007-07-1", "2007-07-13");
    	$res = $this->_verifyActivityTime($results, $projectId, array("Programming", "QA", "Support"), array(0, 95, 0));
    	$this->assertTrue($res);
    }

    /**
     * test method getProjectActivityTime()
     */
    public function testGetProjectActivityTimeWithDeletedActivity() {

    	$report = new ProjectReport();

		// Add one event.
    	$projectId = 1;
		$this->_addEvent(1, 1, "Deleted Activity", "2007-07-12 13:20", 15);
    	$results = $report->getProjectActivityTime($projectId, "2007-07-12", "2007-07-12");
    	$res = $this->_verifyActivityTime($results, $projectId,
               array("Programming", "QA", "Support", "Deleted Activity"), array(0, 0, 0, 15));
    	$this->assertTrue($res);
    	$results = $report->getProjectActivityTime($projectId, "2007-07-1", "2007-07-13");
    	$res = $this->_verifyActivityTime($results, $projectId,
               array("Programming", "QA", "Support", "Deleted Activity"), array(0, 0, 0, 15));
    	$this->assertTrue($res);
    }

    /**
     * test method getProjectActivityTime()
     */
    public function testGetProjectActivityTimeWithMultipleActivities() {

    	$report = new ProjectReport();

    	// QA - Employee 1
		$this->_addEvent(1, 1, "QA", "2007-07-05  09:25", 70);
		$this->_addEvent(1, 1, "QA", "2007-07-07  08:12", 33); // 33 min
		$this->_addEvent(1, 1, "QA", "2007-07-10  09:25", 8 * 60 + 30); // 510 min

    	// QA - Employee 2
		$this->_addEvent(1, 2, "QA", "2007-07-05  09:25", 70); // 70 min
		$this->_addEvent(1, 2, "QA", "2007-07-12  11:25", 4 * 60 + 10); // 250 min
		$this->_addEvent(1, 2, "QA", "2007-07-11  06:25", 4 * 60 + 10); // 250 min

		// QA Events for different project
		$this->_addEvent(2, 1, "QA", "2007-07-05  07:25", 8 * 60 + 10); // 490 min
		$this->_addEvent(2, 2, "QA", "2007-07-05  09:25", 5 * 60); // 300 min

		// Programming - Employee 1
		$this->_addEvent(1, 1, "Programming", "2007-07-05  09:25", 80);
		$this->_addEvent(1, 1, "Programming", "2007-07-07  08:12", 70);
		$this->_addEvent(1, 1, "Programming", "2007-07-10  09:25", 210);

		// Programming - Employee 2
		$this->_addEvent(1, 2, "Programming", "2007-07-05  09:25", 230);
		$this->_addEvent(1, 2, "Programming", "2007-07-12  11:25", 450);
		$this->_addEvent(1, 2, "Programming", "2007-07-11  06:25", 900);

		// Programming for different project
		$this->_addEvent(2, 1, "Programming", "2007-07-05  05:25", 800);
		$this->_addEvent(2, 2, "Programming", "2007-07-12  15:25", 450);

		// Event spanning more than one day
		$this->_addEvent(1, 1, "Programming", "2007-07-19 18:00", 600);
		$this->_addEvent(1, 2, "Programming", "2007-07-18 21:25", 500);


		// Deleted activity for project 2
		$this->_addEvent(2, 1, "Deleted Activity", "2007-07-10  09:25", 400);
		$this->_addEvent(2, 2, "Deleted Activity", "2007-07-11  09:25", 300);

		// Check project 2 time
    	$results = $report->getProjectActivityTime(2, "2007-07-05", "2007-07-25");
    	$res = $this->_verifyActivityTime($results, 2, array("Programming", "QA", "Support", "Deleted Activity"),
                                          array(1250, 790, 0, 700));
    	$this->assertTrue($res);

		// Check project 1 time
    	$results = $report->getProjectActivityTime(1, "2007-07-05", "2007-07-25");
    	$res = $this->_verifyActivityTime($results, 1, array("Programming", "QA", "Support"), array(3040, 1183, 0));
    	$this->assertTrue($res);

    	// Project 1 time for day with spanning events.
    	$results = $report->getProjectActivityTime(1, "2007-07-19", "2007-07-19");
    	$res = $this->_verifyActivityTime($results, 1, array("Programming", "QA", "Support"), array(360+345, 0, 0));
    	$this->assertTrue($res);

		// Project 1 time from 07-10
    	$results = $report->getProjectActivityTime(1, "2007-07-10", "2007-07-30");
    	$res = $this->_verifyActivityTime($results, 1, array("Programming", "QA", "Support"), array(2660, 1010, 0));
    	$this->assertTrue($res);

    }

    /**
     * test getEmployeeActivityTime() method
     */
    public function testGetEmployeeActivityTimeWithNoEvents() {
    	$report = new ProjectReport();

    	// With no events - no days specified
    	$projectId = 1;
    	$activityId = 1;

    	$results = $report->getEmployeeActivityTime($projectId, $activityId);
    	$this->assertTrue(is_array($results));
    	$this->assertEquals(0, count($results));

    	//  With no events - start date specified
    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2001-01-01");
    	$this->assertTrue(is_array($results));
    	$this->assertEquals(0, count($results));

    	//  With no events - start date and end date specified
    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2001-01-01", "2007-01-01");
    	$this->assertTrue(is_array($results));
    	$this->assertEquals(0, count($results));
    }

	/**
	 * test getEmployeeActivityTime() method
	 */
    public function testGetEmployeeActivityTimeWithOneEvent() {
    	$report = new ProjectReport();

		// Add one event.
    	$projectId = 1;
    	$employeeId = 1;
    	$activityName = "Programming";
		$activity = $this->activities[$projectId][$activityName];

    	$activityId = $activity->getId();
		$this->_addEvent($projectId, $employeeId, $activityName, "2007-07-12 10:00", 60);

    	$results = $report->getEmployeeActivityTime($projectId, $activityId);
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(60));
    	$this->assertTrue($res);

    	//  With no events - start date specified
    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2001-01-01");
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(60));
		$this->assertTrue($res);

    	//  With no events - start date and end date specified
    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2001-01-01", "2007-07-13");
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(60));
		$this->assertTrue($res);
    }

	/**
	 * test getEmployeeActivityTime() method
	 */
	public function testGetEmployeeActivityTimeWithSpanningEvent() {
    	$report = new ProjectReport();

		// Add one event.
    	$projectId = 1;
    	$employeeId = 1;
    	$activityName = "QA";
		$activity = $this->activities[$projectId][$activityName];

    	$activityId = $activity->getId();
		$this->_addEvent($projectId, $employeeId, $activityName, "2007-07-12 23:00", 95);

    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2007-07-12", "2007-07-12");
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(60));
    	$this->assertTrue($res);

    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2007-07-12", "2007-07-13");
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(95));
    	$this->assertTrue($res);

    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2007-07-1", "2007-07-15");
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(95));
    	$this->assertTrue($res);

		// With only start date
    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2007-07-1");
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(95));
    	$this->assertTrue($res);

		// Without range
    	$results = $report->getEmployeeActivityTime($projectId, $activityId);
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(95));
    	$this->assertTrue($res);

	}

	/**
	 * test getEmployeeActivityTime() method
	 */
    public function testGetEmployeeActivityTimeWithDeletedActivity() {
    	$report = new ProjectReport();

		// Add one event.
    	$projectId = 1;
    	$employeeId = 1;
    	$activityName = "Deleted Activity";
		$activity = $this->activities[$projectId][$activityName];

    	$activityId = $activity->getId();
		$this->_addEvent($projectId, $employeeId, $activityName, "2007-07-12 13:20", 15);

    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2007-07-12", "2007-07-12");
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(15));
    	$this->assertTrue($res);

    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2007-07-01", "2007-07-13");
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(15));
    	$this->assertTrue($res);

		// With only start date
    	$results = $report->getEmployeeActivityTime($projectId, $activityId, "2007-07-01");
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(15));
    	$this->assertTrue($res);

		// Without date range
    	$results = $report->getEmployeeActivityTime($projectId, $activityId);
    	$res = $this->_verifyEmployeeTime($results, $projectId, $activityId, array(1), array(15));
    	$this->assertTrue($res);

    }

	/**
	 * test getEmployeeActivityTime() method
	 */
    public function testGetEmployeeActivityTimeWithMultipleActivities() {

    	$report = new ProjectReport();

    	// QA - Employee 1
		$this->_addEvent(1, 1, "QA", "2007-07-05  09:25", 70);
		$this->_addEvent(1, 1, "QA", "2007-07-07  08:12", 33); // 33 min
		$this->_addEvent(1, 1, "QA", "2007-07-10  09:25", 8 * 60 + 30); // 510 min

    	// QA - Employee 2
		$this->_addEvent(1, 2, "QA", "2007-07-05  09:25", 70); // 70 min
		$this->_addEvent(1, 2, "QA", "2007-07-12  11:25", 4 * 60 + 10); // 250 min
		$this->_addEvent(1, 2, "QA", "2007-07-11  06:25", 4 * 60 + 10); // 250 min

		// QA Events for different project
		$this->_addEvent(2, 1, "QA", "2007-07-05  07:25", 8 * 60 + 10); // 490 min
		$this->_addEvent(2, 2, "QA", "2007-07-05  09:25", 5 * 60); // 300 min

		// Programming - Employee 1
		$this->_addEvent(1, 1, "Programming", "2007-07-05  09:25", 80);
		$this->_addEvent(1, 1, "Programming", "2007-07-07  08:12", 70);
		$this->_addEvent(1, 1, "Programming", "2007-07-10  09:25", 210);

		// Programming - Employee 2
		$this->_addEvent(1, 2, "Programming", "2007-07-05  09:25", 230);
		$this->_addEvent(1, 2, "Programming", "2007-07-12  11:25", 450);
		$this->_addEvent(1, 2, "Programming", "2007-07-11  06:25", 900);

		// Programming for different project
		$this->_addEvent(2, 1, "Programming", "2007-07-05  05:25", 800);
		$this->_addEvent(2, 2, "Programming", "2007-07-12  15:25", 450);

		// Event spanning more than one day
		$this->_addEvent(1, 1, "Programming", "2007-07-19 18:00", 600);
		$this->_addEvent(1, 2, "Programming", "2007-07-18 21:25", 500);


		// Deleted activity for project 2
		$this->_addEvent(2, 1, "Deleted Activity", "2007-07-10  09:25", 400);
		$this->_addEvent(2, 2, "Deleted Activity", "2007-07-11  09:25", 300);

		//// Project 1 QA
		$activity = $this->activities[1]["QA"];
    	$activityId = $activity->getId();
    	$results = $report->getEmployeeActivityTime(1, $activityId, "2007-07-05", "2007-07-25");
    	$res = $this->_verifyEmployeeTime($results, 1, $activityId, array(1, 2), array(613, 570));
    	$this->assertTrue($res);

		// Without period
    	$results = $report->getEmployeeActivityTime(1, $activityId);
    	$res = $this->_verifyEmployeeTime($results, 1, $activityId, array(1, 2), array(613, 570));
    	$this->assertTrue($res);

		// Only from 07-10
    	$results = $report->getEmployeeActivityTime(1, $activityId, "2007-07-10", "2007-07-25");
    	$res = $this->_verifyEmployeeTime($results, 1, $activityId, array(1, 2), array(510, 500));
    	$this->assertTrue($res);

    	//// Project 1 Programming
		$activity = $this->activities[1]["Programming"];
    	$activityId = $activity->getId();
    	$results = $report->getEmployeeActivityTime(1, $activityId, "2007-07-05", "2007-07-25");
    	$res = $this->_verifyEmployeeTime($results, 1, $activityId, array(1, 2), array(960, 2080));
    	$this->assertTrue($res);

		// Without period
    	$results = $report->getEmployeeActivityTime(1, $activityId);
    	$res = $this->_verifyEmployeeTime($results, 1, $activityId, array(1, 2), array(960, 2080));
    	$this->assertTrue($res);

		// Only from 07-10
    	$results = $report->getEmployeeActivityTime(1, $activityId, "2007-07-10", "2007-07-25");
    	$res = $this->_verifyEmployeeTime($results, 1, $activityId, array(1, 2), array(810, 1850));
    	$this->assertTrue($res);

    	// On day with spanning events
    	$results = $report->getEmployeeActivityTime(1, $activityId, "2007-07-19", "2007-07-19");
    	$res = $this->_verifyEmployeeTime($results, 1, $activityId, array(1, 2), array(360, 345));
    	$this->assertTrue($res);

    	//// Project 1 Support
		$activity = $this->activities[1]["Support"];
    	$activityId = $activity->getId();
    	$results = $report->getEmployeeActivityTime(1, $activityId, "2007-07-05", "2007-07-25");
    	$this->assertTrue(is_array($results));
    	$this->assertEquals(0, count($results));

    	//// Project 2 Deleted Activity
		$activity = $this->activities[2]["Deleted Activity"];
    	$activityId = $activity->getId();
    	$results = $report->getEmployeeActivityTime(2, $activityId, "2007-07-10", "2007-07-11");
    	$res = $this->_verifyEmployeeTime($results, 2, $activityId, array(1, 2), array(400, 300));
    	$this->assertTrue($res);

		//// Project 2 QA
		$activity = $this->activities[2]["QA"];
    	$activityId = $activity->getId();
    	$results = $report->getEmployeeActivityTime(2, $activityId, "2007-07-05", "2007-07-25");
    	$res = $this->_verifyEmployeeTime($results, 2, $activityId, array(1, 2), array(490, 300));
    	$this->assertTrue($res);

		// Without period
    	$results = $report->getEmployeeActivityTime(2, $activityId);
    	$res = $this->_verifyEmployeeTime($results, 2, $activityId, array(1, 2), array(490, 300));
    	$this->assertTrue($res);

		// Only from 07-10
    	$results = $report->getEmployeeActivityTime(2, $activityId, "2007-07-10", "2007-07-25");
    	$this->assertTrue(is_array($results));
    	$this->assertEquals(0, count($results));

    	//// Project 2 Programming
		$activity = $this->activities[2]["Programming"];
    	$activityId = $activity->getId();
    	$results = $report->getEmployeeActivityTime(2, $activityId, "2007-07-05", "2007-07-25");
    	$res = $this->_verifyEmployeeTime($results, 2, $activityId, array(1, 2), array(800, 450));
    	$this->assertTrue($res);

		// Without period
    	$results = $report->getEmployeeActivityTime(2, $activityId);
    	$res = $this->_verifyEmployeeTime($results, 2, $activityId, array(1, 2), array(800, 450));
    	$this->assertTrue($res);

		// Only from 07-12
    	$results = $report->getEmployeeActivityTime(2, $activityId, "2007-07-12", "2007-07-25");
    	$res = $this->_verifyEmployeeTime($results, 2, $activityId, array(2), array(450));
    	$this->assertTrue($res);

    }

	/**
	 * test getEmployeeActivityTime() method
	 */
    public function testGetEmployeeActivityTimePaging() {

    	$report = new ProjectReport();

		// Add some extra employees
		for ($i=4; $i<30; $i++) {

			$empId = str_pad($i, 3, "0", STR_PAD_LEFT);
			$this->_addEmployee($i, $empId,  "Test Employee $i", "First Name $i");
		}

		$times = array();
		$empIds = array();

		// Add events for all employees
		for ($i=1; $i<30; $i++) {
			$this->_addEvent(1, $i, "Programming", "2007-07-05  05:50", $i * 10);
			$this->_addEvent(1, $i, "Programming", "2007-07-12  15:25", $i * 5);

			$times[$i] = $i * 10 + $i * 5;
		}

		$activity = $this->activities[1]["Programming"];
    	$activityId = $activity->getId();

		$sysConf = new sysConf();
		$pageSize = $sysConf->itemsPerPage;

		$timeValues = array_chunk($times, $pageSize);
		$empIds = array_chunk(range(1,29), $pageSize);

		for ($page = 0; $page < count($timeValues); $page++) {

			$results = $report->getEmployeeActivityTime(1, $activityId, "2007-07-05", "2007-07-12", $page + 1);
	    	$res = $this->_verifyEmployeeTime($results, 1, $activityId, $empIds[$page], $timeValues[$page]);
	    	$this->assertTrue($res);
		}
    }

	/**
	 * Test for countEmployeesInActivity() method.
	 */
	public function testCountEmployeesInActivity() {

		$report = new ProjectReport();
		$activity = $this->activities[1]["Programming"];
		$activityId = $activity->getId();
		$this->assertEquals(0, $report->countEmployeesInActivity(1, $activityId, "2007-07-05", "2007-07-12"));

		$this->_addEvent(1, 1, "Programming", "2007-07-05  09:25", 80);
		$this->_addEvent(1, 1, "Programming", "2007-07-07  08:12", 70);
		$this->_addEvent(1, 1, "Programming", "2007-07-10  09:25", 210);

		$this->assertEquals(1, $report->countEmployeesInActivity(1, $activityId, "2007-07-05", "2007-07-06"));
		$this->assertEquals(1, $report->countEmployeesInActivity(1, $activityId, "2007-07-05", "2007-07-12"));

		$this->_addEvent(1, 2, "Programming", "2007-07-25  09:25", 80);
		$this->assertEquals(1, $report->countEmployeesInActivity(1, $activityId, "2007-07-05", "2007-07-20"));
		$this->assertEquals(2, $report->countEmployeesInActivity(2, $activityId, "2007-07-05", "2007-07-30"));

		// Add some extra employees
		for ($i=1; $i<=30; $i++) {

			$id = $i + 3;
			$empId = str_pad($id, 3, "0", STR_PAD_LEFT);
			$this->_addEmployee($id, $empId,  "Test Employee $id", "First Name $id");
			$this->_addEvent(1, $id, "Programming", "2007-07-05  05:50", $id * 10);
			$this->_addEvent(1, $id, "Programming", "2007-07-12  15:25", $id * 5);
		}
		$this->assertEquals(32, $report->countEmployeesInActivity(2, $activityId, "2007-07-05", "2007-07-30"));
	}

	//-------------------------------------------------------------------------
	// Private methods and their tests
	//-------------------------------------------------------------------------

	/**
	 * Tests the _count method
	 */
	 public function testCount() {

		$this->_runQuery("TRUNCATE table hs_hr_project_activity");
	 	$this->assertEquals(0, $this->_count("hs_hr_project_activity"));
	 	$this->_runQuery("INSERT INTO hs_hr_project_activity(activity_id, project_id, name, deleted) " .
				        "VALUES(1, 1, 'Programming', 0)");
		$this->assertEquals(1, $this->_count("hs_hr_project_activity"));

	 	$this->_runQuery("INSERT INTO hs_hr_project_activity(activity_id, project_id, name, deleted) " .
				        "VALUES(2, 2, 'QA', 0)");
		$this->assertEquals(2, $this->_count("hs_hr_project_activity"));

	 	$this->_runQuery("INSERT INTO hs_hr_project_activity(activity_id, project_id, name, deleted) " .
				        "VALUES(3, 2, 'Meetings', 0)");
		$this->assertEquals(3, $this->_count("hs_hr_project_activity"));

		$this->assertEquals(1, $this->_count("hs_hr_project_activity", "activity_id = 1"));
		$this->assertEquals(2, $this->_count("hs_hr_project_activity", "project_id = 2"));
		$this->assertEquals(1, $this->_count("hs_hr_project_activity", "name = 'Meetings'"));

		$this->assertEquals(0, $this->_count("hs_hr_project_activity", "name = 'Test'"));
	 }


	/**
	 * Tests the _addCustomer private method.
	 */
	public function testAddCustomer() {

		$this->assertEquals(1, $this->_count("hs_hr_customer"));
		$this->customerId = 2;
		$this->customerName = "Qwerty";
		$this->_addCustomer();
		$this->assertEquals(2, $this->_count("hs_hr_customer"));
		$this->assertEquals(1, $this->_count("hs_hr_customer", "name = '{$this->customerName}'"));
	}

	/**
	 * Tests the _addProjects method
	 */
	public function testAddProjects() {

		$this->assertEquals(2, $this->_count("hs_hr_project"));
		$this->projects = array( 3 => "Project X", 4 => "Project Y");
		$this->_addProjects();
		$this->assertEquals(4, $this->_count("hs_hr_project"));
		$this->assertEquals(1, $this->_count("hs_hr_project", "name = 'Project X'"));
		$this->assertEquals(1, $this->_count("hs_hr_project", "name = 'Project Y'"));
	}

	/**
	 * Tests the _addActivity method
	 */
	public function testAddActivity() {
		$this->activities = null;
		$count = $this->_count("hs_hr_project_activity");

		$this->_addActivity(1, "Test1");
		$this->assertEquals(1, $this->_count("hs_hr_project_activity", "name = 'Test1' and deleted = 0"));
		$this->assertTrue(isset($this->activities[1]));
		$this->assertTrue(isset($this->activities[1]['Test1']));

		$this->_addActivity(1, "Test2", true);
		$this->assertEquals(1, $this->_count("hs_hr_project_activity", "name = 'Test2' and deleted = 1"));
		$this->assertTrue(isset($this->activities[1]['Test2']));

		$this->assertEquals($count + 2, $this->_count("hs_hr_project_activity"));
	}

	/**
	 * Tests the _addEmployee method
	 */
	public function testAddEmployee() {
		$count = $this->_count("hs_hr_employee");
		$this->_addEmployee(10, "0010", "Ranasinghe", "Ruwan");
		$this->assertEquals($count + 1, $this->_count("hs_hr_employee"));
		$this->assertEquals(1, $this->_count("hs_hr_employee", "emp_number = 10"));
	}

	/**
	 * Tests the _addEvent method
	 */
	public function testAddEvent() {

		$this->assertEquals(0, $this->_count("hs_hr_time_event"));
		$this->_addEvent(1, 1, "Programming", "2006-12-02 10:20", 19);
		$this->assertEquals(1, $this->_count("hs_hr_time_event"));
		$activity = $this->activities[1]['Programming'];
		$activityId = $activity->getId();
		$this->assertEquals(1, $this->_count("hs_hr_time_event", "activity_id = $activityId"));
		$this->assertEquals(1, $this->_count("hs_hr_time_event", "start_time BETWEEN '2006-12-02 10:20' AND '2006-12-02 10:39'"));
	}

	public function testGetNextId() {
		$this->assertEquals(2, $this->_getNextId("hs_hr_customer", "customer_id"));

		$this->customerId = 5;
		$this->customerName = "Qwerty";
		$this->_addCustomer();
		$this->assertEquals(6, $this->_getNextId("hs_hr_customer", "customer_id"));

	}

	/**
	 * Test the CalculateDuration method
	 */
	public function testCalculateDuration() {
		$this->assertEquals(3600, $this->_calculateDuration("2007-01-01 10:45", "2007-01-01 11:45"));
		$this->assertEquals(60, $this->_calculateDuration("2007-06-01 10:45", "2007-06-01 10:46"));
	}

	public function testGetEndTime() {
		$this->assertEquals("2007-01-01 11:45", $this->_getEndTime("2007-01-01 10:45", 3600));
		$this->assertEquals("2007-06-01 10:46", $this->_getEndTime("2007-06-01 10:45", 60));
	}

	/**
	 * Tests the _getTimesheetId method
	 */
	public function testGetTimesheetId() {

		// 2007-5-12 -> prev monday = 14, next sunday = 20
		$this->assertEquals(0, $this->_count("hs_hr_timesheet"));
		$id = $this->_getTimesheetId(1, "2007-5-15 10:10", "2007-5-15 11:20");
		$this->assertEquals(1, $this->_count("hs_hr_timesheet"));
		$this->assertEquals(1, $this->_count("hs_hr_timesheet", "DATE(start_date) = DATE('2007-05-14') AND DATE(end_date) = DATE('2007-05-20')"));

		// Add another timesheet for same period
		$id2 = $this->_getTimesheetId(1, "2007-5-16 10:00", "2007-5-16 11:20");

		$this->assertEquals(1, $this->_count("hs_hr_timesheet"));
		$this->assertEquals($id, $id2);

		// Add timesheet for different period
		$id3 = $this->_getTimesheetId(1, "2007-5-23 10:00", "2007-5-23 11:20");
		$this->assertEquals(2, $this->_count("hs_hr_timesheet"));
		$this->assertNotEquals($id, $id3);
	}

	/**
	 * Tests the _verifyActivityTime method
	 */
	public function testVerifyActivityTime() {

		$results[] = new ProjectActivityTime(1, "Programming", 3600, 1);
		$this->assertTrue($this->_verifyActivityTime($results, 1, array("Programming"), array(3600/60)));

		// Wrong name
		$this->assertFalse($this->_verifyActivityTime($results, 1, array("QA"), array(3600/60)));

		// Wrong time
		$this->assertFalse($this->_verifyActivityTime($results, 1, array("Programming"), array(120)));

		// Wrong project id
		$this->assertTrue($this->_verifyActivityTime($results, 2, array("Programming"), array(3600/60)));

		$results = array();
		$results[] = new ProjectActivityTime(1, "Programming", 4800, 1);
		$results[] = new ProjectActivityTime(2, "QA", 2400, 1);
		$results[] = new ProjectActivityTime(3, "Support", 1800, 1);

		$this->assertTrue($this->_verifyActivityTime($results, 1, array("QA", "Programming", "Support"),
                          array(2400/60, 4800/60, 1800/60)));

		// One time incorrect
		$this->assertFalse($this->_verifyActivityTime($results, 1, array("QA", "Programming", "Support"),
                          array(2400/60, 4800/60, 2400/60)));

		// All results not given
		$this->assertFalse($this->_verifyActivityTime($results, 1, array("QA", "Programming"),
                          array(2400/60, 4800/60)));

		// Wrong project Id
		$this->assertFalse($this->_verifyActivityTime($results, 2, array("QA", "Programming", "Support"),
                          array(2400/60, 4800/60, 2400/60)));

		// One activity wrong
		$this->assertFalse($this->_verifyActivityTime($results, 1, array("QA", "Program", "Support"),
                          array(2400/60, 4800/60, 2400/60)));
		$this->assertFalse($this->_verifyActivityTime($results, 1, array("QA", "Misc", "Support"),
                          array(2400/60, 4800/60, 2400/60)));

		// Extra activity given
		$this->assertFalse($this->_verifyActivityTime($results, 1, array("QA", "Programming", "Support", "Misc"),
                           array(2400/60, 4800/60, 1800/60, 1800/60)));

	}

	/**
	 * Tests the _verifyEmployeeTime method
	 */
	public function testVerifyEmployeeTime() {

		$activity = $this->activities[1]["QA"];
    	$activityId = $activity->getId();

		$results = array(new EmployeeActivityTime(1, "John", "Rajasinghe", $activityId, "QA", 250 * 60));

		$this->assertTrue($this->_verifyEmployeeTime($results, 1, $activityId, array(1), array(250)));

		// Wrong employee
		$this->assertFalse($this->_verifyEmployeeTime($results, 3, $activityId + 1, array(1), array(250)));

		// Wrong activity
		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId + 1, array(1), array(250)));

		// Wrong time
		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId, array(1), array(252)));

		// Activity ID and name not matching in results
		$results = array(new EmployeeActivityTime(1, "John", "Rajasinghe", $activityId, "Programming", 250 * 60));
		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId , array(1), array(250)));

		// EmployeeId and first name does not match in results
		$results = array(new EmployeeActivityTime(1, "Ravi", "Rajasinghe", $activityId, "QA", 250 * 60));
		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId , array(1), array(250)));

		// EmployeeId and last name does not match in results
		$results = array(new EmployeeActivityTime(1, "John", "Rajapaksha", $activityId, "QA", 250 * 60));
		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId , array(1), array(250)));

		$activity = $this->activities[1]["Programming"];
    	$activityId = $activity->getId();
		$results = array();
		$results[] = new EmployeeActivityTime(1, "John", "Rajasinghe", $activityId, "Programming", 480 * 60);
		$results[] = new EmployeeActivityTime(2, "Ravi", "Wickramasinghe", $activityId, "Programming", 400 * 60);
		$results[] = new EmployeeActivityTime(3, "Rasanga", "Kumarasinghe", $activityId, "Programming", 800 * 60);

		$this->assertTrue($this->_verifyEmployeeTime($results, 1, $activityId, array(1,2,3), array(480, 400, 800)));

		// One time incorrect
		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId, array(1,2,3), array(480, 400, 801)));

		// Extra employee in results
		$this->_addEmployee(4, "004",  "Sam", "Samarasinghe");
		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId, array(1,2,3,4), array(480, 400, 800, 200)));

		// Wrong activity id
		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId + 1, array(1,2,3), array(480, 400, 800)));

		// One employee missing
		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId, array(1,2), array(480, 400)));

		// One activity wrong
		$results = array();
		$results[] = new EmployeeActivityTime(1, "John", "Rajasinghe", $activityId, "Programming", 480 * 60);
		$results[] = new EmployeeActivityTime(2, "Ravi", "Wickramasinghe", $activityId, "QA", 400 * 60);
		$results[] = new EmployeeActivityTime(3, "Rasanga", "Kumarasinghe", $activityId, "Programming", 800 * 60);

		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId, array(1,2,3), array(480, 400, 800)));

		// Wrong name for one employee in results
		$results = array();
		$results[] = new EmployeeActivityTime(1, "John", "Rajasinghe", $activityId, "Programming", 480 * 60);
		$results[] = new EmployeeActivityTime(2, "Ravi", "Ranasinghe", $activityId, "QA", 400 * 60);
		$results[] = new EmployeeActivityTime(3, "Rasanga", "Kumarasinghe", $activityId, "Programming", 800 * 60);

		$this->assertFalse($this->_verifyEmployeeTime($results, 1, $activityId, array(1,2,3), array(480, 400, 800)));
	}

	/**
	 * Add customer used by unit test
	 */
	private function _addCustomer() {
		$template = "INSERT INTO hs_hr_customer(customer_id, name, description, deleted) VALUES(%d, '%s', '%s', 0)";
		$sql = sprintf($template, $this->customerId, $this->customerName, "Desc. " . $this->customerName);
		$this->_runQuery($sql);
	}

	/**
	 * Add projects used by unit test.
	 */
	private function _addProjects() {

		$template = "INSERT INTO hs_hr_project(project_id, customer_id, name, description, deleted) " .
				    "VALUES(%d, %d, '%s', '%s', 0)";

		$id = $this->_count("hs_hr_project") + 1;
		foreach ($this->projects as $project) {
			$sql = sprintf($template, $id, $this->customerId, $project, "Desc. " . $project);
			$this->_runQuery($sql);
			$id++;
		}
	}

	/**
	 * Add an activity to the database and saves it in the $this->activities
	 * array
	 *
	 * @param int    $projectId The project Id
	 * @param string $name      The Activity name
	 * @param bool   $deleted   Create activity in deleted state
	 *
	 * @return ProjectActivity The activity object that was created.
	 */
	private function _addActivity($projectId, $name, $deleted = false) {
		$activity = new ProjectActivity();
		$activity->setName($name);
		$activity->setProjectId($projectId);
		$activity->setDeleted($deleted);
		$activity->save();

		$this->activities[$projectId][$name] = $activity;
		return $activity;
	}

	/**
	 * Add employee
	 */
	 private function _addEmployee($empNumber, $empId, $lastName, $firstName) {
	 	$sql = sprintf("INSERT INTO hs_hr_employee(emp_number, employee_id, emp_lastname, emp_firstname) " .
        			"VALUES(%d, '%s', '%s', '%s')", $empNumber, $empId, $lastName, $firstName);
        $this->_runQuery($sql);
	 }

	/**
	 * Insert event into database.
	 */
    private function _addEvent($projectId, $employeeId, $activityName, $startTime, $duration) {

		// Get activity Id
		$this->assertTrue(isset($this->activities[$projectId]));
		$this->assertTrue(isset($this->activities[$projectId][$activityName]));
		$activity = $this->activities[$projectId][$activityName];

		$timeEventId = $this->_getNextId("hs_hr_time_event", "time_event_id");
		$durationInSec = $duration * 60;
		$endTime = $this->_getEndTime($startTime, $durationInSec);
		$activityId = $activity->getId();
		$timesheetId = $this->_getTimesheetId($employeeId, $startTime, $endTime);
		$reportedDate = date("Y-m-d");
		$description = "Desc $activityName";

		$sql = sprintf("INSERT INTO hs_hr_time_event(time_event_id, project_id, activity_id, employee_id, timesheet_id, " .
                       "start_time, end_time, reported_date, duration, description) " .
                       "VALUES(%d, %d, %d, %d, %d, '%s', '%s', '%s', %d, '%s')",
                       $timeEventId, $projectId, $activityId, $employeeId, $timesheetId, $startTime, $endTime,
                       $reportedDate, $durationInSec, "activity $activityName");
        $this->_runQuery($sql);
    }

	/**
	 * Calculate the duration between the given dates in seconds
	 *
	 * @param string start date
	 * @param string end date
	 *
	 * @return int duration in seconds
	 */
    private function _calculateDuration($startDate, $endDate) {
    	$start = strtotime($startDate);
    	$end = strtotime($endDate);
    	$duration = $end - $start;
    	return $duration;
    }

    /**
     * Calculates the end time
     * @param string $startTime The start time
     * @param int    $duration  The duration (in seconds)
     * @return string The end time.
     */
    private function _getEndTime($startTime, $duration) {
    	$start = strtotime($startTime);
    	$end = $start + $duration;
    	$endTime = date('Y-m-d H:i', $end);
    	return $endTime;
    }

	/**
	 * Gets the timesheet id for the given employee for the given period.
	 * If no timesheet is present, creates one
	 *
	 * @param int    $employeeId   Employee id
	 * @param string $startTime    Start time
	 * @param string $endTime      End time
	 *
	 * @return int   Timesheet id
	 */
	private function _getTimesheetId($employeeId, $startTime, $endTime) {
		$start = strtotime($startTime);
		$periodStart = strtotime("last Monday", $start);
		$periodStartDay = date('Y-m-d', $periodStart);

		$id = null;
		$sql = "SELECT timesheet_id FROM hs_hr_timesheet WHERE employee_id = $employeeId AND " .
				" DATE(start_date) = DATE('$periodStartDay')";
		$result = $this->_runQuery($sql);

		if (mysql_num_rows($result) > 0) {
			$row = mysql_fetch_array($result, MYSQL_NUM);
        	$id = $row[0];
		} else {

			$periodEndDay = date('Y-m-d', strtotime("next Sunday", $start));
			$id = $this->_getNextId("hs_hr_timesheet", "timesheet_id");
			$sql = sprintf("INSERT INTO hs_hr_timesheet(timesheet_id, employee_id, timesheet_period_id, start_date, " .
					"end_date, status, comment) VALUES(%d, %d, %d, '%s', '%s', %d, '%s')",
					$id, $employeeId, 1, $periodStartDay, $periodEndDay, Timesheet::TIMESHEET_STATUS_NOT_SUBMITTED,
					"comment");

			$this->_runQuery($sql);
		}

		return $id;
	}

	/**
	 * Verifies that the results contain the given activities and times
	 *
	 * @param array  $results    Array ProjectActivityTime objects
	 * @param string $projectId  project id
	 * @param array  $activities array of activity names expected
	 * @param array  $times      array of times values expected (in minutes)
	 *
	 * @return bool false if verification failed, true otherwise
	 */
	private function _verifyActivityTime($results, $projectId, $activities, $times) {

		// Verify number
		if (count($results) != count($activities)) {
			return false;
		}

		foreach ($results as $activityTime) {

			// find in activities.
			$activityName = $activityTime->getActivityName();
			$index = array_search($activityName, $activities);

			// if not found, return false
			if (($index === false) || ($index === null)) {
				return false;
			}

			// if found, check time
			$time = $times[$index];

			// if time matches, remove from activities
			if ($activityTime->getActivityTime() == ($time * 60)) {
				unset($activities[$index]);
				unset($times[$index]);
			} else {
				return false;
			}
		}

		// see if any activities left, if so return false
		if ((count($activities) != 0) || (count($times) != 0)) {
			return false;
		}
		return true;
	}

	/**
	 * Verifies the results contain the given employees and times
	 *
	 * @param array  $results    The results array
	 * @param string $projectId  The project Id
	 * @param string $activityId Activity Id
	 * @param array  $empIds     Employee Ids
	 * @param array  $times      Activity times
	 *
	 * @return true if results match or false if not
	 */
	private function _verifyEmployeeTime($results, $projectId, $activityId, $empIds, $times) {

		// Verify number
		if (count($results) != count($empIds)) {
			return false;
		}

		foreach ($results as $empActivityTime) {

			// find emp number in list
			$empNumber = $empActivityTime->getEmpNumber();
			$index = array_search($empNumber, $empIds);

			// if not found, return false
			if (($index === false) || ($index === null)) {
				return false;
			}

			// check activity id
			if ($activityId != $empActivityTime->getActivityId()) {
				return false;
			}

			// check activity name match
			$activityName = $empActivityTime->getActivityName();
			if (!isset($this->projects[$projectId][$activityName])) {
				return false;
			}

			$activity = $this->activities[$projectId][$activityName];
			if ($activity->getId() != $activityId) {
				return false;
			}

			// Check employee details
			$empCountWhere = sprintf("emp_number=%d AND emp_firstname='%s' AND emp_lastname='%s'",
                                     $empActivityTime->getEmpNumber(), $empActivityTime->getFirstName(),
                                     $empActivityTime->getLastName());


			if ($this->_count("hs_hr_employee", $empCountWhere) != 1) {
				return false;
			}

			// Check time
			$time = $times[$index];

			if ($empActivityTime->getActivityTime() == ($time * 60)) {

				unset($empIds[$index]);
				unset($times[$index]);
			} else {
				return false;
			}
		}

		// see if any activities left, if so return false
		if ((count($empIds) != 0) || (count($times) != 0)) {
			return false;
		}

		return true;
	}

	/**
	 * Truncates tables used in test.
	 */
    private function _truncateTables() {

        $this->_runQuery("TRUNCATE TABLE `hs_hr_project_activity`");
        $this->_runQuery("TRUNCATE TABLE `hs_hr_time_event`");
		$this->_runQuery("TRUNCATE TABLE `hs_hr_project`");
		$this->_runQuery("TRUNCATE TABLE `hs_hr_customer`");
        $this->_runQuery("TRUNCATE TABLE `hs_hr_timesheet`");
        $this->_runQuery("TRUNCATE TABLE `hs_hr_employee`");
    }

    /**
     * Counts rows in the given table with optional where clause
     *
     * @param  string $table The table name
     * @param  string $where The where clause
     * @return int    number of rows
     */
    private function _count($table, $where = null) {

    	$sql = "SELECT COUNT(*) FROM " . $table;
    	if (!empty($where)) {
    		$sql .= " WHERE " . $where;
    	}
		$result = $this->_runQuery($sql);
		$row = mysql_fetch_array($result, MYSQL_NUM);
        $count = $row[0];
		return $count;
    }

	/**
	 * Return the next Id for the given field in
	 * the given table
	 *
	 * @param string $table Table name
	 * @param string $field Field name
	 * @return int Next Id
	 */
	private function _getNextId($table, $field) {
		$sql = "SELECT MAX($field) FROM $table";
		$result = $this->_runQuery($sql);
		$row = mysql_fetch_array($result, MYSQL_NUM);
        $nextId = $row[0] + 1;
		return $nextId;
	}

    /**
     * Runs the given sql query verifies and returns the
     * result.
     */
    private function _runQuery($sql) {
    	$result = mysql_query($sql, $this->connection);
    	$this->assertTrue($result !== false, mysql_error());
    	return $result;
    }
}

// Call ProjectReportTest::main() if this source file is executed directly.
if (PHPUnit_MAIN_METHOD == "ProjectReportTest::main") {
    ProjectReportTest::main();
}
?>

