starter-kit/bots/task/log.html
2019-08-22 16:05:13 +00:00

379 lines
16 KiB
HTML

<!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";
}
body {
margin: 10px;
}
.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.retried {
background-color: #f7bd7f;
border-color: #b35c00;
}
.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}}
{{#retried}}retried{{/retried}}
{{#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}}
{{#retried}}
<span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span>
{{/retried}}
<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 retried tests:</span>
<ul>
{{#tests}}
{{#entry.interesting}}
<li
{{^entry.retried}}
class="failed"
{{/entry.retried}}
>
<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.interesting}}
{{/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')));
var test_links = {};
var ids = { };
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,
retried: false,
interesting: 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];
r = 0;
while (ids[entry.id]) {
r += 1;
entry.id = m[2] + "-" + r;
}
ids[entry.id] = true;
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 if (segment.indexOf("# RETRY") !== -1) {
retries += 1;
entry.passed = true;
entry.retried = true;
entry.interesting = true;
} else {
entry.passed = false;
entry.interesting = true;
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>