379 lines
16 KiB
HTML
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>
|