char lbl[] = "0123456789";

//#define CHECK_FATAL_RANGE_EXCEPTION
//Don't modify 3 lines upper from this - they are used in file read test
//If you plan to check range exception, you may uncomment it - 
//anyway file test should not happen

/*
 *	PROGRAM:	Class library integrity tests
 *	MODULE:		string_test.cpp
 *	DESCRIPTION:	test class Firebird::string
 *
 *  The contents of this file are subject to the Initial
 *  Developer's Public License Version 1.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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
 *
 *  Software distributed under the License is distributed AS IS,
 *  WITHOUT WARRANTY OF ANY KIND, either express or implied.
 *  See the License for the specific language governing rights
 *  and limitations under the License.
 *
 *  The Original Code was created by Alexander Peshkoff
 *  for the Firebird Open Source RDBMS project.
 *
 *  Copyright (c) 2004 Alexander Peshkoff <peshkoff@mail.ru>
 *  and all contributors signed below.
 *
 *  All Rights Reserved.
 *  Contributor(s): ______________________________________.
 */

#if defined(FIRESTR) && defined(DEV_BUILD)
#define FULL_FIRE
#endif

#ifdef FIRESTR
#define NAME "Firebird::string"
#include "../common/classes/fb_string.h"

using namespace Firebird;
#else
#define NAME "std::basic_string"
#include <string>
typedef std::basic_string<char> string;
typedef string AbstractString;
#endif

#include <time.h>

#ifdef DEV_BUILD
void CheckStr(const AbstractString& check, const char* want, int line) {
	if (strlen(check.c_str()) != check.length()) {
		printf("Length error at %d\n\n", line);
	}
	if (strcmp(check.c_str(), want)) {
		printf("Wanted >%s<,\n   got >%s< at %d\n\n", want, check.c_str(), line);
	}
}
#define validate(a, b) CheckStr(a, b, __LINE__)
#else
#define validate(a, b)
#define check(a, b)
#endif

