<!DOCTYPE html>
<html>
    <head>
        <title>Cockpit Integration Tests</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js" type="text/javascript"></script>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.2.1/mustache.min.js"></script>
        <!-- nicer arrows for the collapsible panels and preformatted text-->
        <style>
        * {
         font-family: "Open Sans";
        }
        .panel-heading:last-child:after {
            font-family:'Glyphicons Halflings';
            content:"\e114";
            float: right;
            color: grey;
        }
        .panel-heading:last-child.collapsed:after {
            content:"\e080";
        }
        .panel-heading.failed {
            color: #A94442;
            background-color: #F2DEDE;
            border-color: #EBCCD1;
        }
        .panel-heading.skipped {
            color: #8A6D3B;
            background-color: #FCF8E3;
            border-color: #FAEBCC;
        }
        li.failed {
            color: #A94442;
            background-color: #F2DEDE;
            border-color: #EBCCD1;
        }
        </style>
        <script id="Tests" type="text/template">
            <div class="panel-group" id="accordion">
                {{#tests}} {{{html}}} {{/tests}}
            </div>
        </script>
        <script id="ScreenshotLink" type="text/template">
            <a href="./{{screenshot}}" title="{{screenshot}}">
                <span class="glyphicon glyphicon-camera" aria-hidden="true"></span>
                screenshot
            </a>
        </script>
        <script id="JournalLink" type="text/template">
            <a href="./{{journal}}" title="{{journal}}">
                <span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span>
                journal
            </a>
        </script>
        <script id="TestEntry" type="text/template">
            <div class="panel panel-default" id="{{id}}">
                <div class="panel-heading
                            {{#collapsed}}collapsed{{/collapsed}}
                            {{^passed}}failed{{/passed}}
                            {{#skipped}}skipped{{/skipped}}" data-toggle="collapse" data-target="#collapse{{id}}"
                            style="cursor: pointer">
                    <h4 class="panel-title">
                        {{#failed}}
                            <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
                        {{/failed}}
                        <span>
                          {{title}}
                        </span>
                        {{#reason}}<span>-- skipped: {{reason}}</span>{{/reason}}
                        {{#screenshots}}
                            {{{screenshot_html}}}
                        {{/screenshots}}
                        {{#journals}}
                            {{{journal_html}}}
                        {{/journals}}
                    </h4>
                </div>
                <div id="collapse{{id}}" class="panel-collapse collapse {{^collapsed}}in{{/collapsed}}">
                    <pre class="panel-body">{{text}}</pre>
                </div>
            </div>
        </script>
        <script id="TextOnly" type="text/template">
            <pre class="panel-body">{{text}}</pre>
        </script>
        <script id="TestProgress" type="text/template">
            <div class="progress" style="width: 40%">
                <div class="progress-bar progress-bar-success" style="width: {{percentage_passed}}%">
                    {{num_passed}}
                    <span class="sr-only">{{percentage_passed}}% Passed</span>
                </div>
                <div class="progress-bar progress-bar-warning" style="width: {{percentage_skipped}}%">
                    {{num_skipped}}
                    <span class="sr-only">{{percentage_skipped}}% Skipped</span>
                </div>
                <div class="progress-bar progress-bar-danger" style="width: {{percentage_failed}}%">
                    {{num_failed}}
                    <span class="sr-only">{{percentage_failed}}% Failed</span>
                </div>
            </div>
        </script>
        <script id="TestingOverview" type="text/template">
            <div id="testing">
                {{total}} tests, {{passed}} passed, {{failed}} failed,
                {{skipped}} skipped, {{left}} to go ({{retries}} retries).<br>

                <span>Failed and skipped tests:</span>
                <ul>
                {{#tests}}
                    {{^entry.passed}}
                        <li
                        {{^entry.skipped}}
                            class="failed"
                        {{/entry.skipped}}
                        >
                        <a href="#{{entry.id}}">
                        {{entry.title}}
                        </a>
                        {{#entry.screenshots}}
                            {{{screenshot_html}}}
                        {{/entry.screenshots}}
                        {{#entry.journals}}
                            {{{journal_html}}}
                        {{/entry.journals}}
                        {{#entry.reason}}<span>-- skipped: {{entry.reason}}</span>{{/entry.reason}}
                        </li>
                    {{/entry.passed}}
                {{/tests}}
                </ul>
            </div>
        </script>
        <script>

var tap_range = /^([0-9]+)\.\.([0-9]+)$/m;
var tap_result = /^(ok|not ok) ([0-9]+) (.*)\)(?: duration: ([0-9]+s))?(?: # SKIP .*)?$$/gm;
var tap_skipped = /^ok [0-9]+ ([^#].*\))(?: duration: ([^#]*))? # SKIP (.*$)/gm;
var image_file = /([A-Za-z0-9-]+)\.(png)$/gm;
var journal_file = /(Journal extracted to )([A-Za-z0-9\-\.]+)\.(log)$/gm;
var test_header_start = "# ----------------------------------------------------------------------"

var entry_template = $("#TestEntry").html();
Mustache.parse(entry_template);
var tests_template = $("#Tests").html();
Mustache.parse(tests_template);
var text_only_template = $("#TextOnly").html();
Mustache.parse(text_only_template);
var progress_template = $("#TestProgress").html();
Mustache.parse(progress_template);
var overview_template = $("#TestingOverview").html();
Mustache.parse(overview_template);
var screenshot_template = $("#ScreenshotLink").html();
Mustache.parse(screenshot_template);
var journal_template = $("#JournalLink").html();
Mustache.parse(journal_template);

function extract(text) {
    var m, s;
    var first, last, total, passed, failed, skipped;
    /* default is to show the text we have, unless we find actual results */
    var altered_text = Mustache.render(text_only_template, {
                    text: text
                });
    var entries = [];
    var indices = {};
    if (m = tap_range.exec(text)) {
        first = parseInt(m[1], 10);
        last = parseInt(m[2], 10);
        total = last-first+1;

        passed = 0;
        failed = 0;
        skipped = 0;
        retries = 0;

        var segments = text.split(test_header_start);
        $('#test-info').text(text.slice(0, text.indexOf('\n')));

        // delete retried segments
        for (s = 0; s < segments.length; s += 1) {
            if (segments[s].indexOf("Retrying due to failure of test harness or framework") == -1)
                continue;
            segments.splice(s, 1);
            s -= 1;
            retries += 1;
        }

        var test_links = {};
        segments.forEach(function (segment, segmentIndex) {
            tap_range.lastIndex = 0;
            tap_result.lastIndex = 0;
            tap_skipped.lastIndex = 0;
            image_file.lastIndex = 0;
            journal_file.lastIndex = 0;
            var entry = { passed: true,
                          skipped: false,
                          screenshots: [],
                          journals: [],
                          text: segment};
            if (m = tap_range.exec(segment)) {
                entry.idx = 0;
                entry.id = "initialization"
                entry.title = entry.id;
                // hide this by default
                // maybe we can have better criteria?
                entry.passed = true;
            } else if (m = tap_result.exec(segment)) {
                entry.idx = m[2];
                entry.id = m[2];
                // our regex cuts this off
                m[3] += ")";
                entry.title = entry.id + ": " + m[3];
                if (m[4])
                    entry.title += ", duration: " + m[4];

                if(m[1] == "ok") {
                    if (m = tap_skipped.exec(segment)) {
                        entry.title = entry.id + ": " + m[1];
                        entry.reason = m[3];
                        entry.skipped = true;
                        entry.passed = false;
                        skipped += 1;
                    } else {
                        passed += 1;
                    }
                } else {
                    entry.passed = false;
                    failed += 1;
                }
            } else {
                // if this isn't the last segment and we don't have a result, treat it as failed
                if (segmentIndex+1 < segments.length) {
                    entry.idx = 8000;
                    entry.id = segment.split("\n")[1].slice(2);
                    entry.title = entry.id;
                    entry.passed = false;
                    failed += 1;
                } else {
                    entry.idx = 10000;
                    entry.id = "in-progress";
                    entry.title = "in progress";
                    entry.passed = true;
                  }
            }
            while (m = image_file.exec(segment)) {
                // we have an image link
                entry.screenshots.push({screenshot_html: Mustache.render(screenshot_template,
                                                                        { screenshot: m[1] + "." + m[2] })
                                       });
            }
            while (m = journal_file.exec(segment)) {
                entry.journals.push({journal_html: Mustache.render(journal_template,
                                                                   { journal: m[2] + "." + m[3] })
                                    });
            }
            entry.failed = !entry.passed && !entry.skipped;
            entry.collapsed = !entry.failed;
            entries.push({ idx: entry.idx, entry: entry, html: Mustache.render(entry_template, entry) });
        });
        entries.sort(function(a, b) {
            a = isNaN(parseInt(a.idx), 10) ? a.idx : parseInt(a.idx, 10);
            b = isNaN(parseInt(b.idx), 10) ? b.idx : parseInt(b.idx, 10);
            return a < b ? -1 : (a > b ? 1 : 0);
        });
        altered_text = Mustache.render(tests_template, { tests: entries });
        // for the overview list, put the failed entries first
        entries.sort(function(a, b) {
                var a_idx = isNaN(parseInt(a.idx, 10)) ? a.idx : parseInt(a.idx, 10);
                var b_idx = isNaN(parseInt(b.idx, 10)) ? b.idx : parseInt(b.idx, 10);
                if (a.entry.skipped == b.entry.skipped)
                    return a_idx < b_idx ? -1 : (a_idx > b_idx ? 1 : 0);
                else if (!a.entry.skipped)
                    return -1;
                else
                    return 1;
            });
        $('#testing').html(Mustache.render(overview_template, { tests: entries,
                                                                passed: passed,
                                                                failed: failed,
                                                                skipped: skipped,
                                                                retries: retries,
                                                                total: total,
                                                                left: total - passed - failed - skipped
                                                              })
                           );
        $('#testing-progress').html(Mustache.render(progress_template,
                                                        { percentage_passed: 100*passed/total,
                                                          percentage_skipped: 100*skipped/total,
                                                          percentage_failed: 100*failed/total,
                                                          num_passed: passed,
                                                          num_skipped: skipped,
                                                          num_failed: failed
                                                        })
                                       );
    } else {
        $('#testing').empty();
        $('#testing-progress').empty();
    }

    return altered_text;
}

var interval_id;

function poll() {
    $.ajax({
        mimeType: 'text/plain; charset=x-user-defined',
        url:      'log',
        type:      'GET',
        dataType:  'text',
        cache:     false,
    }).done(function (text) {
        var amended_text = extract(text);
        $('#log').html(amended_text);
    });

    $.ajax({
        mimeType: 'application/json; charset=x-user-defined',
        url:      'status',
        type:      'GET',
        dataType:  'json',
        cache:     false,
    }).done(function (status) {
        $('#message').text(status.message);
        if ((status.message == "Install failed") ||
            (status.message == "Rebase failed")) {
            $('#testing-progress').html(Mustache.render(progress_template,
                                                    { percentage_passed: 0,
                                                      percentage_skipped: 0,
                                                      percentage_failed: 100,
                                                      num_passed: 0,
                                                      num_skipped: 0,
                                                      num_failed: status.message
                                                    })
                                        );
        }
        $('#status').show();
        clearInterval(interval_id);
    });
}

$(function () {
    interval_id = setInterval(poll, 30000);
    poll();
});

  </script>
</head>
    <body>
        <h3 id="test-info">Logs</h3>
        <p>
            <a href=".">Result directory</a><br>
            <a href="./log">Raw log</a>
        </p>
        <div id="status" style="display:none">
            Done: <span id="message"></span>.
        </div>
        <div id="testing-progress"></div>
        <div id="testing"></div>
        <div id="log"></div>
    </body>
</html>
