#!/usr/bin/env phantomjs

/*
 * Copyright (C) 2013 Red Hat, Inc.
 *
 * Cockpit is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Cockpit is distributed in the hope that it 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 Cockpit; If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * This is a phantom driver for QUnit which outputs TAP
 */

var sys = require('system');
var fs = require('fs');
var child = require("child_process");

var url = null;
var server = [];
var proc;

/*
 * Strip prefix from url
 * We need this to compensate for automake test generation behavior:
 * The tests are called with the path (relative to the build directory) of the testfile,
 * but from the build directory. Some tests make assumptions regarding the structure of the
 * filename. In order to make sure that they receive the same name, regardless of actual
 * build directory location, we need to strip that prefix (path from build to source directory)
 * from the filename
 */
var strip = "";

var failed = false;

var args = sys.args.slice(1);
var arg, optmode = true;

while (args.length > 0) {
   arg = args.shift();
   if (optmode) {
       if (arg === "--") {
           optmode = false;
           continue;
       } else if (arg.indexOf("-") !== 0) {
           optmode = false;
       } else if (arg.indexOf("--strip=") === 0) {
           strip = arg.substring(9);
       } else if (arg === "--strip") {
           strip = args.shift();
       } else {
           sys.stderr.write("tap-phantom: invalid option: " + arg + "\n");
           failed = true;
           usage();
       }
   }
   if (!optmode) {
       if (args.length > 0) {
           server.push(arg);
       } else {
           url = arg;
           if (url.indexOf(strip) == 0)
               url = url.slice(strip.length);
       }
   }
}

if (!failed) {
    if (server.length > 0)
        launch();
    else
        run(url);
}

function usage(code) {
    sys.stderr.write('usage: tap-phantom [--strip=../] -- [server ...] file\n');
    phantom.exit(code === undefined ? 2 : code);
}

function calc_srcdir()
{
    var dir = sys.args[0];
    var pos = dir.lastIndexOf(fs.separator);
    if(pos == -1)
        return ".";
    else
        return dir.substring(0, pos);
}

function print_error(msg, trace, out, prefix) {
    out.writeLine(prefix + msg);
    if (trace && trace.length) {
        trace.forEach(function(frame) {
            out.writeLine(prefix + "  " + (frame.file || frame.sourceURL) + ':' + frame.line +
                          (frame.function ? ' (in function ' + frame.function +')' : ''));
        });
    }
}

function run(url) {
    var page = require('webpage').create();
    page.viewportSize = { width: 800, height: 480 };

    page.onConsoleMessage = function(msg, lineNum, sourceId) {
        /* All done with testing */
        if (msg === "cockpittest-tap-done") {
            if (proc) {
                proc.kill("TERM");
            } else {
                setTimeout(function() { phantom.exit(0); }, 0);
            }

        } else if (msg == "cockpittest-tap-error")
            setTimeout(function() { phantom.exit(1); }, 0);

	else if (msg == "cockpittest-tap-expect-resource-error")
            page.onResourceError = null;

        /* TAP lines go to stdout */
        else if (msg.match(/^ok [0-9]+/i) ||
                 msg.match(/^not ok [0-9]+/i) ||
                 msg.match(/^Bail out!/i) ||
                 msg.match(/^[0-9]+\.\.[0-9]+/) ||
                 msg.match(/^# /))
            sys.stdout.writeLine(msg);

        /* Everything else goes to stderr */
        else
            sys.stderr.writeLine(msg);
    };

    page.onResourceError = function(ex) {
        console.error('Unable to load resource: ' + ex.url + ': ' + ex.errorString);
        phantom.exit(1);
    };

    phantom.onError = function(msg, trace) {
        print_error(msg, trace, sys.stderr, "");
        phantom.exit(1);
    };

    page.onError = function(msg, trace) {
        print_error(msg, trace, sys.stdout, "# ");
        phantom.exit(1);
    };

    page.open(url, function(status) {
        if (status !== "success") {
            console.log("Unable to load:", url);
            phantom.exit(1);
        }

        page.evaluate(function() {
            if (typeof tests_included === "undefined") {
                console.log("Tests not properly included");
                phantom.exit(1);
            }
        });
    });
}

function launch() {
    var running = false;
    var buffer = "";

    proc = child.spawn(server[0], server.slice(1));
    if (proc.pid === 0) {
       console.log("tap-phantom: couldn't spawn: " + server[0]);
       phantom.exit(127);
       return;
    }
    proc.stdout.on("data", function(data) {
        if (running)
            return;
        buffer = buffer + data;
        var pos = buffer.indexOf('\n');
        if (pos >= 0) {
            running = true;
            run(buffer.substring(0, pos) + "/" + url);
        }
    });
    proc.stderr.on("data", function(data) {
        sys.stderr.write(data);
    });
    proc.on("exit", function(code) {
	if (code != 0)
            sys.stderr.writeLine(server[0] + " exited unsuccessfully: " + code);
        phantom.exit(code);
    });
}
