diff options
Diffstat (limited to 'src')
18 files changed, 14 insertions, 1394 deletions
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc index 641b60c291..bda1384e4f 100644 --- a/src/main/cpp/blaze.cc +++ b/src/main/cpp/blaze.cc @@ -340,10 +340,6 @@ static vector<string> GetArgumentArray() { } 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 0748fcc761..84cbf0b81e 100644 --- a/src/main/java/BUILD +++ b/src/main/java/BUILD @@ -524,25 +524,6 @@ java_library( ) 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 @@ java_library( ":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 6aea6e4508..2b29f2dc2f 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 @@ public final class BazelMain { 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 1d1e101b18..eb1fde55e0 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 @@ public class StandaloneTestStrategy extends TestStrategy { 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 cc2c43b2c2..329e995ce4 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.ExecutionOptions; 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 @@ public abstract class TestStrategy implements TestActionContext { // 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 @@ public abstract class TestStrategy implements TestActionContext { @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 1e82d8fc9f..244cb709f7 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 @@ public class BlazeServerStartupOptions extends OptionsBase { + "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 430f2666b8..b1d2fa682d 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 @@ public class StandaloneActionContextProvider extends ActionContextProvider { 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 757fa954f2..0000000000 --- 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 65d12f6702..0000000000 --- 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 149a310a28..0000000000 --- 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 3f483a626a..0000000000 --- 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 b3c3b06d1f..0000000000 --- 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 d8189d38e1..0000000000 --- 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 f57bc30c8e..0000000000 --- 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 f677b2ff37..0000000000 --- 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 2ea712d760..0000000000 --- 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 04a6fb712c..0000000000 --- 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 406dcabd55..0000000000 --- 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(); -}); |