#! /usr/bin/env perl
#testreq: personal
##**************************************************************
##
## Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
## University of Wisconsin-Madison, WI.
##
## Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
##
##**************************************************************

# This tests using the KeepClaimIdle attribute in a DAG. The KeepClaimIdle
# attribute tells the schedd to hold a claim for a specified amount of time after
# the process terminates with the expectation that another process will come
# along to reuse the claim.  In the test below, we set the hold time to 30
# seconds.  We verify that the claim is reused.  Also, we verify that the
# attribute is not set for the final DAGman node.

use CondorTest;
use CondorUtils;

$testdesc =  'Test the condor_q -dag formatter';
$testname = "cmd_q_shows-dag";
$cmd = 'cmd_q_shows-dag.dag';
$dagman_args = "-verbose";

$logfile = "${testname}.dag.dagman.out";
@outfiles = (
	'cmd_q_shows-dag-A.err',
	'cmd_q_shows-dag-A.out',
	'cmd_q_shows-dag-B_A_A.err',
	'cmd_q_shows-dag-B_A_A.out',
	'cmd_q_shows-dag-B_B.err',
	'cmd_q_shows-dag-B_B.out',
	'cmd_q_shows-dag-B_C.err',
	'cmd_q_shows-dag-B_C.out',
	'cmd_q_shows-dag-C.err',
	'cmd_q_shows-dag-C.out',
	'cmd_q_shows-dag-B.dag.condor.sub',
	'cmd_q_shows-dag-B.dag.dagman.log',
	'cmd_q_shows-dag-B.dag.dagman.out',
	'cmd_q_shows-dag-B.dag.lib.err',
	'cmd_q_shows-dag-B.dag.lib.out',
	'cmd_q_shows-dag-B.log',
	'cmd_q_shows-dag-B_A.dag.condor.sub',
	'cmd_q_shows-dag-B_A.dag.dagman.log',
	'cmd_q_shows-dag-B_A.dag.dagman.out',
	'cmd_q_shows-dag-B_A.dag.lib.err',
	'cmd_q_shows-dag-B_A.dag.lib.out',
	'cmd_q_shows-dag-B_A.log',
	'cmd_q_shows-dag.dag.condor.sub',
	'cmd_q_shows-dag.dag.dagman.log',
	'cmd_q_shows-dag.dag.dagman.out',
	'cmd_q_shows-dag.dag.lib.err',
	'cmd_q_shows-dag.dag.lib.out',
	'cmd_q_shows-dag.log',
	'cmd_q_shows-dag.output'
);

# truly const variables in perl
sub IDLE{1};
sub HELD{5};
sub RUNNING{2};

# Get rid of any existing log and output files
unlink $logfile if (-e $logfile);
foreach (@outfiles) { unlink $_ if (-e $_); }

# create files needed to run this test.
emit_dag_files();

$abnormal = sub
{
	die "Want to see only submit, execute and successful completion\n";
};

$aborted = sub
{
	die "Abort event NOT expected\n";
};

$held = sub
{
	die "Held event NOT expected\n";
};

$executed = sub
{
	my %info = @_;

	CondorTest::debug("Good. We need the dag to run. cluster=$info{cluster}\n",1);
};

$submitted = sub
{
	CondorTest::debug("submitted: This test will see submit, executing and successful completion\n",1);
};

my @expected_output = ( 'condor_dagman', '|-A', '|-B', '|-B_A', '|-B_A_A', '|-B_B', '|-B_C',
	'|-C' );

my @rlengths = ( 0, 1, 1, 2, 3, 2, 2, 1 );

$success = sub {
	print "DAG Succeeded as expected. Processing cmd_q_shows-dag.output\n";
	open CONDORQ,"<cmd_q_shows-dag.output" || die "Could not open cmd_q_shows-dag.output\n";
	my @received;
	foreach (<CONDORQ>) {
		chomp $_;
		if ($_ =~ /^\s*\d+\./) {
			push @received, $_;
			CondorTest::debug("Received: $_\n",1);
		} else {
			CondorTest::debug("Ignored : $_\n",1);
		}
	}
	close CONDORQ;
	if( $#received != $#expected_output ) {
		my $expected = scalar @expected_output;
		my $received = scalar @received;
		print STDERR "Expected:\n";
		foreach my $line (@expected_output) { print "    $line\n"; }
		print STDERR "Actual:\n";
		foreach my $line (@received) { print "    $line\n"; }
		die "Expected $expected lines of output, found $received\n";
	}
	# First loop: See that we got the expected patterns
	foreach my $i (0..$#expected_output) {
		if($received[$i] !~ /$expected_output[$i]/) {
			print STDERR "Expected:\n";
			foreach my $line (@expected_output) { print "    $line\n"; }
			print STDERR "Actual:\n";
			foreach my $line (@received) { print "    $line\n"; }
			die "Expected $received[$i], but got \"$expected_output[$i]\"\n";
		}
	}
	# Second loop: Make sure they were nested as expected
	my $indent_length = 0;
	if($received[0] =~ /\s*[0-9]+\.[0-9]+(\s*)/) {
		$indent_length = length $1;	
	}
	foreach my $i (0..$#expected_output) {
		my $rlength;
		if($received[$i] =~ /\s*[0-9]+\.[0-9]+(\s*)/) {
			$rlength = length $1;
		} else {
			$rlength = 0;
		}
		if( $rlength != $rlengths[$i] + $indent_length ) {
			die "Indentation not correct in cmd_q_shows-dag.output\n";
		}
	}
};

