#!/usr/bin/env python3
"""
Copyright (c) 2015 - 2017, David Bidner <david -at- crap.solutions>

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
USE OR PERFORMANCE OF THIS SOFTWARE.
"""
# progtest.py - A simple tool used to apply I/O diffs on a given binary
# Further Information: https://crap.solutions/pages/progtest.html - https://github.com/sigttou/progtest

import sys
import os
import os.path
from subprocess import check_output, CalledProcessError
from difflib import unified_diff
from tempfile import mkdtemp

VAL_ERR = 111
VALGRIND_ARGS = '--leak-check=full --show-leak-kinds=all --track-origins=yes --quiet'
BINARY = sys.argv[1] if len(sys.argv) > 1 else "./testbin"
RESULTS_DIR = mkdtemp()
VALGRIND_CHECK = True
TIMEOUT = 10

RED = '\033[0;31m'
GREEN = '\033[0;32m'
NOCOL = '\033[0m'


def main():
    tests = [os.path.join('./tests/', o) for o in os.listdir('./tests/') if os.path.isdir(os.path.join('./tests/', o))]
    for test in tests:
        input = open(test + '/in') if os.path.isfile(test + '/in') else None
        exp_output = open(test + '/out').read() if os.path.isfile(test + '/out') else ''
        sys.stdout.write('Test: ' + os.path.basename(test))

        args = open(test + '/args').read() if os.path.isfile(test + '/args') else ''
        val_check = False if os.path.isfile(test + '/noval') else VALGRIND_CHECK
        exp_ret = int(open(test + '/ret').read()) if os.path.isfile(test + '/ret') else 0

        call = []
        call.append('timeout')
        call.append(str(TIMEOUT))
        if val_check:
            call.append('valgrind')
            call += VALGRIND_ARGS.split()
            val_logfile = RESULTS_DIR + '/' + os.path.basename(test) + '.val'
            call.append('--log-file={}'.format(val_logfile))
            call.append('--error-exitcode={}'.format(VAL_ERR))
        call.append(BINARY)
        call += args.split()

        fail = False
        try:
            out = check_output(call, stdin=input).decode()
        except CalledProcessError as grepexc:
            out = grepexc.output.decode()
            if grepexc.returncode == VAL_ERR:
                print('\n+++ Valgrind error, see {} for details +++'.format(val_logfile))
                fail = True
            else:
                if not grepexc.returncode == exp_ret:
                    fail = True
                    print('\n+++ Wrong return value: {} should be: {} +++'.format(grepexc.returncode, exp_ret))

        diff = list(unified_diff(exp_output.splitlines(1), out.splitlines(1), fromfile='exp_output', tofile='output'))
        if(diff):
            difffile = open(RESULTS_DIR + '/' + os.path.basename(test) + '.diff', 'w+')
            difffile.writelines(diff)
            print('+++ Diff error, see {} for details +++'.format(difffile.name))
        else:
            print('Diff ' + GREEN + 'OK' + NOCOL)
        if(diff or fail):
            outfile = open(RESULTS_DIR + '/' + os.path.basename(test) + '.out', 'w+')
            outfile.write(out)
            sys.stdout.write('..' + RED + 'FAIL' + NOCOL + '(see {} for output)\n'.format(outfile.name))
        else:
            sys.stdout.write('..ALL:' + GREEN + 'OK' + NOCOL + '\n')


if __name__ == '__main__':
    main()

@@ -1339,16 +1339,34 @@
 OUT = 10
+%0ATIMEOUT_ERR = 124
 %0A%0ARED = 
@@ -1752,27 +1752,24 @@
     
-sys.stdout.write('T
+print('Running t
 est:
@@ -2691,16 +2691,139 @@
         
+fail = True%0A            if grepexc.returncode == exp_ret:%0A                fail = False%0A                break%0A            el
 if grepe
@@ -2871,21 +2871,19 @@
  print('
-%5Cn+++
+   
  Valgrin
@@ -2893,32 +2893,12 @@
 rror
-, see %7B%7D for details +++
+: %7B%7D
 '.fo
@@ -2932,64 +2932,12 @@
     
-    fail = True%0A            else:%0A                if not
+elif
  gre
@@ -2947,39 +2947,43 @@
 c.returncode == 
-exp_ret
+TIMEOUT_ERR
 :%0A              
@@ -2984,37 +2984,63 @@
         
-    fail = True%0A     
+print('    Test timed out!')%0A            else:%0A
         
@@ -3050,20 +3050,32 @@
     
+ 
 print('
-%5Cn+++
+    Test had the
  Wro
@@ -3107,20 +3107,16 @@
 d be: %7B%7D
- +++
 '.format
@@ -3433,134 +3433,85 @@
 nt('
-+++ Diff error, see %7B%7D for details +++'.format(difffile.name))%0A        else:%0A            print('Diff ' + GREEN + 'OK' + NOCOL)
+    Output does not match: %7B%7D'.format(difffile.name))%0A            fail = True
 %0A   
@@ -3522,16 +3522,8 @@
  if(
-diff or 
 fail
@@ -3658,32 +3658,14 @@
     
-sys.stdout.write('..' + 
+print(
 RED 
@@ -3688,29 +3688,12 @@
  + '
-(see %7B%7D for output)%5Cn
+: %7B%7D
 '.fo
@@ -3742,36 +3742,14 @@
     
-sys.stdout.write('..ALL:' + 
+print(
 GREE
@@ -3768,15 +3768,8 @@
 OCOL
- + '%5Cn'
 )%0A%0A%0A

better output