Add report tools for report stage at SI pipeline
diff --git a/report-tools/html/css/styles.css b/report-tools/html/css/styles.css
new file mode 100644
index 0000000..58924cb
--- /dev/null
+++ b/report-tools/html/css/styles.css
@@ -0,0 +1,236 @@
+/*##############################################################################
+
+# Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
+
+#
+
+# SPDX-License-Identifier: BSD-3-Clause
+
+##############################################################################*/
+* {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+body {
+ line-height: 1.7em;
+ color: #7f8c8d;
+ font-size: 13px;
+ font-family: SegoeUI-SemiBold-final,Segoe UI Semibold,SegoeUI-Regular-final,Segoe UI,"Segoe UI Web (West European)",Segoe,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,Tahoma,Helvetica,Arial,sans-serif;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+label {
+ color: #34495e;
+ margin: 5px;
+}
+
+.home-menu {
+ padding: 10px;
+ text-align: center;
+ box-shadow: 0 1px 1px rgba(0,0,0, 0.10);
+}
+.home-menu {
+ background: #0091bd;
+ font-weight: 600;
+ font-family: SegoeUI-SemiBold-final,Segoe UI Semibold,SegoeUI-Regular-final,Segoe UI,"Segoe UI Web (West European)",Segoe,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,Tahoma,Helvetica,Arial,sans-serif;
+ font-size: 16px;
+ color: white;
+}
+
+.home-menu a {
+ color: #6FBEF3;
+}
+.home-menu li a:hover,
+.home-menu li a:focus {
+ background: none;
+ border: none;
+ color: #AECFE5;
+}
+
+
+.block-report {
+ font-size: 18px;
+ font-weight: bold;
+ color: #129fea;
+ border: 1px solid #b9560f;
+ padding: 5px;
+ font-weight: 100;
+ border-radius: 5px;
+ line-height: 1em;
+ margin: 10px;
+}
+.block-report div {
+ font-size: 14px;
+ font-weight: bold;
+ padding-bottom: 5px;
+}
+
+.content-wrapper {
+ /* These styles are required for the "scroll-over" effect */
+ position: absolute;
+ top: 10%;
+ width: 100%;
+ min-height: 12%;
+ z-index: 2;
+ background: white;
+
+}
+
+.content {
+ padding: 1em 1em 3em;
+}
+
+.home-menu {
+ text-align: left;
+}
+.home-menu ul {
+ float: right;
+}
+
+.switch {
+ position: relative;
+ display: inline-block;
+ width: 30px;
+ height: 17px;
+}
+
+.switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ -webkit-transition: .4s;
+ transition: .4s;
+}
+
+.slider:before {
+ position: absolute;
+ content: "";
+ height: 13px;
+ width: 13px;
+ left: 2px;
+ bottom: 2px;
+ background-color: white;
+ -webkit-transition: .4s;
+ transition: .4s;
+}
+
+input:checked + .slider {
+ background-color: #2196F3;
+}
+
+input:focus + .slider {
+ box-shadow: 0 0 1px #2196F3;
+}
+
+input:checked + .slider:before {
+ -webkit-transform: translateX(13px);
+ -ms-transform: translateX(13px);
+ transform: translateX(13px);
+}
+
+.slider.round {
+ border-radius: 17px;
+}
+
+.slider.round:before {
+ border-radius: 50%;
+}
+
+.table-wrap {
+ height: 200px;
+ overflow-y: auto;
+ width: 800px;
+ overflow-x: auto;
+}
+
+.styled-table {
+ border-collapse: collapse;
+ margin: 25px 0;
+ font-size: 0.9em;
+ font-family: sans-serif;
+ min-width: 400px;
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
+}
+
+.styled-table thead tr {
+ background-color: #009879;
+ color: #ffffff;
+ text-align: left;
+}
+
+.styled-table th,
+.styled-table td {
+ padding: 12px 15px;
+}
+
+.styled-table tbody tr {
+ border-bottom: 1px solid #dddddd;
+}
+
+.styled-table tbody tr:nth-of-type(even) {
+ background-color: #f3f3f3;
+}
+
+.styled-table tbody tr:last-of-type {
+ border-bottom: 2px solid #009879;
+}
+
+.styled-table tbody tr.active-row {
+ font-weight: bold;
+ color: #009879;
+}
+
+.styled-table tr:hover td{
+ background-color: #ffff99;
+}
+
+span.link {
+ cursor:pointer;
+ color:blue;
+}
+
+span.item {
+ background: white;
+}
+
+div.item {
+ display: inline-block;
+ letter-spacing: normal;
+ word-spacing: normal;
+ vertical-align: top;
+ text-rendering: auto;
+ padding-right: 10px;
+}
+
+div-item-parent {
+ letter-spacing: -.31em;
+ text-rendering: optimizespeed;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-orient: horizontal;
+ -webkit-box-direction: normal;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+ -ms-flex-line-pack: start;
+ align-content: flex-start;
+ word-spacing: -0.43em;
+}
+
diff --git a/report-tools/html/index.html b/report-tools/html/index.html
new file mode 100644
index 0000000..003c039
--- /dev/null
+++ b/report-tools/html/index.html
@@ -0,0 +1,49 @@
+<!--##############################################################################
+
+# Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
+
+#
+
+# SPDX-License-Identifier: BSD-3-Clause
+
+##############################################################################-->
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="Report Pipeline Template">
+ <link rel="stylesheet" href="css/styles.css">
+ <script type="text/javascript" src="js/reporter.js"></script>
+</head>
+<body>
+ <div>
+ <div class="home-menu">
+ <a href="" target="_blank" id="linkLogo"><img id="imgLogo"></a>
+ <span style="padding:10px" id="spanTitle"></span>
+ </div>
+ </div>
+
+
+</div>
+ <div class="content-wrapper">
+ <div class="content" id="report-content">
+ <h2 id="report-name"></h2>
+ </div>
+ </div>
+ <script>
+(function() {
+ document.title = (typeof title === 'undefined') ? 'Report' : title;
+ document.getElementById("spanTitle").innerHTML = document.title
+ document.getElementById("linkLogo").href = (typeof logo_href === 'undefined') ? '' : logo_href;
+ if (typeof logo_img !== 'undefined') {
+ document.getElementById("imgLogo").src = logo_img;
+ }
+ else {
+ document.getElementById("imgLogo").style.display = "none";
+ }
+ report = generateReport("main_container", textReport);
+})();
+ </script>
+</body>
+</html>
diff --git a/report-tools/html/js/reporter.js b/report-tools/html/js/reporter.js
new file mode 100644
index 0000000..410cf5d
--- /dev/null
+++ b/report-tools/html/js/reporter.js
@@ -0,0 +1,238 @@
+/*##############################################################################
+
+# Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
+
+#
+
+# SPDX-License-Identifier: BSD-3-Clause
+
+##############################################################################*/
+const capitalize = (s) => {
+ if (typeof s !== 'string') return ''
+ return s.charAt(0).toUpperCase() + s.slice(1)
+}
+
+function isValidHttpUrl(str) {
+ var regex = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/;
+ if(!regex .test(str)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+const slideDown = element => element.style.height = `${element.scrollHeight}px`;
+
+function show(elemId) {
+ let elem = document.getElementById(elemId)
+
+ if (elem.style.display == "none") {
+ elem.style.display = "block"
+ setTimeout(function() {elem.classList.toggle('hide')}, 50)
+ } else {
+ elem.classList.toggle('hide')
+ setTimeout(function() {elem.style.display = "none"}, 750)
+ }
+
+}
+
+var counter = 0
+
+class TableReport {
+
+ constructor(data, summary) {
+ this.data = data
+ this.summary = summary
+ this.container = document.createElement("div")
+ this.header = document.createElement("div")
+ this.container.classList.add("table-wrap")
+ this.table = document.createElement("table")
+ this.table.classList.add("styled-table")
+ this.container.appendChild(this.header)
+ this.container.appendChild(this.table)
+ this.generateHeader()
+ this.generateTable() // generate the table first
+ this.generateTableHead() // then the head
+ }
+
+ generateHeader() {
+ this.header.innerHTML = "<h2>Test results: " + this.summary["total"] + " tests, " +
+ "<label style='color:green'>" + this.summary["pass"] + " passed,</label> " +
+ "<label style='color:red'>" + this.summary["fail"] +" failed</label></h2>"
+ }
+
+ generateTableHead() {
+ let table = this.table
+ let thead = table.createTHead();
+ let row = thead.insertRow();
+ for (let key in this.data[0]) {
+ let th = document.createElement("th");
+ let text = document.createTextNode(capitalize(key));
+ th.appendChild(text);
+ row.appendChild(th);
+ }
+ }
+
+ generateTable() {
+ let table = this.table
+ for (let element of this.data) {
+ let row = table.insertRow();
+ for (let key in element) {
+ let cell = row.insertCell();
+ let text = document.createTextNode(element[key]);
+ cell.appendChild(text);
+ }
+ }
+ }
+}
+
+class Report {
+
+ constructor(reportObj) {
+ this.reportObj = reportObj
+ this.reportName = Object.keys(this.reportObj)[0]
+ this.report = this.reportObj[this.reportName]
+ this.testSuites = this.report["test-suites"]
+ this.testAssets = this.report["test-config"]["test-assets"]
+ this.metadata = this.report["metadata"]
+ this.target = this.report["target"]
+ this.testConfig = this.report["test-config"]
+ this.testEnvironments = this.report["test-environments"]
+ this.testSuitesData = {}
+ }
+
+ generateSuitesTables() {
+ this.suitesDivs = {}
+ var results = []
+ var index = 0
+ for (const [testSuiteName, testSuite] of Object.entries(this.testSuites)) {
+ ++index
+ results = []
+ var status = "PASS"
+ var failCount = 0
+ var passCount = 0
+ var counter = 0
+ var metCols = []
+ for (const [testResultName, testResult] of Object.entries(testSuite["test-results"])) {
+ results.push({name: testResultName, status: testResult["status"],
+ ...testResult["metadata"]})
+ if (testResult["status"] == "FAIL") {
+ status = "FAIL"
+ failCount++
+ } else if (testResult["status"] == "PASS") {
+ passCount++
+ }
+ metCols = Object.keys(testResult["metadata"])
+ ++counter
+ }
+ let summary = {"pass": passCount, "fail": failCount, "total": counter}
+ var tableReport = new TableReport(results, summary)
+ this.testSuitesData[testSuiteName] = {tableReport: tableReport, metadata: testSuite['metadata']}
+ }
+ }
+}
+
+function link(url) {
+ window.open(url, '_blank');
+}
+
+function generateItems(obj, container) {
+ let i = 0
+ let click=""
+ for (var [name, value] of Object.entries(obj)) {
+ if ((i++ % 3) == 0) {
+ divGroup = document.createElement("div")
+ divGroup.classList.add("item-parent")
+ container.appendChild(divGroup)
+ }
+ divElem = document.createElement("div")
+ divElem.classList.add("item")
+ style = ' class=item'
+ if (isValidHttpUrl(value))
+ style = ' class="link" onclick=link("' + value + '")'
+ divElem.innerHTML = "<span style='color:black'>" + name + ": </span>" + "<span" + style +">" + value + "</span>"
+ divGroup.appendChild(divElem)
+ }
+}
+
+function generateBlock(obj, title, expanded, extra) {
+ var divBlock = 0
+ var divGroup = 0
+ var divElem = 0
+ var divData = 0
+
+ if (expanded === undefined)
+ expanded = true
+ let id = title.replace(/\s+/g, '-') + counter++
+ let checked = expanded ? "checked" : ""
+ divBlock = document.createElement("div")
+ divBlock.classList.add("block-report")
+ let divTitle = document.createElement("div")
+ divTitle.innerHTML = '<label>' + title + '</div>' +
+ '<label class="switch"><input ' +
+ '" type="checkbox" ' + checked + ' onclick=' +
+ "show('data-" + id + "')>" +
+ '<span class="slider"></span></label>'
+ divBlock.appendChild(divTitle)
+ if (obj.tagName == 'DIV')
+ divData = obj
+ else
+ divData = document.createElement("div")
+ divData.id = "data-" + id
+ divData.classList.add("box")
+ divBlock.setAttribute('data-items-container', divData.id)
+ if (expanded == false) {
+ divData.style.display = 'none'
+ divData.classList.add("hide")
+ }
+
+ divBlock.appendChild(divData)
+ generateItems(obj, divData)
+ if (!(extra === undefined))
+ divData.appendChild(extra)
+ return divBlock
+}
+
+function generateReport(containerName, yamlString) {
+ const jsObject = JSON.parse(yamlString)
+ const report = new Report(jsObject)
+ document.getElementById("report-name").innerHTML = report.reportName
+ var divContent = document.getElementById("report-content")
+
+ var metadata = generateBlock(report.metadata, "Metadata", false)
+ divContent.appendChild(metadata)
+
+ divContent.appendChild(generateBlock(report.target, "Target", false))
+
+ var div = document.createElement("div")
+ divAssets = generateBlock(div, "Test assets", false)
+ for (const [name, data] of Object.entries(report.testConfig['test-assets'])) {
+ var divAsset = generateBlock(data, name, false)
+ div.appendChild(divAsset)
+ }
+
+ divConfig = generateBlock(divAssets, "Test configuration", false)
+ divContent.appendChild(divConfig)
+
+ div = document.createElement("div")
+ divEnvs = generateBlock(div, "Test environments", false)
+ for (const [name, data] of Object.entries(report.testEnvironments)) {
+ var divEnv = generateBlock(data, name, false)
+ div.appendChild(divEnv)
+ }
+ divContent.appendChild(divEnvs)
+
+ div = document.createElement("div")
+ divSuites = generateBlock(div, "Test suites")
+ report.generateSuitesTables()
+ var visible = true
+ for (const [name, suiteData] of Object.entries(report.testSuitesData)) {
+ var divSuite = generateBlock(suiteData['metadata'], name, visible, suiteData['tableReport'].container)
+ div.appendChild(divSuite)
+ if (visible)
+ visible = false
+ }
+
+ divContent.appendChild(divSuites)
+ return report
+}