public void reportRootError(@NotNull String message) {
   RootErrorNode rootErrorNode = new RootErrorNode(myRootNode);
   TCMessage startedMessage = rootErrorNode.createStartedMessage();
   printTCMessage(startedMessage);
   TCMessage finishedMessage = TC.newRootErrorFinishedMessage(rootErrorNode);
   finishedMessage.addAttribute(TCAttribute.EXCEPTION_MESSAGE, message);
   printTCMessage(finishedMessage);
 }
 public void onJstdConfigRunningFinished(
     @Nullable Exception testsRunException, @NotNull TestFileScope testFileScope) {
   ConfigNode configNode = getCurrentConfigNode();
   for (BrowserNode browserNode : configNode.getChildren()) {
     for (TestCaseNode testCaseNode : browserNode.getChildren()) {
       for (TestNode testNode : testCaseNode.getChildren()) {
         TCMessage testFailedMessage = TC.newTestFailedMessage(testNode);
         String reason = testsRunException != null ? "JsTestDriver crash" : "unknown reason";
         testFailedMessage.addAttribute(
             TCAttribute.EXCEPTION_MESSAGE, "Can't execute test due to " + reason + ".");
         testFailedMessage.addAttribute(TCAttribute.IS_TEST_ERROR, "yes");
         printTCMessage(testFailedMessage);
       }
       TCMessage testCaseFinishedMessage = TC.newTestSuiteFinishedMessage(testCaseNode);
       printTCMessage(testCaseFinishedMessage);
     }
     TCMessage browserFinishedMessage = TC.newTestSuiteFinishedMessage(browserNode);
     printTCMessage(browserFinishedMessage);
   }
   if (testsRunException != null) {
     ConfigErrorNode configErrorNode = new ConfigErrorNode(configNode);
     TCMessage startedMessage = configErrorNode.createStartedMessage();
     printTCMessage(startedMessage);
     TCMessage finishedMessage = TC.newConfigErrorFinishedMessage(configErrorNode);
     String fullMessage =
         formatMessage(testsRunException.getMessage(), testsRunException.getCause());
     finishedMessage.addAttribute(TCAttribute.EXCEPTION_MESSAGE, fullMessage);
     printTCMessage(finishedMessage);
   } else if (configNode.getChildren().isEmpty()) {
     final String message;
     Map.Entry<String, Set<String>> testCaseEntry = testFileScope.getSingleTestCaseEntry();
     if (testCaseEntry != null) {
       Set<String> testMethodNames = testCaseEntry.getValue();
       if (testMethodNames.isEmpty()) {
         message = "No '" + testCaseEntry.getKey() + "' test case found or it has no tests.";
       } else {
         message = "No '" + testFileScope.humanize() + "' test method found.";
       }
     } else {
       message = "No tests found. Please check 'test:' section of the configuration file.";
     }
     myErrStream.println(message);
   }
   TCMessage configFinishedMessage = TC.newTestSuiteFinishedMessage(configNode);
   printTCMessage(configFinishedMessage);
 }
  public void onTestCompleted(@NotNull TestPath testPath, @NotNull TestResult testResult) {
    TestNode testNode = getOrCreateTestNode(testPath);
    testNode.detachFromParent();
    String log = testResult.getLog();
    if (log != null && !log.isEmpty()) {
      TCMessage stdOutMessage = TC.newTestStdOutMessage(testNode);
      stdOutMessage.addAttribute(TCAttribute.STDOUT, log + "\n");
      printTCMessage(stdOutMessage);
    }

    int durationMillis = (int) testResult.getTime();
    TestResult.Result status = testResult.getResult();
    if (status == TestResult.Result.passed) {
      TCMessage testFinishedMessage = TC.newTestFinishedMessage(testNode);
      testFinishedMessage.addIntAttribute(TCAttribute.TEST_DURATION, durationMillis);
      printTCMessage(testFinishedMessage);
    } else {
      final String originalStack = testResult.getStack();
      final String parsedMessage = testResult.getParsedMessage();
      final String stackStr;
      if (originalStack.startsWith(parsedMessage)) {
        String s = originalStack.substring(parsedMessage.length());
        stackStr = s.replaceFirst("^[\n\r]*", "");
      } else {
        stackStr = originalStack;
      }
      TCMessage testFailedMessage = TC.newTestFailedMessage(testNode);
      testFailedMessage.addAttribute(TCAttribute.EXCEPTION_MESSAGE, parsedMessage);
      testFailedMessage.addAttribute(TCAttribute.EXCEPTION_STACKTRACE, stackStr);
      if (status == TestResult.Result.error) {
        testFailedMessage.addAttribute(TCAttribute.IS_TEST_ERROR, "yes");
      }
      testFailedMessage.addIntAttribute(TCAttribute.TEST_DURATION, durationMillis);
      printTCMessage(testFailedMessage);
    }
  }
  public void onFileLoadError(
      @NotNull String browserName,
      @Nullable String pathToJsFileWithError,
      @Nullable String errorMessage) {
    ConfigNode configNode = getCurrentConfigNode();
    BrowserNode browserNode = configNode.findChildByName(browserName);
    if (browserNode == null) {
      browserNode = new BrowserNode(browserName, configNode);
      configNode.addChild(browserNode);
    }
    BrowserErrorNode browserErrorNode =
        BrowserErrorNode.newBrowserErrorNode(browserNode, pathToJsFileWithError, errorMessage);
    TCMessage startedMessage = browserErrorNode.createStartedMessage();
    printTCMessage(startedMessage);

    TCMessage finishedMessage = TC.newBrowserErrorFinishedMessage(browserErrorNode);
    if (errorMessage != null) {
      finishedMessage.addAttribute(TCAttribute.EXCEPTION_MESSAGE, errorMessage);
    }
    printTCMessage(finishedMessage);
  }