#!/bin/perl

#	PVL_to_DB_test
#
#	This procedure performs unit tests for the PVL_to_DB Java application.
#
#	The test is performed using a local MySQL database. Access to the
#	database must be configured in the Database.conf file as well as for
#	the mysql client application used to create the test table and
#	confirm the database operations. A Database.conf Server may be
#	selected using the -Server option, otherwise the first Server listed
#	will be used. The mysql server host may be specified using the -Host
#	option, otherwise the default host, typically set in the user's
#	~/.my.cnf file, will be used. The configured Server and mysql host
#	must, of course, be the same.
#
#	The test.PVL_to_DB table is intially created anew. The user must have
#	privileges to create and drop this table, including the creation of the
#	containing catalog if it doesn't exist.
#
#	A sequence of recrod insert tests is followed by a sequence of record
#	update tests. At the end of the tests the test table is dropped, but
#	not the containing catalog.
#
#	PIRL CVS ID: PVL_to_DB_test,v 1.9 2012/04/16 06:14:23 castalia Exp
#
#	Copyright (C) 2007-2012  Arizona Board of Regents on behalf of the
#	Planetary Image Research Laboratory, Lunar and Planetary Laboratory at
#	the University of Arizona.
#
#	This file is part of the PIRL Java Packages.
#
#	The PIRL Java Packages are free software; you can redistribute them
#	and/or modify them under the terms of the GNU Lesser General Public
#	License as published by the Free Software Foundation, either version 3 of
#	the License, or (at your option) any later version.
#
#	The PIRL Java Packages are distributed in the hope that they 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 program. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################

use English;

print "--- PVL_to_DB test ----\n";

$Verbose = 0;
$Cleanup = 1;

while ($option = shift @ARGV)
	{
	if ($option =~ /^-[Vv]/)
		{
		$Verbose = 1;
		next;
		}
	if ($option =~ /^-[Kk]/)
		{
		$Cleanup = 0;
		next ;
		}
	if ($option =~ /^-[Ss]/)
		{
		if (! @ARGV || $ARGV[0] =~ /^-/)
			{
			print STDERR "Missing database server name for $option option.\n";
			exit (1);
			}
		$Database_Server = shift @ARGV;
		next ;
		}
	if ($option =~ /^-[Hh]/)
		{
		if (! @ARGV || $ARGV[0] =~ /^-/)
			{
			print STDERR "Missing hostname for $option option.\n";
			exit (1);
			}
		$Database_Host = shift @ARGV;
		next ;
		}
	print "Unknown argument - \"$option\"\n";
	exit (1);
	}


#	PVL_to_DB configuration file.
$Config_Filename	= "./Database.conf";
if (! open (CONFIG_FILE, "< $Config_Filename"))
	{
	print "Unable to access the configuration file - $Config_Filename\n";
	exit (1);
	}
#	Read the user's configuration file.
@User_Config = <CONFIG_FILE>;
close CONFIG_FILE;

#	Database client for submitting SQL statements.
$SQL_Tool		= "mysql";
#		Add the database host specification.
$SQL_Tool		.= " -h $Database_Host"
	if $Database_Host;
#	Database catalog where the test table will be located.
$Catalog		= "test";
#	Database table on which the test will be performed.
$Table			= "$Catalog.pvl_to_db";

#	Java runtime environment.
$Java_Tool		= "java";
$classpath;
#		Provide the classpath for the database driver.
$classpath		= "/opt/java/mysql_connector"
	unless ($classpath = $ENV{"CLASSPATH"});
#		Add the classpath for the PVL_to_DB class.
$Java_Tool		.= " -classpath ../../..:$classpath";
#		Add the PVL_to_DB class.
$Java_Tool		.= " PIRL.PVL.PVL_to_DB";
#		Add the PVL_to_DB verbose option.
$Java_Tool		.= " -verbose";
#		Add the PVL_to_DB database server option.
$Java_Tool		.= " -server $Database_Server"
	if $Database_Server;

#-------------------------------------------------------------------------------
#	Create the test table

print "--- Create the $Table table.\n"
	if $Verbose;
Database ("CREATE DATABASE IF NOT EXISTS $Catalog");
Database ("DROP TABLE IF EXISTS $Table");
Database ("CREATE TABLE $Table (".
	"ID INT AUTO_INCREMENT PRIMARY KEY,".
	"TEXT_1 TEXT,".
	"TEXT_2 TEXT,".
	"INT_1 INT,".
	"INT_2 INT)"
	);