void test(void) {
{
	string a;
	validate(a, "");
	a = lbl;
	string b = a;
	validate(b, lbl);
	string f = "0123456789";
	validate(f, lbl);
	string g("0123456789", 5);
	validate(g, "01234");
	string h(5, '7');
	validate(h, "77777");
#ifdef FULL_FIRE
	string i('7');
	validate(i, "7");
#endif
	string j(&lbl[3], &lbl[5]);
	validate(j, "34");
}
{
	string a = lbl;
	string b;
	b = a;
	validate(b, lbl);
	b = lbl;
	validate(b, lbl);
	a = 'X';
	validate(a, "X");

	a = b;
	for (string::iterator x = b.begin(); x < b.end(); x++)
		*x = 'u';
	validate(a, lbl);
	validate(b, "uuuuuuuuuu");

	char y[20], *z = y;
	const string c = a;
	for (string::const_iterator x1 = c.begin(); x1 < c.end(); x1++)
		*z++ = *x1;
	*z = 0;
	b = y;
	validate(b, lbl);
}
#ifdef FULL_FIRE
{
	const string a = lbl;
	string b = a.at(5);
	validate(b, "5");
}
{
	string a = lbl;
	string b = a.at(5);
	validate(b, "5");
}
#endif
// conflict with J. requirement to string class - operator const char* {
//	string a = lbl;
//	char c = a[5];
//	a[5] = 'u';
//	validate(a, "01234u6789");
//}
{
	string a = lbl;
	char c = a[5];  // via operator const char*
	a.at(5) = 'u';
	a.at(7) = c;
	validate(a, "01234u6589");
}
#ifdef CHECK_FATAL_RANGE_EXCEPTION
{
	const string a = lbl;
	string b = a.at(15);
}
#endif
{
	string a = lbl;
	a.resize(15, 'u');
	validate(a, "0123456789uuuuu");
}
{
	string a;
	string x = lbl;
	x += lbl;
	x += lbl;
	x += lbl;
	x += lbl;
	x += lbl;
	x += lbl;
	x += lbl;
	x += lbl;
	x += lbl;
	x += lbl;
	x += lbl; //120 bytes
	a = x;
	validate(a, x.c_str());

	x = lbl;
	x += lbl;
	x += lbl;
	x += lbl; //40 bytes
	a = x;
	validate(a, x.c_str());
}
{
	string a = lbl;
	const string b = a;
	a += b;
	validate(a, "01234567890123456789");

	a = lbl;
	a += lbl;
	validate(a, "01234567890123456789");

	a = lbl;
	a += 'u';
	validate(a, "0123456789u");
}
{
	string a, b, c;
	
	a = "uuu";
	b = lbl;
	c = a + b;
	validate(c, "uuu0123456789");

	c = a + lbl;
	validate(c, "uuu0123456789");

	c = b + 'u';
	validate(c, "0123456789u");

	c = lbl + a;
	validate(c, "0123456789uuu");

	c = 'u' + b;
	validate(c, "u0123456789");

	validate(a, "uuu");
	validate(b, lbl);
}
{
	string a = lbl;
	const string b = a;
	a.append(b);
	validate(a, "01234567890123456789");

	a = lbl;
	a.append(lbl);
	validate(a, "01234567890123456789");

	a = lbl;
	a.append(lbl, 6);
	validate(a, "0123456789012345");

	a = lbl;
	a.append(b, 3, 2);
	validate(a, "012345678934");

	a = lbl;
	a.append(b, 3, 20);
	validate(a, "01234567893456789");

	a = lbl;
	a.append(3, 'u');
	validate(a, "0123456789uuu");

	a = lbl;
	a.append(b.begin(), b.end());
	validate(a, "01234567890123456789");

	a = lbl;
	a.append("Something reaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long");
	validate(a, "0123456789Something reaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long");
	a = lbl;
	validate(a, lbl);

	string c = lbl;
	c += lbl;
	c += lbl;
	c += lbl;
	a = lbl;
	a.append("Something reaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long");
	validate(a, "0123456789Something reaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long");
	a = c;
	validate(a, c.c_str());
}
{
	string a, b;
	b = lbl;
	
	a.assign(3, 'u');
	validate(a, "uuu");

	a.assign(lbl);
	validate(a, lbl);
	
	a.assign(lbl, 2);
	validate(a, "01");
	
	a.assign(b, 3, 3);
	validate(a, "345");
	
	a.assign(b);
	validate(a, lbl);

	a = "";
	validate(a, "");
	string::iterator x = b.begin();
	string::iterator y = b.end();
	a.assign(x, y);
	validate(a, lbl);
}
{
	string a, b = lbl;

	a = lbl;
	a.insert(5, 3, 'u');
	validate(a, "01234uuu56789");

	a = lbl;
	a.insert(3, lbl);
	validate(a, "01201234567893456789");
	
	a = lbl;
	a.insert(4, lbl, 2);
	validate(a, "012301456789");
	
	a = lbl;
	a.insert(5, b, 3, 3);
	validate(a, "0123434556789");
	
	a = lbl;
	a.insert(5, b);
	validate(a, "01234012345678956789");

	a = lbl;
	a.insert(2, "Something reaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long");
	validate(a, "01Something reaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long23456789");

	a = lbl;
	string::iterator x = a.begin();
	x++;
	a.insert(x, 5, 'u');
	validate(a, "0uuuuu123456789");

	a = lbl;
	x = a.begin();
	x += 2;
	string::iterator f = b.begin(); 
	string::iterator t = b.end(); 
	f++; t--;
	a.insert(x, f, t);
	validate(a, "011234567823456789");

	a = lbl;
	a.erase();
	validate(a, "");

	a = lbl;
	a.erase(3, 6);
	validate(a, "0129");

	a = lbl;
	a.erase(3, 16);
	validate(a, "012");

	a = lbl;
	a.erase(3);
	validate(a, "012");

	a = lbl;
	x = a.begin();
	x += 3;
	a.erase(x);
	validate(a, "012456789");

	a = lbl;
	x = a.begin();
	x += 3;
	string::iterator y = a.end();
	y -= 2;
	a.erase(x, y);
	validate(a, "01289");
}
{
	string a;
	const string b = lbl;
	string::iterator f0, t0;
	string::const_iterator f, t;

	a = lbl;
	a.replace(5, 2, 3, 'u');
	validate(a, "01234uuu789");

	a = lbl;
	f0 = a.begin() + 5;
	t0 = f0 + 2;
	a.replace(f0, t0, 3, 'u');
	validate(a, "01234uuu789");

	a = lbl;
	a.replace(3, 3, lbl);
	validate(a, "01201234567896789");
	
	a = lbl;
	f0 = a.begin() + 3;
	t0 = f0 + 3;
	a.replace(f0, t0, lbl);
	validate(a, "01201234567896789");
	
	a = lbl;
	a.replace(4, 4, lbl, 2);
	validate(a, "01230189");
	
	a = lbl;
	f0 = a.begin() + 4;
	t0 = f0 + 4;
	a.replace(f0, t0, lbl, 2);
	validate(a, "01230189");
	
	a = lbl;
	a.replace(5, 10, b, 3, 3);
	validate(a, "01234345");
	
	a = lbl;
	f0 = a.begin() + 5;
	t0 = f0 + 10;
	f = b.begin() + 3;
	t = f + 3;
	a.replace(f0, t0, f, t);
	validate(a, "01234345");
	
	a = lbl;
	a.replace(5, 0, b);
	validate(a, "01234012345678956789");

	a = lbl;
	f0 = a.begin() + 5;
	t0 = f0;
	a.replace(f0, t0, b);
	validate(a, "01234012345678956789");
	
	a = lbl;
	a.replace(2, 1, "Something reaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long");
	validate(a, "01Something reaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long3456789");
}
{
	string a, b = lbl;
	char s[40];
	memset(s, 0, 40);

#ifdef FULL_FIRE
	b.copyTo(s, 5);
	a = s;
	validate(a, "0123");

	b.copyTo(s, 40);
	a = s;
	validate(a, lbl);
#endif

//	a.swap(b);
//	validate(b, "3456789");
//	validate(a, lbl); 
}

#ifdef DEV_BUILD
#undef check
#define check(a, b) if (a != b) printf("Wanted %d got %d at %d\n\n",\
										  b, a, __LINE__)
