/**
 * @file lldate_test.cpp
 * @author Adroit
 * @date 2007-02
 * @brief LLDate test cases.
 *
 * $LicenseInfo:firstyear=2007&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"

#include "../llstring.h"
#include "../lldate.h"

#include "../test/lltut.h"


#define VALID_DATE									"2003-04-30T04:00:00Z"
#define VALID_DATE_LEAP								"2004-02-29T04:00:00Z"
#define VALID_DATE_HOUR_BOUNDARY					"2003-04-30T23:59:59Z"
#define VALID_DATE_FRACTIONAL_SECS					"2007-09-26T20:31:33.70Z"

// invalid format
#define INVALID_DATE_MISSING_YEAR					"-04-30T22:59:59Z"
#define INVALID_DATE_MISSING_MONTH					"1900-0430T22:59:59Z"
#define INVALID_DATE_MISSING_DATE					"1900-0430-T22:59:59Z"
#define INVALID_DATE_MISSING_T						"1900-04-30-22:59:59Z"
#define INVALID_DATE_MISSING_HOUR					"1900-04-30T:59:59Z"
#define INVALID_DATE_MISSING_MIN					"1900-04-30T01::59Z"
#define INVALID_DATE_MISSING_SEC					"1900-04-30T01:59Z"
#define INVALID_DATE_MISSING_Z						"1900-04-30T01:59:23"
#define INVALID_DATE_EMPTY							""

// invalid values
// apr 1.1.1 seems to not care about constraining the date to valid
// dates. Put these back when the parser checks.
#define LL_DATE_PARSER_CHECKS_BOUNDARY 0
//#define INVALID_DATE_24HOUR_BOUNDARY				"2003-04-30T24:00:00Z"
//#define INVALID_DATE_LEAP							"2003-04-29T04:00:00Z"
//#define INVALID_DATE_HOUR							"2003-04-30T24:59:59Z"
//#define INVALID_DATE_MIN							"2003-04-30T22:69:59Z"
//#define INVALID_DATE_SEC							"2003-04-30T22:59:69Z"
//#define INVALID_DATE_YEAR							"0-04-30T22:59:59Z"
//#define INVALID_DATE_MONTH							"2003-13-30T22:59:59Z"
//#define INVALID_DATE_DAY							"2003-04-35T22:59:59Z"

namespace tut
{
	struct date_test
	{

	};
	typedef test_group<date_test> date_test_t;
	typedef date_test_t::object date_test_object_t;
	tut::date_test_t tut_date_test("LLDate");

	/* format validation */
	template<> template<>
	void date_test_object_t::test<1>()
	{
		LLDate date(VALID_DATE);
		std::string  expected_string;
		bool result;
		expected_string = VALID_DATE;
		ensure_equals("Valid Date failed" , expected_string, date.asString());

		result = date.fromString(VALID_DATE_LEAP);
		expected_string = VALID_DATE_LEAP; 	
		ensure_equals("VALID_DATE_LEAP failed" , expected_string, date.asString());

		result = date.fromString(VALID_DATE_HOUR_BOUNDARY);
		expected_string = VALID_DATE_HOUR_BOUNDARY; 	
		ensure_equals("VALID_DATE_HOUR_BOUNDARY failed" , expected_string, date.asString());

		result = date.fromString(VALID_DATE_FRACTIONAL_SECS);
		expected_string = VALID_DATE_FRACTIONAL_SECS;
		ensure_equals("VALID_DATE_FRACTIONAL_SECS failed" , expected_string, date.asString());

		result = date.fromString(INVALID_DATE_MISSING_YEAR);
		ensure_equals("INVALID_DATE_MISSING_YEAR should have failed" , result, false);

		result = date.fromString(INVALID_DATE_MISSING_MONTH);
		ensure_equals("INVALID_DATE_MISSING_MONTH should have failed" , result, false);

		result = date.fromString(INVALID_DATE_MISSING_DATE);
		ensure_equals("INVALID_DATE_MISSING_DATE should have failed" , result, false);

		result = date.fromString(INVALID_DATE_MISSING_T);
		ensure_equals("INVALID_DATE_MISSING_T should have failed" , result, false);

		result = date.fromString(INVALID_DATE_MISSING_HOUR);
		ensure_equals("INVALID_DATE_MISSING_HOUR should have failed" , result, false);

		result = date.fromString(INVALID_DATE_MISSING_MIN);
		ensure_equals("INVALID_DATE_MISSING_MIN should have failed" , result, false);

		result = date.fromString(INVALID_DATE_MISSING_SEC);
		ensure_equals("INVALID_DATE_MISSING_SEC should have failed" , result, false);

		result = date.fromString(INVALID_DATE_MISSING_Z);
		ensure_equals("INVALID_DATE_MISSING_Z should have failed" , result, false);

		result = date.fromString(INVALID_DATE_EMPTY);
		ensure_equals("INVALID_DATE_EMPTY should have failed" , result, false);
	}

	/* Invalid Value Handling */
	template<> template<>
	void date_test_object_t::test<2>()
	{
#if LL_DATE_PARSER_CHECKS_BOUNDARY
		LLDate date;
		std::string  expected_string;
		bool result;

		result = date.fromString(INVALID_DATE_24HOUR_BOUNDARY);
		ensure_equals("INVALID_DATE_24HOUR_BOUNDARY should have failed" , result, false);
		ensure_equals("INVALID_DATE_24HOUR_BOUNDARY date still set to old value on failure!" , date.secondsSinceEpoch(), 0);

		result = date.fromString(INVALID_DATE_LEAP);
		ensure_equals("INVALID_DATE_LEAP should have failed" , result, false);

		result = date.fromString(INVALID_DATE_HOUR);
		ensure_equals("INVALID_DATE_HOUR should have failed" , result, false);

		result = date.fromString(INVALID_DATE_MIN);
		ensure_equals("INVALID_DATE_MIN should have failed" , result, false);

		result = date.fromString(INVALID_DATE_SEC);
		ensure_equals("INVALID_DATE_SEC should have failed" , result, false);

		result = date.fromString(INVALID_DATE_YEAR);
		ensure_equals("INVALID_DATE_YEAR should have failed" , result, false);

		result = date.fromString(INVALID_DATE_MONTH);
		ensure_equals("INVALID_DATE_MONTH should have failed" , result, false);

		result = date.fromString(INVALID_DATE_DAY);
		ensure_equals("INVALID_DATE_DAY should have failed" , result, false);
#endif
	}

	/* API checks */
	template<> template<>
	void date_test_object_t::test<3>()
	{
		LLDate date;
		std::istringstream stream(VALID_DATE);
		std::string  expected_string = VALID_DATE;
		date.fromStream(stream);
		ensure_equals("fromStream failed", date.asString(), expected_string);
	}

	template<> template<>
	void date_test_object_t::test<4>()
	{
		LLDate date1(VALID_DATE);
		LLDate date2(date1);
		ensure_equals("LLDate(const LLDate& date) constructor failed", date1.asString(), date2.asString());
	}

	template<> template<>
	void date_test_object_t::test<5>()
	{
		LLDate date1(VALID_DATE);
		LLDate date2(date1.secondsSinceEpoch());
		ensure_equals("secondsSinceEpoch not equal",date1.secondsSinceEpoch(), date2.secondsSinceEpoch());
		ensure_equals("LLDate created using secondsSinceEpoch not equal", date1.asString(), date2.asString());
	}

	template<> template<>
	void date_test_object_t::test<6>()
	{
		LLDate date(VALID_DATE);
		std::ostringstream stream;
		stream << date;
		std::string expected_str = VALID_DATE;
		ensure_equals("ostringstream failed", expected_str, stream.str());
	}

	template<> template<>
	void date_test_object_t::test<7>()
	{
		LLDate date;
		std::istringstream stream(VALID_DATE);
		stream >> date;
		std::string expected_str = VALID_DATE;
		std::ostringstream out_stream;
        out_stream << date;

		ensure_equals("<< failed", date.asString(),expected_str);
		ensure_equals("<< to >> failed", stream.str(),out_stream.str());		
	}
}