$Source_Filename	= "PVL_to_DB_test.source";
$Map_Filename		= "PVL_to_DB_test.map";
$Config_Filename	= "PVL_to_DB_test.conf";

$PVL_to_DB = $Java_Tool.
	" -Configure $Config_Filename".
	" -Map $Map_Filename ".
	$Source_Filename;

$Total_Tests = 0;
$Passed_Tests = 0;

#-------------------------------------------------------------------------------
#	Insert
print "\n>>> Insert\n";

$Description = "Insert one record, all fields (except auto-increment id).";
#	Make a copy of the user's config file.
Config ('');
Source (
	'Text_1 = First'."\n".
	'Text_2 = Second'."\n".
	'Number_1 = 1000'."\n".
	'Number_2 = 2000'."\n"
	);
Map (
	"Group = $Table"."\n".
	'  TEXT_1 = Text_1'."\n".
	'  TEXT_2 = Text_2'."\n".
	'  INT_1  = Number_1'."\n".
	'  INT_2  = Number_2'."\n".
	'End_Group'."\n"
	);
Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n"
		);


$Description = "Insert another record, using config parameter Test_Parameter.";
Config (
	'Group = PVL_to_DB'."\n".
	'	Test_Parameter = Text'."\n".
	'End_Group'."\n"
	);
Source (
	'Text_1 = First'."\n".
	'Text_2 = Second'."\n".
	'Number_1 = 1000'."\n".
	'Number_2 = 2000'."\n"
	);
Map (
	"Group = $Table"."\n".
	'  TEXT_1 = "${Test_Parameter}_1"'."\n".
	'  TEXT_2 = "${Test_Parameter}_2"'."\n".
	'  INT_1  = Number_1'."\n".
	'  INT_2  = Number_2'."\n".
	'End_Group'."\n"
	);
Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	First	Second	1000	2000'."\n"
		);


$Description = "Insert record field from Array values.";
Config (
	'Group = PVL_to_DB'."\n".
	'	Test_Parameter = Record'."\n".
	'End_Group'."\n"
	);
Source (
	'Record = (a, b, 1, 2)'."\n".
	'Record = (AA, BB, 10, 20)'."\n"
	);
Map (
	"Group = $Table"."\n".
	'  TEXT_1 = "Record"'."\n".
	'  TEXT_2 = "${Test_Parameter}@1[1]"'."\n".
	'  INT_1  = "${Test_Parameter}[2]"'."\n".
	'  INT_2  = "${Test_Parameter}[3]"'."\n".
	'End_Group'."\n"
	);
Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	First	Second	1000	2000'."\n".
		'3	a	BB	1	2'."\n"
		);

#-------------------------------------------------------------------------------
#	Update
print "\n>>> Update\n";

$Description = "Update one record.";
Config (
	'Group = PVL_to_DB'."\n".
	'	Test_Parameter = Text'."\n".
	'End_Group'."\n"
	);
Source (
	'Text_1 = One'."\n".
	'Text_2 = Second'."\n".
	'Number_1 = 1000'."\n".
	'Number_2 = 2000'."\n"
	);
Map (
	"'$Table.TEXT_1:ID=2' = ".'"${Test_Parameter}_1"'."\n"
	);
Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	One	Second	1000	2000'."\n".
		'3	a	BB	1	2'."\n"
		);


$Description = "Update with reference to a command line parameter.";
$pvl_to_db = $PVL_to_DB;
$PVL_to_DB .= " -Set Ident=3";
Source (
	'Number = 3'."\n"
	);
Map (
	"Group = '$Table:ID=".'${Ident}'."'"."\n".
	'  INT_2  = "Number"'."\n".
	'End_Group'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	One	Second	1000	2000'."\n".
		'3	a	BB	1	3'."\n"
		);


$Description = "Command line parameter overriding config.";
$PVL_to_DB .= " -Set INT=2";
Config (
	'INT = -1'."\n"
	);
Source (
	'Number = 3'."\n"
	);
Map (
	"Group = '$Table:ID=\${Ident}'"."\n".
	'  INT_2  = "${INT}"'."\n".
	'End_Group'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	One	Second	1000	2000'."\n".
		'3	a	BB	1	2'."\n"
		);
$PVL_to_DB =$pvl_to_db;