$failure = sub
{
	die "Error: DAG is not expected to fail!\n";
};

CondorTest::RegisterExitedSuccess( $testname, $success);
CondorTest::RegisterExitedFailure( $testname, $failure );
CondorTest::RegisterExecute($testname, $executed);
CondorTest::RegisterExitedAbnormal( $testname, $abnormal );
CondorTest::RegisterAbort( $testname, $aborted );
CondorTest::RegisterHold( $testname, $held );
CondorTest::RegisterSubmit( $testname, $submitted );

if( CondorTest::RunDagTest($testname, $cmd, 0, $dagman_args) ) {
	CondorTest::debug("$testname: SUCCESS\n",1);
	exit(0);
} else {
	die "$testname: CondorTest::RunTest() failed\n";
}

#
# helper functions
#
sub emit_dag_files {

	my $dag_content = "JOB A ${testname}-A.cmd
VARS A JOB=\"A\"
SUBDAG EXTERNAL B ${testname}-B.dag
ABORT-DAG-ON B 1 RETURN 0
JOB C ${testname}-C.cmd
VARS C JOB=\"C\"
";

	my $dag_b_content = "SUBDAG EXTERNAL B_A ${testname}-B_A.dag
ABORT-DAG-ON B_A 1
JOB B_B ${testname}-B_B.cmd
VARS B_B JOB=\"B_B\"
JOB B_C ${testname}-B_C.cmd
VARS B_C JOB=\"B_C\"
";

	my $dag_b_a_content = "JOB B_A_A ${testname}-B_A_A.cmd
ABORT-DAG-ON B_A_A 1
VARS B_A_A JOB=\"B_A_A\"
";

	# all of the dag submit files are derived from this with TAG, LOT and TIME substituted.
	# these jobs start on hold because we don't want them to start until the sub_q_content job releases them.
	my $sub_tag_content = "universe = vanilla
if \$(IsWindows)
  executable = ${testname}.bat
else
  executable = ${testname}.sh
endif
output = ${testname}_TAG.out
error = ${testname}_TAG.err
log = ${testname}LOT.log
arguments = \$(JOB) TIME
hold=true
queue
";

	# the leafiest dag submit file runs condor_q -dag
	# LOT and TIME are substituted.
	# "getenv = true" needed for condor_q to find the correct condor instance.
	my $sub_q_content = "universe = vanilla
if \$(IsWindows)
  executable = ${testname}-q.bat
else
  executable = ${testname}-q.sh
endif
output = ${testname}.output
getenv = true
error = ${testname}.error
log = ${testname}LOT.log
arguments = \$(JOB) TIME
queue
";

	if (CondorUtils::is_windows()) {
		emit_file(".bat", "\@echo \%1 \%2\n");
	} else {
		emit_file(".sh", "#!/usr/bin/env bash\necho \$1 \$2\n");
	}

	# make sure out and err files exist, NMI complains if these files are not around
	emit_file(".out", "");
	emit_file(".err", "");

	# create dag and subdags
	emit_file(".dag", $dag_content);
	emit_file("-B.dag", $dag_b_content);
	emit_file("-B_A.dag", $dag_b_a_content);

	# create submit files
	emit_file("-A.cmd", specialize_content($sub_tag_content, "TAG"=>"A", "TIME"=>"120", "LOT"=>""));
	emit_file("-B_B.cmd", specialize_content($sub_tag_content, "TAG"=>"B_B", "TIME"=>"100", "LOT"=>"-B"));
	emit_file("-B_C.cmd", specialize_content($sub_tag_content, "TAG"=>"B_C", "TIME"=>"100", "LOT"=>"-B"));
	emit_file("-C.cmd", specialize_content($sub_tag_content, "TAG"=>"C", "TIME"=>"120", "LOT"=>""));

	emit_file("-B_A_A.cmd", specialize_content($sub_q_content, "TAG"=>"B_A_A", "TIME"=>"80", "LOT"=>"-B_A"));
	if (CondorUtils::is_windows()) {
		emit_file("-q.bat", "\@condor_q -dag -nobatch\n\@condor_release -all\@exit /b 1\n");
	} else {
		emit_file("-q.sh", "#!/usr/bin/env bash\ncondor_q -dag -nobatch\ncondor_release -all\nexit 1\n");
	}
}

sub emit_file {
	my $namex = shift;
	my $content = shift;

	my $fname = $testname . $namex;
	open (FH, ">$fname") || die "error writing to $fname: $!\n";
	print FH $content;
	close (FH);
	if ( ! CondorUtils::is_windows()) { chmod (0755, $fname); }
}

# return the first arg, after substituting
# all of the remaining args should be key=>val pairs where $key in the input
# text is globally replaced with $val
#
sub specialize_content {
	my $content = shift;
	my %repl = @_;

	while (($key, $val) = each %repl) {
		$content =~ s/$key/$val/g;
	}
	return $content;
}