#endif

{
	string a = "012345uuu345678";
					//	 9
	string b = "345";
	
	check(a.find(b), 3);
	check(a.find("45"), 4);
	check(a.find('5'), 5);
	check(a.find("ZZ"), string::npos);

	check(a.rfind(b), 9);
	check(a.rfind("45"), 10);
	check(a.rfind('5'), 11);
	check(a.rfind("ZZ"), string::npos);

	check(a.find("45", 8), 10);

	check(a.find_first_of("aub"), 6);
	check(a.find_first_of(b), 3);
	check(a.find_first_of("54"), 4);
	check(a.find_first_of('5'), 5);
	check(a.find_first_of("ZZ"), string::npos);

	check(a.find_last_of("aub"), 8);
	check(a.find_last_of(b), 11);
	check(a.find_last_of("54"), 11);
	check(a.find_last_of('5'), 11);
	check(a.find_last_of("ZZ"), string::npos);

	check(a.find_first_of("45", 8), 10);

	b = "010";
	check(a.find_first_not_of("aub"), 0);
	check(a.find_first_not_of(b), 2);
	check(a.find_first_not_of("0102"), 3);
	check(a.find_first_not_of('0'), 1);
	check(a.find_first_not_of(a), string::npos);

	b = "878";
	check(a.find_last_not_of("aub"), 14);
	check(a.find_last_not_of(b), 12);
	check(a.find_last_not_of("78"), 12);
	check(a.find_last_not_of('8'), 13);
	check(a.find_last_not_of(a), string::npos);

	check(a.find_first_not_of("u345", 8), 12);
}
{
	string a = lbl;
	string b;

	b = a.substr(3, 4);
	validate(b, "3456");

	b = a.substr(5, 20);
	validate(b, "56789");
#ifdef FULL_FIRE
	b = a.substr(50, 20);
	validate(b, "");

#endif
}
#ifdef FULL_FIRE
{
	string b;
	FILE *x = fopen("string_test.cpp", "rt");
	b.LoadFromFile(x);
	validate(b, "char lbl[] = \"0123456789\";");
	b.LoadFromFile(x);
	validate(b, "");
	b.LoadFromFile(x);
	validate(b, "//#define CHECK_FATAL_RANGE_EXCEPTION");
	fclose(x);
}
#endif
{
	string a = "Something moderaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaately long";
	string a1 = a;
	string b = a + a1;
	string b1 = b;
	string c = b + b1;
	string d = c;
	string e = c;
	string f = c;
	string g = c;
	string h = c;
	string i = c;
	string j = c;
	string k = c;
	string l = c;
	string m = c;
	string n = c;
	string o = c;
	string p = c;
}