$Description = "Math expression.";
Map (
	"'$Table.INT_1:ID=2' = ".'"2 * 3 + 1"'."\n"
	);
Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	One	Second	7	2000'."\n".
		'3	a	BB	1	2'."\n"
		);


$Description = "Quoted string.";
Source (
	'Record = (a, b, 1, 2)'."\n".
	'Record = (AA, BB, 10, 20)'."\n"
	);
Map (
	"'$Table.TEXT_1:ID=2' = ".'"\'Quoted\'"'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	2000'."\n".
		'3	a	BB	1	2'."\n"
		);


$Description = "Multi-reference with quoted strings combination.";
Source (
	'Record = (R0, R1, 22, 33)'."\n"
	);
Map (
	"'$Table.TEXT_1:ID=3' = ".'"Record[0]\' and \'Record[1]\'_\'Record[3]"'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	2000'."\n".
		'3	R0 and R1_33	BB	1	2'."\n"
		);


$Description = "Alternate source reference.";
Map (
	"'$Table.TEXT_1:ID=3' = ".'"NONE_SUCH | Record"'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	2000'."\n".
		'3	R0	BB	1	2'."\n"
		);


$Description = "Alternate source reference with quoted string.";
Map (
	"'$Table.TEXT_1:ID=3' = ".'"NONE_SUCH | \'alternate\'"'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	2000'."\n".
		'3	alternate	BB	1	2'."\n"
		);


$Description = "Multiple alternate source reference.";
Map (
	"'$Table.TEXT_1:ID=3' = ".'"NONE_SUCH | Record | \'wrong\'"'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	2000'."\n".
		'3	R0	BB	1	2'."\n"
		);


$Description = "Multiple references with alternate sources.";
Map (
	"'$Table.TEXT_1:ID=3' = ".'"Record[4] | Record\',\'Record[1] | "'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	2000'."\n".
		'3	R0,R1	BB	1	2'."\n"
		);


$Description = "Parenthesized references.";
Source (
	'Record = (A, B, C)'."\n"
	);
Map (
	"'$Table.TEXT_1:ID=3' = ".'"(Record[0]\',\'Record[1]\',\'Record[2]) | Record"'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	2000'."\n".
		'3	A,B,C	BB	1	2'."\n"
		);


$Description = "Parenthesized references with alternate source.";
Source (
	'Record = D'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	2000'."\n".
		'3	D	BB	1	2'."\n"
		);


$Description = "Update or Insert fallback.";
Source (
	'Record = (AA, BB, 11, 22)'."\n"
	);
Map (
	"Group = '*$Table:ID=5'"."\n".
	'  TEXT_1 = "Record[0]"'."\n".
	'  TEXT_2 = "Record[1]"'."\n".
	'  INT_1  = "Record[2]"'."\n".
	'  INT_2  = "Record[3]"'."\n".
	'End_Group'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	2000'."\n".
		'3	D	BB	1	2'."\n".
		'4	AA	BB	11	22'."\n"
		);


$Description = "Database reference in Group name.";
Source (
	'Number = 3'."\n"
	);
Map (
	"Group = '$Table:ID={$Table.INT_2:ID=3}'"."\n".
	'  INT_2  = Number'."\n".
	'End_Group'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	3'."\n".
		'3	D	BB	1	2'."\n".
		'4	AA	BB	11	22'."\n"
		);


$Description = "Database reference in map source reference.";
Source (
	'Number = 3'."\n"
	);
Map (
	"Group = $Table\n".
	"  TEXT_1 = '\"{$Table.TEXT_1:ID=1}\"'"."\n".
	"  TEXT_2 = '\"{$Table.TEXT_2:ID=2}\"'"."\n".
	"  INT_1  = '{$Table.INT_1:ID=3}'"."\n".
	"  INT_2  = '{$Table.INT_2:ID=4}'"."\n".
	'End_Group'."\n"
	);

Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	3'."\n".
		'3	D	BB	1	2'."\n".
		'4	AA	BB	11	22'."\n".
		'5	First	Second	1	22'."\n"
		);

$Description = "No expression eval marker of mathematical expression.";
Source (
	'Text_1 = "2/3"'."\n"
	);
Map (
	"'$Table.TEXT_1:ID=5' = ':Text_1'"."\n"
	);
