RELNOTES: remove webstatusserver (--use_webstatusserver). 

It seems unused, and the Bazel dashboard is more useful, as can persist results.

--
MOS_MIGRATED_REVID=104085798
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index 641b60c..bda1384 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -340,10 +340,6 @@
   } else {
     result.push_back("--nofatal_event_bus_exceptions");
   }
-  if (globals->options.webstatus_port) {
-    result.push_back("--use_webstatusserver=" + \
-                     ToString(globals->options.webstatus_port));
-  }
 
   // This is only for Blaze reporting purposes; the real interpretation of the
   // jvm flags occurs when we set up the java command line.
diff --git a/src/main/java/BUILD b/src/main/java/BUILD
index 0748fcc..84cbf0b 100644
--- a/src/main/java/BUILD
+++ b/src/main/java/BUILD
@@ -524,25 +524,6 @@
 )
 
 java_library(
-    name = "webstatusserver",
-    srcs = glob([
-        "com/google/devtools/build/lib/webstatusserver/**/*.java",
-    ]),
-    deps = [
-        ":analysis-exec-rules-skyframe",
-        ":buildtool-runtime",
-        ":clock",
-        ":events",
-        ":options",
-        ":packages",
-        ":util",
-        "//src/main/protobuf:proto_test_status",
-        "//third_party:gson",
-        "//third_party:guava",
-    ],
-)
-
-java_library(
     name = "bazel-core",
     srcs = glob(
         [
@@ -601,7 +582,6 @@
         ":unix",
         ":util",
         ":vfs",
-        ":webstatusserver",
         "//src/java_tools/singlejar:zip",
         "//src/main/java/com/google/devtools/build/lib/bazel/dash",
         "//src/main/java/com/google/devtools/build/lib/sandbox",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java
index 6aea6e4..2b29f2d 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java
@@ -48,8 +48,7 @@
           com.google.devtools.build.lib.worker.WorkerModule.class,
           com.google.devtools.build.lib.standalone.StandaloneModule.class,
           com.google.devtools.build.lib.sandbox.SandboxModule.class,
-          com.google.devtools.build.lib.runtime.BuildSummaryStatsModule.class,
-          com.google.devtools.build.lib.webstatusserver.WebStatusServerModule.class);
+          com.google.devtools.build.lib.runtime.BuildSummaryStatsModule.class);
 
   public static void main(String[] args) {
     BlazeVersionInfo.setBuildInfo(tryGetBuildInfo());
diff --git a/src/main/java/com/google/devtools/build/lib/rules/test/StandaloneTestStrategy.java b/src/main/java/com/google/devtools/build/lib/rules/test/StandaloneTestStrategy.java
index 1d1e101..eb1fde5 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/test/StandaloneTestStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/test/StandaloneTestStrategy.java
@@ -54,10 +54,12 @@
 
   private final Path workspace;
 
-  public StandaloneTestStrategy(OptionsClassProvider requestOptions,
-      OptionsClassProvider startupOptions, BinTools binTools, Map<String, String> clientEnv,
+  public StandaloneTestStrategy(
+      OptionsClassProvider requestOptions,
+      BinTools binTools,
+      Map<String, String> clientEnv,
       Path workspace) {
-    super(requestOptions, startupOptions, binTools, clientEnv);
+    super(requestOptions, binTools, clientEnv);
     this.workspace = workspace;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/test/TestStrategy.java b/src/main/java/com/google/devtools/build/lib/rules/test/TestStrategy.java
index cc2c43b..329e995 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/test/TestStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/test/TestStrategy.java
@@ -29,7 +29,6 @@
 import com.google.devtools.build.lib.exec.SymlinkTreeHelper;
 import com.google.devtools.build.lib.profiler.Profiler;
 import com.google.devtools.build.lib.profiler.ProfilerTask;
-import com.google.devtools.build.lib.runtime.BlazeServerStartupOptions;
 import com.google.devtools.build.lib.util.ShellEscaper;
 import com.google.devtools.build.lib.util.io.FileWatcher;
 import com.google.devtools.build.lib.util.io.OutErr;
@@ -129,20 +128,17 @@
 
   // Used for generating unique temporary directory names.
   private final AtomicInteger tmpIndex = new AtomicInteger(0);
-  private final boolean statusServerRunning;
   protected final ImmutableMap<String, String> clientEnv;
   protected final ExecutionOptions executionOptions;
   protected final BinTools binTools;
 
-  public TestStrategy(OptionsClassProvider requestOptionsProvider,
-      OptionsClassProvider startupOptionsProvider, BinTools binTools,
+  public TestStrategy(
+      OptionsClassProvider requestOptionsProvider,
+      BinTools binTools,
       Map<String, String> clientEnv) {
     this.executionOptions = requestOptionsProvider.getOptions(ExecutionOptions.class);
     this.binTools = binTools;
     this.clientEnv = ImmutableMap.copyOf(clientEnv);
-    BlazeServerStartupOptions startupOptions =
-        startupOptionsProvider.getOptions(BlazeServerStartupOptions.class);
-    statusServerRunning = startupOptions != null && startupOptions.useWebStatusServer > 0;
   }
 
   @Override
@@ -315,8 +311,8 @@
   @Nullable
   protected TestCase parseTestResult(Path resultFile) {
     /* xml files. We avoid parsing it unnecessarily, since test results can potentially consume
-       a large amount of memory. */
-    if (executionOptions.testSummary != TestSummaryFormat.DETAILED && !statusServerRunning) {
+     a large amount of memory. */
+    if (executionOptions.testSummary != TestSummaryFormat.DETAILED) {
       return null;
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeServerStartupOptions.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeServerStartupOptions.java
index 1e82d8f..244cb70 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeServerStartupOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeServerStartupOptions.java
@@ -235,9 +235,4 @@
           + "changes instead of scanning every file for a change.")
   public boolean watchFS;
 
-  @Option(name = "use_webstatusserver",
-      defaultValue = "0",
-      category = "server startup",
-      help = "Specifies port to run web status server on (0 to disable, which is default).")
-  public int useWebStatusServer;
 }
diff --git a/src/main/java/com/google/devtools/build/lib/standalone/StandaloneActionContextProvider.java b/src/main/java/com/google/devtools/build/lib/standalone/StandaloneActionContextProvider.java
index 430f266..b1d2fa6 100644
--- a/src/main/java/com/google/devtools/build/lib/standalone/StandaloneActionContextProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/standalone/StandaloneActionContextProvider.java
@@ -69,9 +69,9 @@
     BlazeRuntime runtime = env.getRuntime();
     boolean verboseFailures = buildRequest.getOptions(ExecutionOptions.class).verboseFailures;
 
-    TestActionContext testStrategy = new StandaloneTestStrategy(buildRequest,
-        runtime.getStartupOptionsProvider(), runtime.getBinTools(), env.getClientEnv(),
-        runtime.getWorkspace());
+    TestActionContext testStrategy =
+        new StandaloneTestStrategy(
+            buildRequest, runtime.getBinTools(), env.getClientEnv(), runtime.getWorkspace());
 
     Builder<ActionContext> strategiesBuilder = ImmutableList.builder();
 
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/IndexPageHandler.java b/src/main/java/com/google/devtools/build/lib/webstatusserver/IndexPageHandler.java
deleted file mode 100644
index 757fa95..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/IndexPageHandler.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.webstatusserver;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonObject;
-
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpServer;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Handlers for displaying the index page of server.
- *
- */
-public class IndexPageHandler {
-  private List<TestStatusHandler> testHandlers = new ArrayList<>();
-  private IndexPageJsonData dataHandler;
-  private StaticResourceHandler frontendHandler;
-
-  public IndexPageHandler(HttpServer server, List<TestStatusHandler> testHandlers) {
-    this.testHandlers = testHandlers;
-    this.dataHandler = new IndexPageJsonData(this);
-    this.frontendHandler =
-        StaticResourceHandler.createFromRelativePath("static/index.html", "text/html");
-    server.createContext("/", frontendHandler);
-    server.createContext("/tests/list", dataHandler);
-  }
-
-  /**
-   * Puts data from the build log into json suitable for frontend.
-   * 
-   */
-  private class IndexPageJsonData implements HttpHandler {
-    private IndexPageHandler pageHandler;
-    private Gson gson = new Gson();
-    public IndexPageJsonData(IndexPageHandler indexPageHandler) {
-      this.pageHandler = indexPageHandler;
-    }
-
-    @Override
-    public void handle(HttpExchange exchange) throws IOException {
-      exchange.getResponseHeaders().put("Content-Type", ImmutableList.of("application/json"));
-      JsonArray response = new JsonArray();
-      for (TestStatusHandler handler : this.pageHandler.testHandlers) {  
-        WebStatusBuildLog buildLog = handler.getBuildLog();
-        JsonObject test = new JsonObject();
-        test.add("targets",  gson.toJsonTree(buildLog.getTargetList()));
-        test.addProperty("startTime", buildLog.getStartTime());
-        test.addProperty("finished", buildLog.finished());
-        test.addProperty("uuid", buildLog.getCommandId().toString());
-        response.add(test);
-      }
-      String serializedResponse = response.toString();
-      exchange.sendResponseHeaders(200, serializedResponse.length());
-      try (OutputStream os = exchange.getResponseBody()) {
-        os.write(serializedResponse.getBytes(StandardCharsets.UTF_8));
-      }
-    }
-  }
-}
-
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/StaticResourceHandler.java b/src/main/java/com/google/devtools/build/lib/webstatusserver/StaticResourceHandler.java
deleted file mode 100644
index 65d12f6..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/StaticResourceHandler.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.webstatusserver;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.io.CharStreams;
-import com.google.devtools.build.lib.util.ResourceFileLoader;
-
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-
-/**
- * Handler for static resources (JS, html, css...)
- */
-public class StaticResourceHandler implements HttpHandler {
-  private String response;
-  private List<String> contentType;
-  private int httpCode;
-
-  public static StaticResourceHandler createFromAbsolutePath(String path, String contentType) {
-    return new StaticResourceHandler(path, contentType, true);
-  }
-
-  public static StaticResourceHandler createFromRelativePath(String path, String contentType) {
-    return new StaticResourceHandler(path, contentType, false);
-  }
-
-  private StaticResourceHandler(String path, String contentType, boolean absolutePath) {
-    try {
-      if (absolutePath) {
-        InputStream resourceStream = loadFromAbsolutePath(WebStatusServerModule.class, path);
-        response = CharStreams.toString(new InputStreamReader(resourceStream));
-
-      } else {
-        response = ResourceFileLoader.loadResource(WebStatusServerModule.class, path);
-      }
-      httpCode = 200;
-    } catch (IOException e) {
-      throw new IllegalArgumentException("resource " + path + " not found");
-    }
-    this.contentType = ImmutableList.of(contentType);
-  }
-
-  @Override
-  public void handle(HttpExchange exchange) throws IOException {
-    exchange.getResponseHeaders().put("Content-Type", contentType);
-    exchange.sendResponseHeaders(httpCode, response.length());
-    try (OutputStream os = exchange.getResponseBody()) {
-      os.write(response.getBytes(StandardCharsets.UTF_8));
-    }
-  }
-
-  public static InputStream loadFromAbsolutePath(Class<?> loadingClass, String path)
-      throws IOException {
-    URL resourceUrl = loadingClass.getClassLoader().getResource(path);
-    if (resourceUrl == null) {
-      throw new IllegalArgumentException("resource " + path + " not found");
-    }
-    return resourceUrl.openStream();
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/TestStatusHandler.java b/src/main/java/com/google/devtools/build/lib/webstatusserver/TestStatusHandler.java
deleted file mode 100644
index 149a310..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/TestStatusHandler.java
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.webstatusserver;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
-import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.reflect.TypeToken;
-
-import com.sun.net.httpserver.HttpContext;
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpServer;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.lang.reflect.Type;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-import java.util.Map.Entry;
-
-/**
- * Collection of handlers for displaying the test data.
- */
-class TestStatusHandler {
-  private StaticResourceHandler frontendHandler;
-  private WebStatusBuildLog buildLog;
-  private HttpHandler detailsHandler;
-  private HttpServer server;
-  private ImmutableList<HttpContext> contexts;
-  private CommandJsonData commandHandler;
-  private Gson gson = new Gson();
-
-  public TestStatusHandler(HttpServer server, WebStatusBuildLog buildLog) {
-    Builder<HttpContext> builder = ImmutableList.builder();
-    this.buildLog = buildLog;
-    this.server = server;
-    detailsHandler = new TestStatusResultJsonData(this);
-    commandHandler = new CommandJsonData(this);
-    frontendHandler = StaticResourceHandler.createFromRelativePath("static/test.html", "text/html");
-    builder.add(
-        server.createContext("/tests/" + buildLog.getCommandId() + "/details", detailsHandler));
-    builder.add(
-        server.createContext("/tests/" + buildLog.getCommandId() + "/info", commandHandler));
-    builder.add(server.createContext("/tests/" + buildLog.getCommandId(), frontendHandler));
-    contexts = builder.build();
-  }
-
-  public WebStatusBuildLog getBuildLog() {
-    return buildLog;
-  }
-
-  
-  /**
-   *  Serves JSON objects containing command info, which will be rendered by frontend.
-   */
-  private class CommandJsonData implements HttpHandler {
-    private TestStatusHandler testStatusHandler;
-    
-    public CommandJsonData(TestStatusHandler testStatusHandler) {
-      this.testStatusHandler = testStatusHandler;
-    }
-
-    @Override
-    public void handle(HttpExchange exchange) throws IOException {
-      exchange.getResponseHeaders().put("Content-Type", ImmutableList.of("application/json"));
-      Type commandInfoType = new TypeToken<Map<String, JsonElement>>() {}.getType();
-      JsonObject response = gson.toJsonTree(testStatusHandler.buildLog.getCommandInfo(),
-          commandInfoType).getAsJsonObject();
-      response.addProperty("startTime", testStatusHandler.buildLog.getStartTime());
-      response.addProperty("finished", testStatusHandler.buildLog.finished());
-
-      String serializedResponse = response.toString();
-      exchange.sendResponseHeaders(200, serializedResponse.length());
-      try (OutputStream os = exchange.getResponseBody()) {
-        os.write(serializedResponse.getBytes(StandardCharsets.UTF_8));
-      }
-    }
-  }
-  
-  /**
-   * Serves JSON objects containing test cases, which will be rendered by frontend.
-   */
-  private class TestStatusResultJsonData implements HttpHandler {
-    private TestStatusHandler testStatusHandler;
-
-    public TestStatusResultJsonData(TestStatusHandler testStatusHandler) {
-      this.testStatusHandler = testStatusHandler;
-    }
-
-    @Override
-    public void handle(HttpExchange exchange) throws IOException {
-      Map<String, JsonObject> testInfo = testStatusHandler.buildLog.getTestCases();
-      exchange.getResponseHeaders().put("Content-Type", ImmutableList.of("application/json"));
-      JsonObject response = new JsonObject();
-      for (Entry<String, JsonObject> testCase : testInfo.entrySet()) {
-        response.add(testCase.getKey(), testCase.getValue());
-      }
-
-      String serializedResponse = response.toString();
-      exchange.sendResponseHeaders(200, serializedResponse.length());
-      try (OutputStream os = exchange.getResponseBody()) {
-        os.write(serializedResponse.getBytes(StandardCharsets.UTF_8));
-      }
-    }
-  }
-
-  /**
-   * Adds another URI for existing test data. If specified URI is already used by some other 
-   * handler, the previous handler will be removed.
-   */
-  public void overrideURI(String uri) {
-    String detailsPath = uri + "/details";
-    String commandPath = uri + "/info";
-    try {
-      this.server.removeContext(detailsPath);
-      this.server.removeContext(commandPath);
-    } catch (IllegalArgumentException e) {
-      // There was nothing to remove, so proceed with creation (unfortunately the server api doesn't
-      // have "hasContext" method)
-    }
-    this.server.createContext(detailsPath, this.detailsHandler); 
-    this.server.createContext(commandPath, this.commandHandler);   
-  }
-  
-  /**
-   * Deregisters all the handlers associated with the test.
-   */
-  public void deregister() {
-    for (HttpContext c : this.contexts) {
-      this.server.removeContext(c);
-    }
-  }
-}
-
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/WebStatusBuildLog.java b/src/main/java/com/google/devtools/build/lib/webstatusserver/WebStatusBuildLog.java
deleted file mode 100644
index 3f483a6..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/WebStatusBuildLog.java
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.webstatusserver;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.view.test.TestStatus.BlazeTestStatus;
-import com.google.devtools.build.lib.view.test.TestStatus.TestCase;
-import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.logging.Logger;
-
-/**
- * Stores information about one build command. The data is stored in JSON so that it can be
- * can be easily fed to frontend.
- *
- * <p> The information is grouped into following structures:
- * <ul>
- * <li> {@link #commandInfo} contain information about the build known when it starts but before
- *      anything is actually compiled/run
- * <li> {@link #testCases} contain detailed information about each test case ran, for now they're
- *
- * </ul>
- */
-public class WebStatusBuildLog {
-  private Gson gson = new Gson();
-  private boolean complete = false;
-  private static final Logger LOG =
-      Logger.getLogger(WebStatusEventCollector.class.getCanonicalName());
-  private Map<String, JsonElement> commandInfo = new HashMap<>();
-  private Map<String, JsonObject> testCases = new HashMap<>();
-  private long startTime;
-  private ImmutableList<String> targetList;
-  private UUID commandId;
-
-  public WebStatusBuildLog(UUID commandId) {
-    this.commandId = commandId;
-  }
-
-  public WebStatusBuildLog addInfo(String key, Object value) {
-    commandInfo.put(key, gson.toJsonTree(value));
-    return this;
-  }
-
-  public void addStartTime(long startTime) {
-    this.startTime = startTime;
-  }
-
-  public void addTargetList(List<String> targets) {
-    this.targetList = ImmutableList.copyOf(targets);
-  }
-
-  public void finish() {
-    commandInfo = ImmutableMap.copyOf(commandInfo);
-    complete = true;
-  }
-
-  public Map<String, JsonElement> getCommandInfo() {
-    return commandInfo;
-  }
-
-  public ImmutableMap<String, JsonObject> getTestCases() {
-    // TODO(bazel-team): not really immutable, since one can do addProperty on
-    // values (unfortunately gson doesn't support immutable JsonObjects)
-    return ImmutableMap.copyOf(testCases);
-  }
-
-  public boolean finished() {
-    return complete;
-  }
-
-  public List<String> getTargetList() {
-    return targetList;
-  }
-
-  public long getStartTime() {
-    return startTime;
-  }
-
-  public void addTestTarget(Label label) {
-    String targetName = label.toShorthandString();
-    if (!testCases.containsKey(targetName)) {
-      JsonObject summary = createTestCaseEmptyJsonNode(targetName);
-      summary.addProperty("finished", false);
-      summary.addProperty("status", "started");
-      testCases.put(targetName, summary);
-    } else {
-      // TODO(bazel-team): figure out if there are any situations it can happen
-    }
-  }
-
-  public void addTestSummary(Label label, BlazeTestStatus status, List<Long> testTimes,
-      boolean isCached) {
-    JsonObject testCase = testCases.get(label.toShorthandString());
-    testCase.addProperty("status", status.toString());
-    testCase.add("times", gson.toJsonTree(testTimes));
-    testCase.addProperty("cached", isCached);
-    testCase.addProperty("finished", true);
-  }
-
-  public void addTargetBuilt(Label label, boolean success) {
-    if (testCases.containsKey(label.toShorthandString())) {
-      if (success) {
-        testCases.get(label.toShorthandString()).addProperty("status", "built");
-      } else {
-        testCases.get(label.toShorthandString()).addProperty("status", "build failure");
-      }
-    } else {
-      LOG.info("Unhandled target: " + label);
-    }
-  }
-
-  @VisibleForTesting
-  static JsonObject createTestCaseEmptyJsonNode(String fullName) {
-    JsonObject currentNode = new JsonObject();
-    currentNode.addProperty("fullName", fullName);
-    currentNode.addProperty("name", "");
-    currentNode.addProperty("className", "");
-    currentNode.add("results", new JsonObject());
-    currentNode.add("times", new JsonObject());
-    currentNode.add("children", new JsonObject());
-    currentNode.add("failures", new JsonObject());
-    currentNode.add("errors", new JsonObject());
-    return currentNode;
-  }
-
-  private static JsonObject createTestCaseEmptyJsonNode(String fullName, TestCase testCase) {
-    JsonObject currentNode = createTestCaseEmptyJsonNode(fullName);
-    currentNode.addProperty("name", testCase.getName());
-    currentNode.addProperty("className", testCase.getClassName());
-    return currentNode;
-  }
-
-  private JsonObject mergeTestCases(JsonObject currentNode, String fullName, TestCase testCase,
-      int shardNumber) {
-    if (currentNode == null) {
-      currentNode = createTestCaseEmptyJsonNode(fullName, testCase);
-    }
-
-    if (testCase.getRun()) {
-      JsonObject results = (JsonObject) currentNode.get("results");
-      JsonObject times = (JsonObject) currentNode.get("times");
-
-      if (testCase.hasResult()) {
-        results.addProperty(Integer.toString(shardNumber), testCase.getResult());
-      }
-
-      if (testCase.hasStatus()) {
-        results.addProperty(Integer.toString(shardNumber), testCase.getStatus().toString());
-      }
-
-      if (testCase.hasRunDurationMillis()) {
-        times.addProperty(Integer.toString(shardNumber), testCase.getRunDurationMillis());
-      }
-    }
-    JsonObject children = (JsonObject) currentNode.get("children");
-
-    for (TestCase child : testCase.getChildList()) {
-      String fullChildName = child.getClassName() + "." + child.getName();
-      JsonObject childNode = mergeTestCases((JsonObject) children.get(fullChildName), fullChildName,
-          child, shardNumber);
-      if (!children.has(fullChildName)) {
-        children.add(fullChildName, childNode);
-      }
-    }
-    return currentNode;
-  }
-
-  public void addTestResult(Label label, TestCase testCase, int shardNumber) {
-    String testResultFullName = label.toShorthandString();
-    if (!testCases.containsKey(testResultFullName)) {
-      testCases.put(testResultFullName, createTestCaseEmptyJsonNode(testResultFullName, testCase));
-    }
-    mergeTestCases(testCases.get(testResultFullName), testResultFullName, testCase, shardNumber);
-  }
-
-  public UUID getCommandId() {
-    return commandId;
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/WebStatusEventCollector.java b/src/main/java/com/google/devtools/build/lib/webstatusserver/WebStatusEventCollector.java
deleted file mode 100644
index b3c3b06..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/WebStatusEventCollector.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.webstatusserver;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
-import com.google.common.eventbus.EventBus;
-import com.google.common.eventbus.Subscribe;
-import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
-import com.google.devtools.build.lib.analysis.ConfiguredTarget;
-import com.google.devtools.build.lib.analysis.TargetCompleteEvent;
-import com.google.devtools.build.lib.buildtool.BuildRequest;
-import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent;
-import com.google.devtools.build.lib.buildtool.buildevent.TestFilteringCompleteEvent;
-import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.events.Reporter;
-import com.google.devtools.build.lib.rules.test.TestResult;
-import com.google.devtools.build.lib.runtime.CommandCompleteEvent;
-import com.google.devtools.build.lib.runtime.CommandStartEvent;
-import com.google.devtools.build.lib.runtime.TestSummary;
-
-import java.util.logging.Logger;
-
-/**
- * This class monitors the build progress, collects events and preprocesses them for use by
- * frontend.
- * 
- */
-public class WebStatusEventCollector {
-  private static final Logger LOG =
-      Logger.getLogger(WebStatusEventCollector.class.getCanonicalName());
-  private final EventBus eventBus;
-  private final Reporter reporter;
-  private final int port;
-  private WebStatusBuildLog currentBuild;
-  private WebStatusServerModule serverModule;
-
-  public WebStatusEventCollector(EventBus eventBus, Reporter reporter,
-      WebStatusServerModule webStatusServerModule) {
-    this.eventBus = eventBus;
-    this.eventBus.register(this);
-    this.reporter = reporter;
-    this.port = webStatusServerModule.getPort();
-    this.serverModule = webStatusServerModule;
-    LOG.info("Created new status collector");
-  }
-
-  @Subscribe
-  public void buildStarted(BuildStartingEvent startingEvent) {
-    BuildRequest request = startingEvent.getRequest();
-    BlazeVersionInfo versionInfo = BlazeVersionInfo.instance();
-    currentBuild.addStartTime(request.getStartTime());
-    currentBuild.addTargetList(request.getTargets());
-    currentBuild
-        .addInfo("version", versionInfo)
-        .addInfo("commandName", request.getCommandName())
-        .addInfo("outputFs", startingEvent.getOutputFileSystem())
-        .addInfo("symlinkPrefix", request.getSymlinkPrefix())
-        .addInfo("optionsDescription", request.getOptionsDescription())
-        .addInfo("targets", request.getTargets())
-        .addInfo("viewOptions", request.getViewOptions());
-  }
-
-  @Subscribe
-  @SuppressWarnings("unused")
-  public void commandComplete(CommandCompleteEvent completeEvent) {
-    currentBuild.addInfo("endTime", completeEvent.getEventTimeInEpochTime());
-    currentBuild.finish();
-  }
-
-  @Subscribe
-  @SuppressWarnings("unused")
-  public void commandStarted(CommandStartEvent event) {
-    this.currentBuild = new WebStatusBuildLog(event.getCommandId());
-    this.serverModule.commandStarted();
-    String webStatusServerUrl = "http://localhost:" + port;
-    this.reporter.handle(Event.info("Status page: " + webStatusServerUrl + "/tests/"
-        + this.currentBuild.getCommandId() + " (alternative link: " + webStatusServerUrl
-        + WebStatusServerModule.LAST_TEST_URI + " )"));
-  }
-
-  @Subscribe
-  public void doneTestFiltering(TestFilteringCompleteEvent event) {
-    if (event.getTestTargets() != null) {
-      Builder<Label> builder = ImmutableList.builder();
-      for (ConfiguredTarget target : event.getTestTargets()) {
-        builder.add(target.getLabel());
-      }
-      doneTestFiltering(builder.build());
-    }
-  }
-
-  @VisibleForTesting
-  public void doneTestFiltering(Iterable<Label> testLabels) {
-    for (Label label : testLabels) {
-      currentBuild.addTestTarget(label);
-    }
-  }
-
-  @Subscribe
-  public void testTargetComplete(TestSummary summary) {
-    currentBuild.addTestSummary(summary.getTarget().getLabel(), summary.getStatus(),
-        summary.getTestTimes(), summary.isCached());
-  }
-
-  @Subscribe
-  public void testTargetResult(TestResult result) {
-    currentBuild.addTestResult(result.getTestAction().getOwner().getLabel(),
-        result.getData().getTestCase(), result.getShardNum());
-  }
-
-  @Subscribe
-  public void targetComplete(TargetCompleteEvent event) {
-    // TODO(bazel-team): would getting more details about failure be useful?
-    currentBuild.addTargetBuilt(event.getTarget().getTarget().getLabel(), !event.failed());
-  }
-
-  public WebStatusBuildLog getBuildLog() {
-    return this.currentBuild;
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/WebStatusServerModule.java b/src/main/java/com/google/devtools/build/lib/webstatusserver/WebStatusServerModule.java
deleted file mode 100644
index d8189d3..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/WebStatusServerModule.java
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.webstatusserver;
-
-import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.analysis.BlazeDirectories;
-import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
-import com.google.devtools.build.lib.runtime.BlazeModule;
-import com.google.devtools.build.lib.runtime.BlazeServerStartupOptions;
-import com.google.devtools.build.lib.runtime.Command;
-import com.google.devtools.build.lib.runtime.CommandEnvironment;
-import com.google.devtools.build.lib.util.AbruptExitException;
-import com.google.devtools.build.lib.util.Clock;
-import com.google.devtools.common.options.OptionsBase;
-import com.google.devtools.common.options.OptionsProvider;
-
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpServer;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.LinkedList;
-import java.util.UUID;
-import java.util.logging.Logger;
-
-/**
- * Web server for monitoring blaze commands status.
- */
-public class WebStatusServerModule extends BlazeModule {
-  static final String LAST_TEST_URI = "/tests/last";
-  // 100 is an arbitrary limit; it seems like a reasonable size for history and it's okay to change
-  // it
-  private static final int MAX_TESTS_STORED = 100;
-
-  private HttpServer server;
-  private boolean running = false;
-  private BlazeServerStartupOptions serverOptions;
-  private static final Logger LOG =
-      Logger.getLogger(WebStatusServerModule.class.getCanonicalName());
-  private int port;
-  private LinkedList<TestStatusHandler> testsRan = new LinkedList<>();
-  @SuppressWarnings("unused")
-  private WebStatusEventCollector collector;
-  @SuppressWarnings("unused")
-  private IndexPageHandler indexHandler;
-
-  @Override
-  public Iterable<Class<? extends OptionsBase>> getStartupOptions() {
-    return ImmutableList.<Class<? extends OptionsBase>>of(BlazeServerStartupOptions.class);
-  }
-
-  @Override
-  public void blazeStartup(OptionsProvider startupOptions, BlazeVersionInfo versionInfo,
-      UUID instanceId, BlazeDirectories directories, Clock clock) throws AbruptExitException {
-    serverOptions = startupOptions.getOptions(BlazeServerStartupOptions.class);
-    if (serverOptions.useWebStatusServer <= 0) {
-      LOG.info("web status server disabled");
-      return;
-    }
-    port = serverOptions.useWebStatusServer;
-    try {
-      server = HttpServer.create(new InetSocketAddress(port), 0);
-      serveStaticContent();
-      TextHandler lastCommandHandler = new TextHandler("No commands ran yet.");
-      server.createContext("/last", lastCommandHandler);
-      server.setExecutor(null);
-      server.start();
-      indexHandler = new IndexPageHandler(server, this.testsRan);
-      running = true;
-      LOG.info("Running web status server on port " + port);
-    } catch (IOException e) {
-      // TODO(bazel-team): Display information about why it failed
-      running = false;
-      LOG.warning("Unable to run web status server on port " + port);
-    }
-  }
-
-  @Override
-  public void beforeCommand(Command command, CommandEnvironment env)
-      throws AbruptExitException {
-    if (!running) {
-      return;
-    }
-    collector =
-        new WebStatusEventCollector(env.getEventBus(), env.getReporter(), this);
-  }
-
-  public void commandStarted() {
-    WebStatusBuildLog currentBuild = collector.getBuildLog();
-
-    if (testsRan.size() == MAX_TESTS_STORED) {
-      TestStatusHandler oldestTest = testsRan.removeLast();
-      oldestTest.deregister();
-    }
-
-    TestStatusHandler lastTest = new TestStatusHandler(server, currentBuild);
-    testsRan.add(lastTest);
-
-    lastTest.overrideURI(LAST_TEST_URI);
-  }
-
-  private void serveStaticContent() {
-    StaticResourceHandler testjs =
-        StaticResourceHandler.createFromRelativePath("static/test.js", "application/javascript");
-    StaticResourceHandler indexjs =
-        StaticResourceHandler.createFromRelativePath("static/index.js", "application/javascript");
-    StaticResourceHandler style =
-        StaticResourceHandler.createFromRelativePath("static/style.css", "text/css");
-    StaticResourceHandler d3 = StaticResourceHandler.createFromAbsolutePath(
-        "third_party/javascript/d3/d3-js.js", "application/javascript");
-    StaticResourceHandler jquery = StaticResourceHandler.createFromAbsolutePath(
-        "third_party/javascript/jquery/v2_0_3/jquery_uncompressed.jslib",
-        "application/javascript");
-    StaticResourceHandler testFrontend =
-        StaticResourceHandler.createFromRelativePath("static/test.html", "text/html");
-
-    server.createContext("/css/style.css", style);
-    server.createContext("/js/test.js", testjs);
-    server.createContext("/js/index.js", indexjs);
-    server.createContext("/js/lib/d3.js", d3);
-    server.createContext("/js/lib/jquery.js", jquery);
-    server.createContext(LAST_TEST_URI, testFrontend);
-  }
-
-  private static class TextHandler implements HttpHandler {
-    private String response;
-
-    private TextHandler(String response) {
-      this.response = response;
-    }
-
-    @Override
-    public void handle(HttpExchange exchange) throws IOException {
-      exchange.getResponseHeaders().put("Content-Type", ImmutableList.of("text/plain"));
-      exchange.sendResponseHeaders(200, response.length());
-      try (OutputStream os = exchange.getResponseBody()) {
-        os.write(response.getBytes(StandardCharsets.UTF_8));
-      }
-    }
-  }
-
-  public int getPort() {
-    return port;
-  }
-}
-
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/static/index.html b/src/main/java/com/google/devtools/build/lib/webstatusserver/static/index.html
deleted file mode 100644
index f57bc30..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/static/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<html>
-<head>
-  <title> Bazel web server </title>
-  <link rel="stylesheet" type="text/css" href="/css/style.css"></link>
-  <script src="/js/lib/d3.js" type="application/javascript"></script>
-  <script src="/js/lib/jquery.js" type="application/javascript"></script>
-  <script src="/js/index.js" type="application/javascript"></script>
-</head>
-<body onload="showData()">
-  <h1> Bazel web server status page </h1>
-  <div id="testsList">
-  </div>
-</body>
-</html>
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/static/index.js b/src/main/java/com/google/devtools/build/lib/webstatusserver/static/index.js
deleted file mode 100644
index f677b2f..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/static/index.js
+++ /dev/null
@@ -1,75 +0,0 @@
-var icons = {
-  running: '\u25B6',
-  finished: '\u2611'
-};
-
-function showData() {
-  renderTestList(getTestsData());
-}
-
-function getTestsData() {
-  // TODO(bazel-team): change it to async callback retrieving data in background
-  // (for simplicity this is synchronous now)
-  return $.ajax({
-      type: 'GET',
-      url: document.URL + 'tests/list',
-      async: false
-  }).responseJSON;
-}
-
-function renderTestList(tests) {
-  var rows = d3.select('#testsList')
-    .selectAll()
-    .data(tests)
-    .enter().append('div')
-    .classed('info-cell', true);
-
-  // status
-  rows.append('div').classed('info-detail', true).text(function(j) {
-    return j.finished ? icons.finished : icons.running;
-  });
-
-  // target(s) name(s)
-  rows.append('div').classed('info-detail', true).text(function(j) {
-    if (!j.targets || j.targets.length == 0) {
-      return 'Unknown target.';
-    } else if (j.targets.length == 1) {
-      return j.targets[0];
-    }
-    return j.targets;
-  });
-
-  // start time
-  rows.append('div').classed('info-detail', true).text(function(j) {
-    // Pad value with 2 zeroes
-    function pad(value) {
-      return value < 10 ? '0' + value : value;
-    }
-
-    var
-      date = new Date(j.startTime),
-      today = new Date(Date.now()),
-      h = pad(date.getHours()),
-      m = pad(date.getMinutes()),
-      dd = pad(date.getDay()),
-      mm = pad(date.getMonth()),
-      yy = date.getYear(),
-      day;
-
-    // don't show date if ran today
-    if (dd != today.getDay() && mm != today.getMonth() &&
-        yy != today.getYear()) {
-      day = ' on ' + yy + '-' + mm + '-' + dd;
-    } else {
-      day = '';
-    }
-    return h + ':' + m;
-  });
-
-  // link
-  rows.append('div').classed('info-detail', true).classed('button', true)
-      .append('a').attr('href', function(datum, index) {
-        return '/tests/' + datum.uuid;
-      })
-      .text('link');
-}
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/static/style.css b/src/main/java/com/google/devtools/build/lib/webstatusserver/static/style.css
deleted file mode 100644
index 2ea712d..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/static/style.css
+++ /dev/null
@@ -1,39 +0,0 @@
-a {
-  color: #666
-}
-body {
-  font-family: 'Helvetica Neue', HelveticaNeue, Helvetica, sans-serif;
-}
-.test-case {
-  outline:1px solid #eeeeee;
-  display:block;
-  padding:5px
-}
-.test-case:nth-child(odd) {
-  background: #eee
-}
-.test-case:nth-child(even) {
-  background: #fff
-}
-.test-detail {
-  display:inline-block;
-  min-width:10px;
-  padding-left:5px;
-  padding-right:5px
-}
-.info-cell {
-  padding:5px;
-  background: #eee;
-  outline:1px solid #fff;
-  display:block
-}
-.info-detail {
-  padding-right:5px;
-  padding-left:5px;
-  display:inline-block
-}
-.button {
-  cursor:pointer;
-  color:#666;
-  float:right
-}
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/static/test.html b/src/main/java/com/google/devtools/build/lib/webstatusserver/static/test.html
deleted file mode 100644
index 04a6fb7..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/static/test.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<html>
-<head><title>Tests Result Page</title>
-  <link rel="stylesheet" type="text/css" href="/css/style.css"></link>
-  <script src="/js/lib/d3.js" type="application/javascript"></script>
-  <script src="/js/lib/jquery.js" type="application/javascript"></script>
-  <script src="/js/test.js" type="application/javascript"></script>
-</head>
-<body>
-<h1> Bazel web status server </h1>
-<div id="testInfo">
-  No test info to display.
-</div>
-<br>
-<div id="testFilters">
-  <div class="info-cell">
-    <input placeholder="Filter by name" type=text id="search"></input>
-    <!-- TODO(bazel-team) this is very simplistic view of tests,
-      we probably need more filters -->
-    <input type=checkbox checked=true id="boxPassed">passed</input>
-    <input type=checkbox checked=true id="boxFailed">failed</input>
-    <button id="clearFilters"> clear filters </button>
-  </div>
-</div>
-<div id="testDetails">
-  No test details to display.
-</div>
-</body>
-</html>
diff --git a/src/main/java/com/google/devtools/build/lib/webstatusserver/static/test.js b/src/main/java/com/google/devtools/build/lib/webstatusserver/static/test.js
deleted file mode 100644
index 406dcab..0000000
--- a/src/main/java/com/google/devtools/build/lib/webstatusserver/static/test.js
+++ /dev/null
@@ -1,384 +0,0 @@
-var icons = {
-  running: '?',
-  passed: '\u2705',
-  errors: '\u274c'
-};
-
-
-function showData() {
-  renderDetails(getDetailsData(), false);
-  renderInfo(getCommandInfo());
-}
-
-function getCommandInfo() {
-  var url = document.URL;
-  if (url[url.length - 1] != '/') {
-    url += '/';
-  }
-  return $.ajax({
-      type: 'GET',
-      url: url + 'info',
-      async: false
-  }).responseJSON;
-}
-
-function getDetailsData() {
-  // TODO(bazel-team): auto refresh, async callback
-  var url = document.URL;
-  if (url[url.length - 1] != '/') {
-    url += '/';
-  }
-  return $.ajax({
-      type: 'GET',
-      url: url + 'details',
-      async: false
-  }).responseJSON;
-}
-
-
-function showDate(d) {
-  function pad(x) {
-    return x < 10 ? '0' + x : '' + x;
-  }
-  var today = new Date();
-  var result = pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' +
-               pad(d.getSeconds());
-  if (d.getDate() === today.getDate() && d.getMonth() === today.getMonth() &&
-      d.getYear() === today.getYear()) {
-    result += ' today';
-  } else {
-    result += pad(d.getDate()) + ' ' + pad(d.getMonth()) + ' ' + d.getYear();
-  }
-  return result;
-}
-
-function renderInfo(info) {
-  $('#testInfo').empty();
-  var data = [
-    ['Targets: ', info['targets']],
-    ['Started at: ', showDate(new Date(info['startTime']))],
-  ];
-  if (info['finished']) {
-    data.push(['Finished at: ', showDate(new Date(info['endTime']))]);
-  } else {
-    data.push(['Still running']);
-  }
-  var selection = d3.select('#testInfo').selectAll()
-      .data(data)
-      .enter().append('div')
-      .classed('info-cell', true);
-  selection
-      .append('div')
-      .classed('info-detail', true)
-      .text(function(d) { return d[0]; });
-  selection
-      .append('div')
-      .classed('info-detail', true)
-      .text(function(d) { return d[1]; });
-}
-
-// predicate is either a predicate function or null - in the latter case
-// everything is shown
-function renderDetails(tests, predicate) {
-  $('#testDetails').empty();
-  if (tests.length == 0) {
-    $('#testDetails').text('No test details to display.');
-    return;
-  }
-  // flatten object to array and set visibility
-  tests = $.map(tests, function(element) {
-    if (predicate) {
-      setVisibility(predicate, element);
-    }
-    return element;
-  });
-  var rows = d3.select('#testDetails').selectAll()
-    .data(tests)
-    .enter().append('div')
-    .classed('test-case', true);
-
-  function addTestDetail(selection, toplevel) {
-    function fullName() {
-      selection.append('div').classed('test-detail', true).text(function(j) {
-        return j.fullName;
-      });
-    }
-    function propagateStatus(j) {
-      var result = '';
-      var failures = [];
-      var errors = [];
-      $.each(j.results, function(key, value) {
-        if (value == 'FAILED') {
-          failures.push(key);
-        }
-        if (value == 'ERROR') {
-          errors.push(key);
-        }
-      });
-      if (failures.length > 0) {
-        var s = failures.length > 1 ? 's' : '';
-        result += 'Failed on ' + failures.length + ' shard' + s + ': ' +
-                    failures.join();
-      }
-      if (errors.length > 0) {
-        var s = failures.length > 1 ? 's' : '';
-        result += 'Errors on ' + errors.length + ' shard' + s + ': ' +
-                    errors.join();
-      }
-      if (result == '') {
-        return j.status;
-      }
-      return result;
-    }
-    function testCaseStatus() {
-      selection.append('div')
-          .classed('test-detail', true)
-          .text(propagateStatus);
-    }
-    function testTargetStatus() {
-      selection.append('div')
-          .classed('test-detail', true)
-          .text(function(target) {
-                  var childStatus = propagateStatus(target);
-                  if (target.finished = false) {
-                    return target.status + ' ' + stillRunning;
-                  } else {
-                    if (childStatus == 'PASSED') {
-                      return target.status;
-                    } else {
-                      return target.status + ' ' + childStatus;
-                    }
-                  }
-          });
-    }
-    function testTargetStatusIcon() {
-      selection.append('div')
-          .classed('test-detail', true)
-          .attr('color', function(target) {
-                  var childStatus = propagateStatus(target);
-                  if (target.finished == false) {
-                    return 'running';
-                  } else {
-                    if (childStatus == 'PASSED') {
-                      return 'passed';
-                    } else {
-                      return 'errors';
-                    }
-                  }})
-          .text(function(target) {
-                  var childStatus = propagateStatus(target);
-                  if (target.finished == false) {
-                    return icons.running;
-                  } else {
-                    if (childStatus == 'PASSED') {
-                      return icons.passed;
-                    } else {
-                      return icons.errors;
-                    }
-                  }
-          });
-    }
-    function testCaseTime() {
-      selection.append('div').classed('test-detail', true).text(function(j) {
-        var times = $.map(j.times, function(element, key) { return element });
-        if (times.length < 1) {
-          return '?';
-        } else {
-          return Math.max.apply(Math, times) / 1000 + ' s';
-        }
-      });
-    }
-
-    function visibilityFilter() {
-      selection.attr('show', function(datum) {
-        return ('show' in datum) ? datum['show'] : true;
-      });
-    }
-
-    // Toplevel nodes represent test targets, so they look a bit different
-    if (toplevel) {
-      testTargetStatusIcon();
-      fullName();
-    } else {
-      testTargetStatusIcon();
-      fullName();
-      testCaseStatus();
-      testCaseTime();
-    }
-    visibilityFilter();
-  }
-
-  function addNestedDetails(table, toplevel) {
-    table.sort(function(data1, data2) {
-      if (data1.fullName < data2.fullName) {
-        return -1;
-      }
-      if (data1.fullName > data2.fullName) {
-        return 1;
-      }
-      return 0;
-    });
-
-    addTestDetail(table, toplevel);
-
-    // Add children nodes + show/hide button
-    var nonLeafNodes = table.filter(function(data, index) {
-      return !($.isEmptyObject(data.children));
-    });
-    var nextLevelNodes = nonLeafNodes.selectAll().data(function(d) {
-      return $.map(d.children, function(element, key) { return element });
-    });
-
-    if (nextLevelNodes.enter().empty()) {
-      return;
-    }
-
-    nonLeafNodes
-        .append('div')
-        .classed('test-detail', true)
-        .classed('button', true)
-        .text(function(j) {
-          return 'Show details';
-        })
-        .attr('toggle', 'off')
-        .on('click', function(datum) {
-          if ($(this).attr('toggle') == 'on') {
-            $(this).siblings('.test-case').not('[show=false]').hide();
-            $(this).attr('toggle', 'off');
-            $(this).text('Show details');
-          } else {
-            $(this).siblings('.test-case').not('[show=false]').show();
-            $(this).attr('toggle', 'on');
-            $(this).text('Hide details');
-          }
-        });
-    nextLevelNodes.enter().append('div').classed('test-case', true);
-    addNestedDetails(nextLevelNodes, false);
-  }
-
-  addNestedDetails(rows, true);
-  $('.button').siblings('.test-case').hide();
-  if (predicate) {
-    toggleVisibility();
-  }
-}
-
-function toggleVisibility() {
-  $('#testDetails > [show=false]').hide();
-  $('#testDetails > [show=true]').show();
-  $('[toggle=on]').siblings('[show=false]').hide();
-  $('[toggle=on]').siblings('[show=true]').show();
-}
-
-function setVisibility(predicate, object) {
-  var show = predicate(object);
-  var childrenPredicate = predicate;
-  // It rarely makes sense to show a non-leaf node and hide its children, so
-  // we just show all children
-  if (show) {
-    childrenPredicate = function() { return true; };
-  }
-  if ('children' in object) {
-    for (var child in object.children) {
-      setVisibility(childrenPredicate, object.children[child]);
-      show = object.children[child]['show'] || show;
-    }
-  }
-  object['show'] = show;
-}
-
-// given a list of predicates, return a function
-function intersectFilters(filterList) {
-  var filters = filterList.filter(function(x) { return x });
-  return function(x) {
-    for (var i = 0; i < filters.length; i++) {
-      if (!filters[i](x)) {
-        return false;
-      }
-    }
-    return true;
-  }
-}
-
-function textFilterActive() {
-  return $('#search').val();
-}
-
-function getTestFilters() {
-  var statusFilter = null;
-  var textFilter = null;
-  var filters = [];
-  var passed = $('#boxPassed').prop('checked');
-  var failed = $('#boxFailed').prop('checked');
-  // add checkbox filters only when necessary (ie. something is unchecked - when
-  // everything is checked this means user wants to see everything).
-  if (!(passed && failed)) {
-    var checkBoxFilters = [];
-    if (passed) {
-      checkBoxFilters.push(function(object) {
-        return object.status == 'PASSED';
-      });
-    }
-    if (failed) {
-      checkBoxFilters.push(function(object) {
-        return 'status' in object && object.status != 'PASSED';
-      });
-    }
-    filters.push(function(object) {
-      return checkBoxFilters.some(function(f) { return f(object); });
-    });
-  }
-  if (textFilterActive()) {
-    filters.push(function(object) {
-      // TODO(bazel-team): would case insentive search make more sense?
-      return ('fullName' in object &&
-          object.fullName.indexOf($('#search').val()) != -1);
-    });
-  }
-  return filters;
-}
-
-function redraw() {
-  renderDetails(getDetailsData(), intersectFilters(getTestFilters()));
-}
-
-function updateVisibleCases() {
-  var predicate = intersectFilters(getTestFilters());
-  var parentCases = d3.selectAll('#testDetails > div').data();
-  parentCases.forEach(function(element, index) {
-    setVisibility(predicate, element);
-  });
-  d3.selectAll('.test-detail').attr('show', function(datum) {
-    return ('show' in datum) ? datum['show'] : true;
-  });
-  d3.selectAll('.test-case').attr('show', function(datum) {
-    return ('show' in datum) ? datum['show'] : true;
-  });
-  toggleVisibility();
-  if (textFilterActive()) {
-    // expand nodes to save some clicking - if user searched for something that
-    // is leaf of the tree, she definitely wants to see it
-    $('#testDetails > [show=true]').find('[toggle=off]').click();
-  }
-}
-
-function enableControls() {
-  var redrawTimeout = null;
-  $('#boxPassed').click(updateVisibleCases);
-  $('#boxFailed').click(updateVisibleCases);
-  $('#search').keyup(function() {
-    clearTimeout(redrawTimeout);
-    redrawTimeout = setTimeout(updateVisibleCases, 500);
-  });
-  $('#clearFilters').click(function() {
-    $('#boxPassed').prop('checked', true);
-    $('#boxFailed').prop('checked', true);
-    $('#search').val('');
-    updateVisibleCases();
-  });
-}
-
-$(function() {
-  showData();
-  enableControls();
-});