#ifdef DEV_BUILD
#undef check
#define check(get, want) if ( (get < 0 ? -1 : get > 0 ? 1 : 0) != want)\
	printf("Wanted %d got %d at %d\n\n", want, get, __LINE__)
#endif

#ifdef FULL_FIRE
{
	PathName c = "Aa";
	PathName d = "AB";

	check(c.compare(d), -1);
}
#endif

{
	string a = lbl;
	string b = a;
	string c = "Aaa";
	string d = "AB";
	string e = "Aa";

	check(a.compare(b), 0);
	check(a.compare(c), -1);
	check(c.compare(a), 1);

	check(c.compare(d), 1);
	check(c.compare(e), 1);
	
	check(a.compare(1, 10, b), 1);
	check(a.compare(1, 10, b, 1, 10), 0);
	check(a.compare(lbl), 0);
	check(a.compare(1, 3, lbl + 1, 3), 0);
}
#ifdef FULL_FIRE
{
	string a = "  011100   ", b;
	
	b = a;
	b.ltrim();
	validate(b, "011100   ");

	b = a;
	b.rtrim();
	validate(b, "  011100");

	b = a;
	b.trim(" 0");
	validate(b, "111");

	b = a;
	b.alltrim("02 ");
	validate(b, "111");

	b = a;
	b.trim("012");
	validate(b, "  011100   ");

	validate(a, "  011100   ");
}
{
	string a = lbl;
	a += '\377';
	string b = a;
	a += " ";
	a.rtrim();
	validate(a, b.c_str());
}
{
	string a = "AaBbCc", b;

	b = a;
	b.lower();
	validate(b, "aabbcc");

	b = a;
	b.upper();
	validate(b, "AABBCC");

	validate(a, "AaBbCc");
}
#endif
}

clock_t t;

void start() {
	t = clock();
}

#ifdef DEV_BUILD
#define TEST_ITEMS 1
#else
#define TEST_ITEMS 100000
#endif

void report() {
	clock_t d = clock();
	printf("Test of %d iterations with %s took %d milliseconds.\n", 
		TEST_ITEMS, NAME, (int)(d-t)*1000/CLOCKS_PER_SEC);
}

int main(int ac, char **av) {
	int n = TEST_ITEMS;
	start();
	while (n--) {
		test();
	}
	report();
#if defined(DEV_BUILD) && defined(FIRESTR)
	getDefaultMemoryPool()->print_contents(stdout, false);
#endif
printf("Press enter to continue\n");
getchar();
return 0;
}
