/*
 * Copyright 2012-present Facebook, Inc.
 *
 * 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.facebook.buck.test;

import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.facebook.buck.test.result.type.ResultType;
import com.facebook.buck.util.XmlDomParser;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import static java.nio.charset.StandardCharsets.UTF_8;

public class XmlTestResultParser {

  /** Utility Class:  Do not instantiate. */
  private XmlTestResultParser() {}

  public static List<TestCaseSummary> parseAndroid(Path xmlFile, String serialNumber)
      throws IOException, SAXException {
    String fileContents = new String(Files.readAllBytes(xmlFile), UTF_8);
    Document doc = XmlDomParser.parse(new InputSource(new StringReader(fileContents)),
        /* namespaceAware */ true);
    Element root = doc.getDocumentElement();
    Preconditions.checkState("testsuite".equals(root.getTagName()));

    NodeList testElements = doc.getElementsByTagName("testcase");

    List<TestCaseSummary> results = new ArrayList<>();

    Map<String, List<TestResultSummary>> testResultsMap = new LinkedHashMap<>();

    for (int i = 0; i < testElements.getLength(); i++) {
      Element node = (Element) testElements.item(i);

      String className = node.getAttribute("classname");
      List<TestResultSummary> testResults = testResultsMap.get(className);
      if (testResults == null) {
        testResults = new ArrayList<>();
        testResultsMap.put(className, testResults);
      }

      testResults.add(getTestResultFromElement(node, getTestCaseName(className, serialNumber)));
    }

    for (Map.Entry<String, List<TestResultSummary>> entry : testResultsMap.entrySet()) {
      TestCaseSummary testCaseSummary = new TestCaseSummary(
          getTestCaseName(entry.getKey(), serialNumber),
          entry.getValue());
      results.add(testCaseSummary);
    }

    return results;
  }

  private static String getTestCaseName(String className, String serialNumber) {
    return className + " (" + serialNumber + ")";
  }

  private static TestResultSummary getTestResultFromElement(Element node, String testCaseName) {
    double time = Float.parseFloat(node.getAttribute("time"));
    String testName = node.getAttribute("name");

    String message = null;
    String stacktrace = null;
    String stdOut = null;
    String stdErr = null;
    ResultType type = ResultType.SUCCESS;

    NodeList failure = node.getElementsByTagName("failure");
    if (failure.getLength() == 1) {
      stdOut = failure.item(0).getTextContent();
      type = ResultType.FAILURE;
    }
    return new TestResultSummary(
        testCaseName,
        testName,
        type,
        Math.round(time * 1000),
        message,
        stacktrace,
        stdOut,
        stdErr);
  }

  public static TestCaseSummary parse(Path xmlFile) throws IOException {
    String xmlFileContents = new String(Files.readAllBytes(xmlFile), UTF_8);

    try {
      return doParse(xmlFileContents);
    } catch (NumberFormatException | SAXException e) {
      // This is an attempt to track down an inexplicable error that we have observed in the wild.
      String message = createDetailedExceptionMessage(xmlFile, xmlFileContents);
      throw new RuntimeException(message, e);
    }
  }

  private static TestCaseSummary doParse(String xml) throws IOException, SAXException {
    Document doc = XmlDomParser.parse(new InputSource(new StringReader(xml)),
        /* namespaceAware */ true);
    Element root = doc.getDocumentElement();
    Preconditions.checkState("testcase".equals(root.getTagName()));
    String testCaseName = root.getAttribute("name");

    NodeList testElements = doc.getElementsByTagName("test");
    List<TestResultSummary> testResults = Lists.newArrayListWithCapacity(testElements.getLength());
    for (int i = 0; i < testElements.getLength(); i++) {
      Element node = (Element) testElements.item(i);
      String testName = node.getAttribute("name");
      long time = Long.parseLong(node.getAttribute("time"));
      String typeString = node.getAttribute("type");
      ResultType type = ResultType.valueOf(typeString);

      String message;
      String stacktrace;
      if (type == ResultType.SUCCESS) {
        message = null;
        stacktrace = null;
      } else {
        message = node.getAttribute("message");
        stacktrace = node.getAttribute("stacktrace");
      }

      NodeList stdoutElements = node.getElementsByTagName("stdout");
      String stdOut;
      if (stdoutElements.getLength() == 1) {
        stdOut = stdoutElements.item(0).getTextContent();
      } else {
        stdOut = null;
      }

      NodeList stderrElements = node.getElementsByTagName("stderr");
      String stdErr;
      if (stderrElements.getLength() == 1) {
        stdErr = stderrElements.item(0).getTextContent();
      } else {
        stdErr = null;
      }

      TestResultSummary testResult = new TestResultSummary(
          testCaseName,
          testName,
          type,
          time,
          message,
          stacktrace,
          stdOut,
          stdErr);
      testResults.add(testResult);
    }

    return new TestCaseSummary(testCaseName, testResults);
  }

  private static String createDetailedExceptionMessage(Path xmlFile, String xmlFileContents) {
    return "Error parsing test result data in " + xmlFile.toAbsolutePath() + ".\n" +
        "File contents:\n" + xmlFileContents;
  }
}

@@ -3392,20 +3392,24 @@
       st
-dOut
+acktrace
  = failu

Use stackTrace for instrumentation test failures instead of stdout