#!/usr/bin/perl -w
# exotic 0.4
# Copyright (c) 2007-2012, Jan Vidar Krey

use strict;

my $program = $0;
my $file;
my $line;
my @tests;
my $found;
my $test;
my @files;

if ($#ARGV == -1)
{
	die "Usage: $program files\n";
}

my $version = "0.4";

foreach my $arg (@ARGV)
{
	push(@files, $arg);
}


print "/* THIS FILE IS AUTOMATICALLY GENERATED BY EXOTIC - DO NOT EDIT THIS FILE! */\n";
print "/* exotic/$version (Mon Nov  7 11:15:31 CET 2011) */\n\n";

print "#define __EXOTIC__STANDALONE__\n";
standalone_include_exotic_h();

if ($#ARGV >= 0)
{
	foreach $file (@ARGV)
	{
		$found = 0;
		open( FILE, "$file") || next;
		my @data = <FILE>;
		my $comment = 0;
	
		foreach $line (@data) {
			chomp($line);
		
			if ($comment == 1)
			{
				if ($line =~ /^(.*\*\/)(.*)/)
				{
					$line = $2;
					$comment = 0;
				}
				else
				{
					next; # ignore comment data
				}
			}
		
			if ($line =~ /^(.*)\/\*(.*)\*\/(.*)$/)
			{
				$line = $1 . " " . $3; # exclude comment stuff in between "/*" and "*/".
			}
	
			if ($line =~ /^(.*)(\/\/.*)$/) {
				$line = $1; # exclude stuff after "//" (FIXME: does not work if they are inside a string)
			}
		
			if ($line =~ /\/\*/) {
				$comment = 1;
				next;
			}
	
			if ($line =~ /^\s*EXO_TEST\(\s*(\w+)\s*,/)
			{
				$found++;
				push(@tests, $1);
			}
		}
	
		if ($found > 0) {
			print "#include \"" . $file . "\"\n";
		}
	
		close(FILE);
	}
	print "\n";
}

# Write a main() that will start the test run
print "int main(int argc, char** argv)\n{\n";
print "\tstruct exotic_handle handle;\n\n";
print "\tif (exotic_initialize(&handle, argc, argv) == -1)\n\t\treturn -1;\n\n";

if ($#tests > 0)
{
	print "\t/* Register the tests to be run */\n";
	foreach $test (@tests)
	{
		print "\texotic_add_test(&handle, &exotic_test_" . $test . ", \"" . $test . "\");\n";
	}
}
else
{
	print "\t/* No tests are found! */\n";
}

print "\n";
print "\treturn exotic_run(&handle);\n";
print "}\n\n";


standalone_include_exotic_c();

sub standalone_include_exotic_h {
print '
/*
 * Exotic (EXtatic.Org Test InfrastuCture)
 * Copyright (c) 2007, Jan Vidar Krey
 */

#ifndef EXO_TEST
#define EXO_TEST(NAME, block) int exotic_test_ ## NAME (void) block
#endif

#ifndef HAVE_EXOTIC_AUTOTEST_H
#define HAVE_EXOTIC_AUTOTEST_H

#ifdef __cplusplus
extern "C" {
#endif

typedef int(*exo_test_t)();

enum exo_toggle { cfg_default, cfg_off, cfg_on };

struct exotic_handle
{
	enum exo_toggle config_show_summary;
	enum exo_toggle config_show_pass;
	enum exo_toggle config_show_fail;
	unsigned int fail;
	unsigned int pass;
	struct exo_test_data* first;
	struct exo_test_data* current;
};

extern int  exotic_initialize(struct exotic_handle* handle, int argc, char** argv);
extern void exotic_add_test(struct exotic_handle* handle, exo_test_t, const char* name);
extern int  exotic_run(struct exotic_handle* handle);

#ifdef __cplusplus
}
#endif

#endif /* HAVE_EXOTIC_AUTOTEST_H */

'; }
sub standalone_include_exotic_c {
print '
/*
 * Exotic - C/C++ source code testing
 * Copyright (c) 2007, Jan Vidar Krey
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void exotic_version();

#ifndef __EXOTIC__STANDALONE__
#include "autotest.h"
static void exotic_version()
{
	printf("Extatic.org Test Infrastructure: exotic " "${version}" "\n\n");
	printf("Copyright (C) 2007 Jan Vidar Krey, janvidar@extatic.org\n");
	printf("This is free software with ABSOLUTELY NO WARRANTY.\n\n");
}
#endif

struct exo_test_data
{
	exo_test_t test;
	char* name;
	struct exo_test_data* next;
};


static void exotic_summary(struct exotic_handle* handle)
{
	int total     = handle->pass + handle->fail;
	int pass_rate = total ? (100*handle->pass / total) : 0;
	int fail_rate = total ? (100*handle->fail / total) : 0;
	
	printf("\n");
	printf("--------------------------------------------\n");
	printf("Results:\n");
	printf(" * Total tests run:  %8d\n", total);
	printf(" * Tests passed:     %8d (~%d%%)\n", (int) handle->pass, pass_rate);
	printf(" * Tests failed:     %8d (~%d%%)\n", (int) handle->fail, fail_rate);
	printf("--------------------------------------------\n");
}


static void exotic_help(const char* program)
{
	printf("Usage: %s [OPTIONS]\n\n", program);
	printf("Options:\n");
	printf("  --help      -h    Show this message\n");
	printf("  --version   -v    Show version\n");
	printf("  --summary   -s    show only summary)\n");
	printf("  --fail      -f    show only test failures\n");
	printf("  --pass      -p    show only test passes\n");
	printf("\nExamples:\n");
	printf("  %s -s -f\n\n", program);
}


int exotic_initialize(struct exotic_handle* handle, int argc, char** argv)
{
	int n;
	if (!handle || !argv) return -1;
	
	memset(handle, 0, sizeof(struct exotic_handle));
	
	for (n = 1; n < argc; n++)
	{
		if (!strcmp(argv[n], "-h") || !strcmp(argv[n], "--help"))
		{
			exotic_help(argv[0]);
			exit(0);
		}
		
		if (!strcmp(argv[n], "-v") || !strcmp(argv[n], "--version"))
		{
			exotic_version();
			exit(0);
		}
		
		if (!strcmp(argv[n], "-s") || !strcmp(argv[n], "--summary"))
		{
			handle->config_show_summary = cfg_on;
			continue;
		}
		
		if (!strcmp(argv[n], "-f") || !strcmp(argv[n], "--fail"))
		{
			handle->config_show_fail = cfg_on;
			continue;
		}
		
		if (!strcmp(argv[n], "-p") || !strcmp(argv[n], "--pass"))
		{
			handle->config_show_pass = cfg_on;
			continue;
		}
		
		fprintf(stderr, "Unknown argument: %s\n\n", argv[n]);
		exotic_help(argv[0]);
		exit(0);
	}
	
	if (handle->config_show_summary == cfg_on)
	{
		if (handle->config_show_pass == cfg_default) handle->config_show_pass = cfg_off;
		if (handle->config_show_fail == cfg_default) handle->config_show_fail = cfg_off;
	
	}
	else if (handle->config_show_pass == cfg_on)
	{
		if (handle->config_show_summary == cfg_default) handle->config_show_summary = cfg_off;
		if (handle->config_show_fail    == cfg_default) handle->config_show_fail = cfg_off;
	}
	else if (handle->config_show_fail == cfg_on)
	{
		if (handle->config_show_summary == cfg_default) handle->config_show_summary = cfg_off;
		if (handle->config_show_fail    == cfg_default) handle->config_show_fail = cfg_off;
	}
	else
	{
		if (handle->config_show_summary == cfg_default) handle->config_show_summary = cfg_on;
		if (handle->config_show_fail    == cfg_default) handle->config_show_fail = cfg_on;
		if (handle->config_show_pass    == cfg_default) handle->config_show_pass = cfg_on;
	}
	return 0;
}


void exotic_add_test(struct exotic_handle* handle, exo_test_t func, const char* name)
{
	struct exo_test_data* test;
	if (!handle)
	{
		fprintf(stderr, "exotic_add_test: failed, no handle!\n");
		exit(-1);
	}
	
	test = (struct exo_test_data*) malloc(sizeof(struct exo_test_data));
	if (!test)
	{
		fprintf(stderr, "exotic_add_test: out of memory!\n");
		exit(-1);
	}
	
	/* Create the test */
	memset(test, 0, sizeof(struct exo_test_data));
	test->test = func;
	test->name = strdup(name);
	
	/* Register the test in the handle */
	if (!handle->first)
	{
		handle->first = test;
		handle->current = test;
	}
	else
	{
		handle->current->next = test;
		handle->current = test;
	}
}


int exotic_run(struct exotic_handle* handle)
{
	struct exo_test_data* tmp = NULL;
	
	if (!handle)
	{
		fprintf(stderr, "Error: exotic is not initialized\n");
		return -1;
	}

	handle->current = handle->first;
	while (handle->current)
	{
		tmp = handle->current;
		
		if (handle->current->test()) {
			if (handle->config_show_pass == cfg_on) printf("* PASS test \'%s\'\n", tmp->name);
			handle->pass++;
		} else {
			if (handle->config_show_fail == cfg_on) printf("* FAIL test \'%s\'\n", tmp->name);
			handle->fail++;
		}
		
		handle->current = handle->current->next;
		
		free(tmp->name);
		free(tmp);
	}
	
	if (!handle->first)
	{
		printf("(No tests added)\n");
	}
	
	if (handle->config_show_summary == cfg_on)
		exotic_summary(handle);

	return (handle->fail) ? handle->fail : 0;
}


static void exotic_version() {
	printf("exotic 0.4-standalone\n\n");
	printf("Copyright (C) 2007 Jan Vidar Krey, janvidar@extatic.org\n");
	printf("This is free software with ABSOLUTELY NO WARRANTY.\n\n");
}
'; }