// test.unit.js
// 
// Copyright (c) 2010 Guillaume Lathoud
// MIT License
//
// Run unit tests.
// This is meant to be used with the Google V8 Engine:
// 
// d8 test.unit.js

/*global array from jscheck load os passed log narcissus object print read str unit write*/

if ((typeof load !== 'undefined') && (typeof from === 'undefined'))
    load('from.js');

from.global.unit = {};

from.req('log.js', 'array.js', 'str.js')(function () {

    print( 'Testing the Narcissus parser...' );

    from.req('test-narcissus-xengine.js')
    (function () {
        
        var unit = from.global.unit;
        unit.passed = from.global.passed;

        // At this point we should have unit.passed.string

        // List the automatic tests
        var tunt = 'test.unit.narcissus.tree2code.js'

        , auto_test_arr = array.filter( 
            array.map( os.system( 'find', [ '.', '-name', 'test.unit.*.js' ] ).match( /[^\/]*?$/gim ), 
                       str.trim )
            
            , function (s) { return s && (s !== tunt) }
        )
        
        ;

        // sort: Important to make sure that the from.req happens in
        // the right order, e.g. to AVOID loading
        // `test.unit.tool.tailopt.js` *after*
        // `test.unit.tool.tailopt.not_a_tailcall.js`, which would
        // erase the latter tests from the object space.
        auto_test_arr.sort();
        
        from.req.apply( from, auto_test_arr
                        .concat( [ tunt, 'jscheck.js', 'narcissus.tree.equal.js', 'str.js', 'narcissus.code.compress.js' ]))
        ( function () {
            
            // Load myself
            var MYNAME = 'test.unit.js';
            from.xhr(MYNAME)( function (response) {
                
                // Am I generated code?
                var i_am_original = /\/\/ Am I generated code\?/.test(response.responseText);
                print(MYNAME + ': i_am_original:', i_am_original);

                // Automatic tests

                var test_it = function (/*string*/test_name) {

                    if (!test_name)
                        return;

                    var test = new Function( 'return from.global.' + test_name + ';' )();

                    if (!test)
                        return;

                    if (typeof test === 'function') {
                        
                        if (-1 < array.indexOf( object.keys( unit.passed ), test_name ))
                            return; // already tested
                        
                        write( 'Running ' + test_name + '... ' );
                        var r = test( /*silent*/true );
                        print(
                            unit.passed[ test_name ] = 
                                r && r.errors && r.warnings && (r.errors.length === 0) && (r.warnings.length === 0)
                        );
                        
                    }

                    // Recursive search for more tests 
                    // (also valid for functions, because functions are objects)
                    
                    if (test instanceof Object) {
                        for (var k in test)
                            test_it( test_name + '.' + k);
                        
                    }
                };

                array.map( auto_test_arr, function (test_file_name) { test_it( /^(.*)\.js$/.exec(test_file_name)[1] ); } );

                // Systematic unit tests on all javascript files in the current directory

                var s, arr = os.system('ls').split(/\n/g), rx = /\.js$/, rx_original = /_original\.js$/;
                
                while ((s = arr.pop()) != null) {

                    s = str.trim(s);
                    if ((!rx.test(s)) || rx_original.test(s))
                        continue;

                    var result
                    ,   source = read(s)
                    ;

                    array.map( 
                        ['jscheck', 'test.unit.narcissus.tree2code'],
                        function (test_name) {
                            
                            write( 'Running ' + test_name + ' on file "' + s + '"...' );
                            result = eval(test_name)( source );

                            if (!i_am_original) {

                                result.warnings    = array.filter( 
                                    result.warnings, 
                                    function (w) { return !/Undeclared global (function|variable)/.test(w.message); } );

                                result.has_warning = (result.warnings.length > 0);
                            }

                            print( unit.passed[ test_name + '("' + s + '")' ] = result && result.errors && 
                                 (result.errors.length === 0) && result.warnings && (result.warnings.length === 0) );

                            if (!unit.passed[ test_name + '("' + s + '")' ]) {
                                for (var a = 0, a_max = result.errors.length; a < a_max; a++) {
                                    print( ' - error #' + a + ': ' + result.errors[a].message );
                                }
                                if (result.warnings.length > 0)
                                    print( ' - also ' + result.warnings.length + ' warnings.');
                            }
                        });

                }

                // Test assignment operators

                var arr = [
                    'a+=2;'
                    , 'a-=2;'
                    , 'a*=2;'
                    , 'a/=2;'
                    , 'a+=2;b.a+=3;a[k]/=3-"456";'
                    , 'a+=2;b.a-=2;a[k]/=3-"456";'
                    , 'a+=2;b.a-=3;a[k]/=3-"456";b[0]*=-134;a.b[c]+=d-e;'
                    , 'a-=2;b.a-=3;a[k]/=3-"456";b[0]*=-134;a.b[c]+=d-e;'
                    , 'a+=2;b.a+=3;a[k]/=3-"456";b[0]*=-134;a.b[c]+=d-e;'
                    , 'a+=2;b.a-=3;a[k]-=3-"456";b[0]*=-134;a.b[c]+=d-e;'
                    , 'a+=2;b.a-=3;a[k]/=3-"456";b[0]+=-134;a.b[c]+=d-e;'
                    , 'a+=2;b.a-=3;a[k]/=3-"456";b[0]*=-134;a.b[c]/=d-e;'
                    , 'a+=2;b.a-=3;a[k]*=3-"456";b[0]-=-134;a.b[c]+=d-e;'
                    , 'a-=2;b.a-=3;a[k]/=3-"456";b[0]-=-134;a.b[c]*=d-e;'
                ]
                , arr_jscheck = array.map( arr, jscheck )
                , test_name
                ;

                test_name = 'assignOp_jscheck';
                write('Running ' + test_name + '...');
                print( unit.passed[ test_name ] = array.and( arr_jscheck, function (x, ind) { 
                    
                    var ret = !x.has_errors;

                    if (!ret)
                        print(test_name + ': error for arr[ind:' + ind + ']:"' + arr[ind] + '", error message:' + x.errors[0].message);

                    return ret;
                } ));
                
                test_name = 'assignOp_tree2code';
                write('Running ' + test_name + '...');
                print( unit.passed[ test_name ] = array.and( arr_jscheck, function (x, ind) { 

                    var ret = (narcissus.tree2code(x.tree) === arr[ind]); 

                    if (!ret)
                        print(test_name + ': error for arr[ind:' + ind + ']:"' + arr[ind] + '"');

                    return ret;
                } ));

                test_name = 'assignOp_tree_equal';
                write('Running ' + test_name + '...');
                unit.passed[ test_name ] = true;
                var a,b,c;
                for (a = arr_jscheck.length; a--; ) {
                    c = narcissus.tree.equal( arr_jscheck[a].tree, jscheck( arr[a] ).tree );
                    
                    if (c.match !== true) {
                        print( test_name + ': error for arr[a:' + a + ']:"' + arr[a] + '"')
                        unit.passed[ test_name ] = false;
                    }
                }
                print(unit.passed[ test_name ]);

                test_name = 'assignOp_tree_equal_not';
                write('Running ' + test_name + '...');
                unit.passed[ test_name ] = true;
                var a,b,c;
                for (a = arr_jscheck.length; a--; ) {

                    for (b = a; b--; ) {
                        
                        c = narcissus.tree.equal( arr_jscheck[a].tree, arr_jscheck[b].tree );

                        if (c.match !== false) {
                            print( test_name + ': error for arr[a:' + a + ']:"' + arr[a] + '"')
                            print( test_name + ':       and arr[b:' + b + ']:"' + arr[b] + '"' );
                            unit.passed[ test_name ] = false;
                        }
                    }
                }
                print(unit.passed[ test_name ]);
                

                if (i_am_original) {

                    test_name = 'Unitus_Regeneratus';
                    
                    print(test_name + ": Additional test: parse and regenerate all files, and run all the tests again!");

                    print(test_name + ": Parsing and regenerating all files...");
                    var outputdir = os.system('./test.parse_regen.py').replace(/(^\s*|\s*$)/g, '')
                    print(test_name + ": outputdir:", outputdir);
                    
                    var pwd = str.trim(os.system('pwd'));
                    os.chdir(outputdir);
                    print(test_name + ": Running all tests again, using the regenerated implementation...")
                    var output = os.system('d8', [MYNAME]);
                    os.chdir(pwd);
                    
                    print('\n---------- output of ' + test_name + ' ---------- START')

                    print(output);

                    print('---------- output of ' + test_name + ' ---------- END\n')

                    unit.passed[ test_name ] = true;

                    if (! /i_am_original: false/.test( output )) {
                        print( test_name + ': failure on "i_am_original"' );
                        unit.passed[ test_name ] = false;
                    }

                    var mo = /Summary:\s+[\s\S]*\s- full success\!\s*$/.exec( output )
                    if (!mo) {
                        print( test_name + ': failure on "full success"' );
                        unit.passed[ test_name ] = false;
                    } 

                    if (unit.passed[ test_name ]) {
                        print(test_name + ": successful result!");
                        print(test_name + ": deleting the temporary dir: " + outputdir);
                        os.system('rm', ['-rf', outputdir]);
                    } else {
                        print(test_name + ": failure!");
                    }
                }

                if (i_am_original) {

                    test_name = 'Unitus_Kompressus';
                    
                    print(test_name + ": Additional test: compress all files, and run all the tests again!");

                    print(test_name + ": Compressing all files...");
                    var outputdir = os.system('./test.compress.py').replace(/(^\s*|\s*$)/g, '')
                    print(test_name + ": outputdir:", outputdir);
                    
                    var pwd = str.trim(os.system('pwd'));
                    os.chdir(outputdir);
                    print(test_name + ": Running all tests again, using the compressed implementation...")
                    var output = os.system('d8', [MYNAME]);
                    os.chdir(pwd);
                    
                    print('\n---------- output of ' + test_name + ' ---------- START')

                    print(output);

                    print('---------- output of ' + test_name + ' ---------- END\n')

                    unit.passed[ test_name ] = true;

                    if (! /i_am_original: false/.test( output )) {
                        print( test_name + ': failure on "i_am_original"' );
                        unit.passed[ test_name ] = false;
                    }

                    var mo = /Summary:\s+[\s\S]*\s- full success\!\s*$/.exec( output )
                    if (!mo) {
                        print( test_name + ': failure on "full success"' );
                        unit.passed[ test_name ] = false;
                    } 

                    if (unit.passed[ test_name ]) {
                        print(test_name + ": successful result!");
                        print(test_name + ": deleting the temporary dir: " + outputdir);
                        os.system('rm', ['-rf', outputdir]);
                    } else {
                        print(test_name + ": failure!");
                    }
                }

                if (i_am_original) {

                    test_name = 'Unitus_Kompressus_do_not_force_all_new_local_varnames';
                    
                    print(test_name + ": Additional test: compress all files, and run all the tests again!");

                    print(test_name + ": Compressing all files...");
                    var outputdir = os.system('./test.compress.py', ['true']).replace(/(^\s*|\s*$)/g, '')
                    print(test_name + ": outputdir:", outputdir);
                    
                    var pwd = str.trim(os.system('pwd'));
                    os.chdir(outputdir);
                    print(test_name + ": Running all tests again, using the compressed implementation...")
                    var output = os.system('d8', [MYNAME]);
                    os.chdir(pwd);
                    
                    print('\n---------- output of ' + test_name + ' ---------- START')

                    print(output);

                    print('---------- output of ' + test_name + ' ---------- END\n')

                    unit.passed[ test_name ] = true;

                    if (! /i_am_original: false/.test( output )) {
                        print( test_name + ': failure on "i_am_original"' );
                        unit.passed[ test_name ] = false;
                    }

                    var mo = /Summary:\s+[\s\S]*\s- full success\!\s*$/.exec( output )
                    if (!mo) {
                        print( test_name + ': failure on "full success"' );
                        unit.passed[ test_name ] = false;
                    } 

                    if (unit.passed[ test_name ]) {
                        print(test_name + ": successful result!");
                        print(test_name + ": deleting the temporary dir: " + outputdir);
                        os.system('rm', ['-rf', outputdir]);
                    } else {
                        print(test_name + ": failure!");
                    }
                }

                print('.');
                print('Tests finished!');
                print();
                print('Summary:');

                arr = [];
                for (s in unit.passed)
                    if (unit.passed[s])
                        arr.push(s);
                arr.sort();
                if (arr.length)
                    print('- success for: ', arr.join(', '));
                else
                    print('- no success.');

                arr = [];
                for (s in unit.passed)
                    if (!unit.passed[s])
                        arr.push(s);
                arr.sort();
                if (arr.length)
                    print('- failure for: ', arr.join(', '));
                else
                    print('- full success!');


                from.req.done('test.unit.js', true);
            });
        });

    });

});

