From e4c1fb3ea72417cf0e253556eefba926905e80b7 Mon Sep 17 00:00:00 2001
From: Eelco Dolstra <eelco.dolstra@logicblox.com>
Date: Sun, 9 Jan 2011 17:58:52 +0000
Subject: [PATCH] * Pretty-print the VM build log and publish it as a build
 product.

svn path=/nixos/trunk/; revision=25468
---
 lib/test-driver/log2html.xsl   | 117 +++++++++++++++++++++++++++++++++
 lib/test-driver/logfile.css    | 110 +++++++++++++++++++++++++++++++
 lib/test-driver/test-driver.pl |   6 +-
 lib/test-driver/treebits.js    |  40 +++++++++++
 lib/testing.nix                |   9 ++-
 5 files changed, 279 insertions(+), 3 deletions(-)
 create mode 100644 lib/test-driver/log2html.xsl
 create mode 100644 lib/test-driver/logfile.css
 create mode 100644 lib/test-driver/treebits.js

diff --git a/lib/test-driver/log2html.xsl b/lib/test-driver/log2html.xsl
new file mode 100644
index 00000000000..cf7f8f8c057
--- /dev/null
+++ b/lib/test-driver/log2html.xsl
@@ -0,0 +1,117 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+  <xsl:output method='html' encoding="UTF-8"
+              doctype-public="-//W3C//DTD HTML 4.01//EN"
+              doctype-system="http://www.w3.org/TR/html4/strict.dtd" />
+
+  <xsl:template match="logfile">
+    <html>
+      <head>
+        <script type="text/javascript" src="jquery.min.js"></script> 
+        <script type="text/javascript" src="jquery-ui.min.js"></script>
+        <script type="text/javascript" src="treebits.js" />
+        <link rel="stylesheet" href="logfile.css" type="text/css" />
+        <title>Log File</title>
+      </head>
+      <body>
+        [<a href="javascript:" class="logTreeExpandAll">Expand all</a>]
+        [<a href="javascript:" class="logTreeCollapseAll">Collapse all</a>]
+        <ul class='toplevel'>
+          <xsl:for-each select='line|nest'>
+            <li>
+              <xsl:apply-templates select='.'/>
+            </li>
+          </xsl:for-each>
+        </ul>
+      </body>
+    </html>
+  </xsl:template>
+
+  
+  <xsl:template match="nest">
+
+    <!-- The tree should be collapsed by default if all children are
+         unimportant or if the header is unimportant. -->
+    <xsl:variable name="collapsed" select="not(./head[@expanded]) and count(.//*[@error]) = 0"/>
+                  
+    <xsl:variable name="style"><xsl:if test="$collapsed">display: none;</xsl:if></xsl:variable>
+
+    <xsl:if test="line|nest">
+      <a href="javascript:" class="logTreeToggle"></a>
+      <xsl:text> </xsl:text>
+    </xsl:if>
+    
+    <xsl:apply-templates select='head'/>
+
+    <!-- Be careful to only generate <ul>s if there are <li>s, otherwise it’s malformed. -->
+    <xsl:if test="line|nest">
+      
+      <ul class='nesting' style="{$style}">
+        <xsl:for-each select='line|nest'>
+
+          <!-- Is this the last line?  If so, mark it as such so that it
+               can be rendered differently. -->
+          <xsl:variable  name="class"><xsl:choose><xsl:when test="position() != last()">line</xsl:when><xsl:otherwise>lastline</xsl:otherwise></xsl:choose></xsl:variable>
+        
+          <li class='{$class}'>
+            <span class='lineconn' />
+            <span class='linebody'>
+              <xsl:apply-templates select='.'/>
+            </span>
+          </li>
+        </xsl:for-each>
+      </ul>
+    </xsl:if>
+    
+  </xsl:template>
+
+  
+  <xsl:template match="head|line">
+    <code>
+      <xsl:if test="@error">
+        <xsl:attribute name="class">errorLine</xsl:attribute>
+      </xsl:if>
+      <xsl:if test="@warning">
+        <xsl:attribute name="class">warningLine</xsl:attribute>
+      </xsl:if>
+      <xsl:if test="@priority = 3">
+        <xsl:attribute name="class">prio3</xsl:attribute>
+      </xsl:if>
+
+      <xsl:if test="@type = 'serial'">
+        <xsl:attribute name="class">serial</xsl:attribute>
+      </xsl:if>
+      
+      <xsl:if test="@machine">
+        <xsl:choose>
+          <xsl:when test="@type = 'serial'">
+            <span class="machine"><xsl:value-of select="@machine"/># </span>
+          </xsl:when>
+          <xsl:otherwise>
+            <span class="machine"><xsl:value-of select="@machine"/>: </span>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+      
+      <xsl:choose>
+        <xsl:when test="@image">
+          <a href="{@image}"><xsl:apply-templates/></a>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:apply-templates/>
+        </xsl:otherwise>
+      </xsl:choose>        
+    </code>
+  </xsl:template>
+
+  
+  <xsl:template match="storeref">
+    <em class='storeref'>
+      <span class='popup'><xsl:apply-templates/></span>
+      <span class='elided'>/...</span><xsl:apply-templates select='name'/><xsl:apply-templates select='path'/>
+    </em>
+  </xsl:template>
+  
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/lib/test-driver/logfile.css b/lib/test-driver/logfile.css
new file mode 100644
index 00000000000..a7f83b4c925
--- /dev/null
+++ b/lib/test-driver/logfile.css
@@ -0,0 +1,110 @@
+body {
+    font-family: sans-serif;
+    background: white;
+}
+
+
+ul.nesting, ul.toplevel {
+    padding: 0;
+    margin: 0;
+}
+
+ul.toplevel {
+    list-style-type: none;
+}
+
+.line, .head {
+    padding-top: 0em;
+}
+
+ul.nesting li.line, ul.nesting li.lastline {
+    position: relative;
+    list-style-type: none;
+}
+
+ul.nesting li.line {
+    padding-left: 2.0em;
+}
+
+ul.nesting li.lastline {
+    padding-left: 2.1em; /* for the 0.1em border-left in .lastline > .lineconn */
+}
+
+li.line {
+    border-left: 0.1em solid #6185a0;
+}
+
+li.line > span.lineconn, li.lastline > span.lineconn {
+    position: absolute;
+    height: 0.65em;
+    left: 0em;
+    width: 1.5em;
+    border-bottom: 0.1em solid #6185a0;
+}
+
+li.lastline > span.lineconn {
+    border-left: 0.1em solid #6185a0;
+}
+
+
+em.storeref {
+    color: #500000;
+    position: relative; 
+    width: 100%;
+}
+
+em.storeref:hover {
+    background-color: #eeeeee;
+}
+
+*.popup {
+    display: none;
+/*    background: url('http://losser.st-lab.cs.uu.nl/~mbravenb/menuback.png') repeat; */
+    background: #ffffcd;
+    border: solid #555555 1px;
+    position: absolute;
+    top: 0em;
+    left: 0em;
+    margin: 0;
+    padding: 0;
+    z-index: 100;
+}
+
+em.storeref:hover span.popup {
+    display: inline;
+    width: 40em;
+}
+
+
+.logTreeToggle {
+    text-decoration: none;
+    font-family: monospace;
+    font-size: larger;
+}
+
+.errorLine {
+    color: #ff0000;
+    font-weight: bold;
+}
+
+.warningLine {
+    color: darkorange;
+    font-weight: bold;
+}
+
+.prio3 {
+    font-style: italic;
+}
+
+code {
+    white-space: pre-wrap;
+}
+
+.serial {
+    color: #56115c;
+}
+
+.machine {
+    color: #002399;
+    font-style: italic;
+}
diff --git a/lib/test-driver/test-driver.pl b/lib/test-driver/test-driver.pl
index db92a951b16..0ccde63fc3e 100644
--- a/lib/test-driver/test-driver.pl
+++ b/lib/test-driver/test-driver.pl
@@ -60,8 +60,10 @@ sub subtest {
 
 sub runTests {
     if (defined $ENV{tests}) {
-        eval "$context $ENV{tests}";
-        die $@ if $@;
+        $log->nest("running the VM test script", sub {
+            eval "$context $ENV{tests}";
+            die $@ if $@;
+        }, { expanded => 1 });
     } else {
         my $term = Term::ReadLine->new('nixos-vm-test');
         $term->ReadHistory;
diff --git a/lib/test-driver/treebits.js b/lib/test-driver/treebits.js
new file mode 100644
index 00000000000..d88ac075fd9
--- /dev/null
+++ b/lib/test-driver/treebits.js
@@ -0,0 +1,40 @@
+$(document).ready(function() {
+
+    /* Set the appearance of the toggle depending on whether the
+       corresponding subtree is initially shown or hidden. */
+    $(".logTreeToggle").map(function() {
+        if ($(this).siblings("ul:hidden").length == 0) {
+            $(this).text("-");
+        } else {
+            $(this).text("+");
+        }
+    });
+
+    /* When a toggle is clicked, show or hide the subtree. */
+    $(".logTreeToggle").click(function() {
+        if ($(this).siblings("ul:hidden").length != 0) {
+            $(this).siblings("ul").show();
+            $(this).text("-");
+        } else {
+            $(this).siblings("ul").hide();
+            $(this).text("+");
+        }
+    });
+
+    /* Implementation of the expand all link. */
+    $(".logTreeExpandAll").click(function() {
+        $(".logTreeToggle", $(this).siblings(".toplevel")).map(function() {
+            $(this).siblings("ul").show();
+            $(this).text("-");
+        });
+    });
+
+    /* Implementation of the collapse all link. */
+    $(".logTreeCollapseAll").click(function() {
+        $(".logTreeToggle", $(this).siblings(".toplevel")).map(function() {
+            $(this).siblings("ul").hide();
+            $(this).text("+");
+        });
+    });
+
+});
diff --git a/lib/testing.nix b/lib/testing.nix
index f02d4de251c..7a0e9ea4347 100644
--- a/lib/testing.nix
+++ b/lib/testing.nix
@@ -45,7 +45,7 @@ rec {
       
       inherit tests;
       
-      buildInputs = [ pkgs.qemu_kvm ];
+      buildInputs = [ pkgs.qemu_kvm pkgs.libxslt ];
 
       buildCommand =
         ''
@@ -58,7 +58,14 @@ rec {
             mv $i $out/coverage-data/$(dirname $i)
           done
 
+          xsltproc --output $out/log.html ${./test-driver/log2html.xsl} $out/log.xml
+          ln -s ${./test-driver/logfile.css} $out/logfile.css
+          ln -s ${./test-driver/treebits.js} $out/treebits.js
+          ln -s ${pkgs.jquery_ui}/js/jquery.min.js $out/
+          ln -s ${pkgs.jquery_ui}/js/jquery-ui.min.js $out/
+
           touch $out/nix-support/hydra-build-products
+          echo "report testlog $i" >> $out/nix-support/hydra-build-products
 
           for i in $out/*.png; do
             echo "report screenshot $i" >> $out/nix-support/hydra-build-products