Done (1)
	if ! Check ($Description, 0,
		'ID	TEXT_1	TEXT_2	INT_1	INT_2'."\n".
		'1	First	Second	1000	2000'."\n".
		'2	Quoted	Second	7	3'."\n".
		'3	D	BB	1	2'."\n".
		'4	AA	BB	11	22'."\n".
		'5	2/3	Second	1	22'."\n"
		);

Done (0);

#-------------------------------------------------------------------------------

#	Submit an SQL request to the database server.

sub Database {

my ($SQL) = @_;
my $DB_op = "$SQL_Tool -e '".$SQL."'";
print "$DB_op\n"
	if $Verbose;
if (system ("$DB_op") >> 8)
	{
	print "*** FAIL: You may not have the database privilege to do this.\n";
	print "$DB_op\n"
		if ! $Verbose;
	exit (1);
	}
}


#	Make a copy of the user's config parameters to the $Config_Filename,
#	adding any additional $Config PVL to the end of the file.

sub Config {

($Config) = @_;
if (! open (CONFIG_FILE, "> $Config_Filename") ||
	! print CONFIG_FILE @User_Config, $Config)
	{
	print "**** FAIL: Unable to create the config file - $Config_Filename\n";
	Done (1);
	}
close CONFIG_FILE;
}


#	Write the $Source PVL statements to the $Source_Filename.

sub Source {

($Source) = @_;
if (! open (SOURCE_FILE, "> $Source_Filename") ||
	! print SOURCE_FILE $Source)
	{
	print "**** FAIL: Unable to create the source file - $Source_Filename\n";
	Done (1);
	}
close SOURCE_FILE;
}


#	Write the $Map PVL statements to the $Map_Filename.

sub Map {

($Map) = @_;
if (! open (MAP_FILE, "> $Map_Filename") ||
	! print MAP_FILE $Map)
	{
	print "**** FAIL: Unable to create the map file - $Map_Filename\n";
	Done (1);
	}
close MAP_FILE;
}


#	Check the results of the PVL_to_DB operation:
#		Run the $PVL_to_DB and capture its report.
#		Compare the specified exit status with what resulted.
sub Check {

my ($description, $expected_status, $expected_table) = @_;

#	Run the $PVL_to_DB command and capture the report.
print
	"\n",
	"----------------------------------------------------------------------\n",
	"$PVL_to_DB\n"
	if $Verbose;
$PVL_to_DB_report = `$PVL_to_DB`;
print
	$PVL_to_DB_report
	if $Verbose;

#	Check the exit status.
my $obtained_status = $? >> 8;
$expected_status &= 0xFF;
my $match = ($obtained_status == $expected_status);
print
	"\n",
	"----------------------------------------------------------------------\n",
	"$PVL_to_DB\n",
	$PVL_to_DB_report
	if (! $Verbose && ! $match);
print
	"--> Expected exit status $expected_status\n",
	"    Obtained exit status $obtained_status\n"
	if ($Verbose || ! $match);

if ($match)
	{
	#	Check the table contents.
	print 'echo "select * from ', $Table, '" | ', $SQL_Tool, "\n"
		if $Verbose;
	my $obtained_table = qx/echo "select * from $Table" | $SQL_Tool/;
	$match = $obtained_table eq $expected_table;
	print
		"--> Expected table contents:\n$expected_table",
		"    Obtained table contents:\n$obtained_table"
		if ($Verbose || ! $match);
	}

print
	"=== Configuration: $Config_Filename\n", $Config,
	"=== Source: $Source_Filename\n", $Source,
	"=== Map: $Map_Filename\n", $Map
	if ($Verbose || ! $match);

print
	"*** ", ($match ? "PASS" : "FAIL"), ": $description\n";

$Total_Tests++;
$Passed_Tests++
	if $match;
return $match;
}


#	End of test cleanup.:
#		Report the number tests passed and tried.
#		Delete the $Config_Filename, $Source_Filename and $Map_Filename.
#		Delete the test $Table from the database catalog.
#	Exit with the specified status value.

sub Done {

my ($status) = @_;

print "\n",
	$Passed_Tests, " test", ($Passed_Tests == 1 ? "" : "s"), " passed.\n",
	$Total_Tests, " test", ($Total_Tests == 1 ? "" : "s"), " tried.\n";

if ($Cleanup)
	{
	#	Remove the Config, Source and Map files.
	unlink ($Config_Filename);
	unlink ($Source_Filename);
	unlink ($Map_Filename);

	#	Remove the test table
	Database ("DROP TABLE IF EXISTS $Table");
	}

exit ($status);
}
