1
+ <!DOCTYPE html>
2
+ < html lang ="en ">
3
+
4
+ < head >
5
+ < meta charset ="UTF-8 ">
6
+ < title > Petri test results</ title >
7
+ < style type ="text/css ">
8
+ body {
9
+ font-family : monospace;
10
+ font-size : 14px ;
11
+ }
12
+ </ style >
13
+ < script >
14
+ const baseUrl = "https://openvmmghtestresults.blob.core.windows.net/results" ;
15
+
16
+ const cross = "❌" ; // Cross for failed tests
17
+ const check = "✅" ; // Check for passed tests
18
+
19
+ function parseBlobs ( xmlText ) {
20
+ const parser = new DOMParser ( ) ;
21
+ const xmlDoc = parser . parseFromString ( xmlText , "text/xml" ) ;
22
+ const blobs = xmlDoc . getElementsByTagName ( "Blob" ) ;
23
+ let blobNames = [ ] ;
24
+ for ( const blob of blobs ) {
25
+ const name = blob . getElementsByTagName ( "Name" ) [ 0 ] . textContent ;
26
+ const date = new Date ( blob . getElementsByTagName ( "Creation-Time" ) [ 0 ] . textContent ) ;
27
+ let metadata = { } ;
28
+ for ( const meta of blob . getElementsByTagName ( "Metadata" ) ) {
29
+ const child = meta . children [ 0 ] ;
30
+ metadata [ child . tagName ] = child . textContent ;
31
+ }
32
+ blobNames . push ( {
33
+ name : name ,
34
+ creationTime : date ,
35
+ metadata : metadata ,
36
+ } ) ;
37
+ }
38
+ return blobNames ;
39
+ }
40
+
41
+ // Get the blob list, which is in XML via a GET request.
42
+ function getTestList ( runName ) {
43
+ const url = `${ baseUrl } ?restype=container&comp=list&showonly=files&prefix=${ encodeURIComponent ( runName ) } ` ;
44
+ fetch ( url )
45
+ . then ( response => response . text ( ) )
46
+ . then ( data => {
47
+ let blobs = parseBlobs ( data ) ;
48
+ let run = { } ;
49
+ for ( const blob of blobs ) {
50
+ const nameParts = blob . name . split ( "/" ) ;
51
+ let fileName = nameParts [ nameParts . length - 1 ] ;
52
+ let failed ;
53
+ if ( fileName === "petri.passed" ) {
54
+ failed = false ;
55
+ } else if ( fileName === "petri.failed" ) {
56
+ failed = true ;
57
+ } else {
58
+ continue ; // Not a test result file.
59
+ }
60
+ const testName = nameParts [ nameParts . length - 2 ] ;
61
+ const jobName = nameParts [ nameParts . length - 3 ] ;
62
+ const path = nameParts . slice ( 0 , - 3 ) . join ( "/" ) ;
63
+ const url = `test.html?run=${ path } &job=${ jobName } &test=${ testName } ` ;
64
+ if ( ! run [ jobName ] ) {
65
+ run [ jobName ] = {
66
+ failed : false ,
67
+ tests : [ ] ,
68
+ } ;
69
+ }
70
+ let job = run [ jobName ] ;
71
+ job . failed |= failed ;
72
+ job . tests . push ( {
73
+ name : testName ,
74
+ url : url ,
75
+ failed : failed ,
76
+ } ) ;
77
+ }
78
+
79
+ let failedHtml = "" ;
80
+ let passingHtml = "" ;
81
+
82
+ for ( const job in run ) {
83
+ run [ job ] . tests . sort ( ( a , b ) => {
84
+ if ( a . failed !== b . failed ) {
85
+ return a . failed ? - 1 : 1 ; // Failed tests first.
86
+ }
87
+ return a . name . localeCompare ( b . name ) ; // Then by name.
88
+ } ) ;
89
+ let thisHtml = `<li>${ job } <ul>` ;
90
+ for ( const test of run [ job ] . tests ) {
91
+ let icon = test . failed ? cross : check ;
92
+ thisHtml += `<li><a href="${ test . url } ">${ icon } ${ test . name } </a></li>` ;
93
+ }
94
+ thisHtml += "</ul></li>" ;
95
+ if ( run [ job ] . failed ) {
96
+ failedHtml += thisHtml ;
97
+ } else {
98
+ passingHtml += thisHtml ;
99
+ }
100
+ }
101
+
102
+ let html = `<h2>Failed jobs</h2>
103
+ <ul>${ failedHtml } </ul>
104
+ <h2>Passing jobs</h2>
105
+ <ul>${ passingHtml } </ul>` ;
106
+
107
+ document . getElementById ( "runList" ) . innerHTML = html ;
108
+ } )
109
+ . catch ( error => console . error ( 'Error fetching blob list:' , error ) ) ;
110
+ }
111
+
112
+ function getRunList ( ) {
113
+ const url = `${ baseUrl } ?restype=container&comp=list&showonly=files&include=metadata&prefix=runs/` ;
114
+ fetch ( url )
115
+ . then ( response => response . text ( ) )
116
+ . then ( data => {
117
+ const blobs = parseBlobs ( data ) ;
118
+ const runs = blobs . map ( blob => {
119
+ // Remove runs/ prefix.
120
+ return {
121
+ name : blob . name . replace ( / ^ r u n s \/ / , '' ) ,
122
+ creationTime : blob . creationTime ,
123
+ failed : blob . metadata [ "petrifailed" ] ,
124
+ } ;
125
+ } ) ;
126
+ runs . sort ( ( a , b ) => b . creationTime - a . creationTime ) ; // Sort by creation time, newest first.
127
+ let html = `<table>
128
+ <thead>
129
+ <tr>
130
+ <th>Time</th>
131
+ <th>Run</th>
132
+ <th>Failed</th>
133
+ </tr>
134
+ </thead>
135
+ <tbody>` ;
136
+ for ( const run of runs ) {
137
+ const marker = run . failed > 0 ? cross : check ;
138
+ html += `<tr>
139
+ <td>${ run . creationTime . toLocaleString ( ) } </td>
140
+ <td><a href="?run=${ encodeURIComponent ( run . name ) } ">${ run . name } ${ marker } </a></td>
141
+ <td>${ run . failed } </td>
142
+ </tr>` ;
143
+ }
144
+ html += "</table>" ;
145
+ if ( runs . length === 0 ) {
146
+ html = "No runs found." ;
147
+ }
148
+ document . getElementById ( "runList" ) . innerHTML = html ;
149
+ } )
150
+ . catch ( error => console . error ( 'Error fetching run list:' , error ) ) ;
151
+ }
152
+
153
+ window . onload = function ( ) {
154
+ const urlParams = new URLSearchParams ( window . location . search ) ;
155
+ const run = urlParams . get ( 'run' ) ;
156
+ document . getElementById ( "runList" ) . innerText = "Loading..." ;
157
+ if ( run ) {
158
+ document . getElementById ( "runName" ) . innerText = run ;
159
+ document . getElementById ( "backToRuns" ) . innerHTML = `<a href="?">All runs</a>` ;
160
+ getTestList ( run ) ;
161
+ } else {
162
+ document . getElementById ( "runName" ) . innerText = "Runs" ;
163
+ getRunList ( ) ;
164
+ }
165
+ } ;
166
+ </ script >
167
+ </ head >
168
+
169
+ < body >
170
+ < h1 id ="runName "> Loading</ h1 >
171
+ < div id ="backToRuns "> </ div >
172
+ < div id ="runList "> </ div >
173
+ </ body >
174
+
175
+ </ html >
0 commit comments