| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * 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.android.regression.tests; |
| |
| import com.android.tradefed.result.MetricsXMLResultReporter; |
| import com.android.tradefed.result.TestDescription; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.List; |
| import java.util.Set; |
| |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.parsers.SAXParser; |
| import javax.xml.parsers.SAXParserFactory; |
| |
| /** Parser that extracts test metrics result data generated by {@link MetricsXMLResultReporter}. */ |
| public class MetricsXmlParser { |
| |
| /** Thrown when MetricsXmlParser fails to parse a metrics xml file. */ |
| @SuppressWarnings("serial") |
| public static class ParseException extends Exception { |
| public ParseException(Throwable cause) { |
| super(cause); |
| } |
| |
| public ParseException(String msg, Throwable cause) { |
| super(msg, cause); |
| } |
| } |
| |
| /* |
| * Parses the xml format. Expected tags/attributes are: |
| * testsuite name="runname" tests="X" |
| * runmetric name="metric1" value="1.0" |
| * testcase classname="FooTest" testname="testMethodName" |
| * testmetric name="metric2" value="1.0" |
| */ |
| private static class MetricsXmlHandler extends DefaultHandler { |
| |
| private static final String TESTSUITE_TAG = "testsuite"; |
| private static final String TESTCASE_TAG = "testcase"; |
| private static final String TIME_TAG = "time"; |
| private static final String RUNMETRIC_TAG = "runmetric"; |
| private static final String TESTMETRIC_TAG = "testmetric"; |
| |
| private TestDescription mCurrentTest = null; |
| |
| private Metrics mMetrics; |
| private Set<String> mBlocklistMetrics; |
| |
| public MetricsXmlHandler(Metrics metrics, Set<String> blocklistMetrics) { |
| mMetrics = metrics; |
| mBlocklistMetrics = blocklistMetrics; |
| } |
| |
| @Override |
| public void startElement(String uri, String localName, String name, Attributes attributes) |
| throws SAXException { |
| if (TESTSUITE_TAG.equalsIgnoreCase(name)) { |
| // top level tag - maps to a test run in TF terminology |
| String testCount = getMandatoryAttribute(name, "tests", attributes); |
| mMetrics.setNumTests(Integer.parseInt(testCount)); |
| mMetrics.addRunMetric(TIME_TAG, getMandatoryAttribute(name, TIME_TAG, attributes)); |
| } |
| if (TESTCASE_TAG.equalsIgnoreCase(name)) { |
| // start of description of an individual test method |
| String testClassName = getMandatoryAttribute(name, "classname", attributes); |
| String methodName = getMandatoryAttribute(name, "testname", attributes); |
| mCurrentTest = new TestDescription(testClassName, methodName); |
| } |
| if (RUNMETRIC_TAG.equalsIgnoreCase(name)) { |
| String metricName = getMandatoryAttribute(name, "name", attributes); |
| String metricValue = getMandatoryAttribute(name, "value", attributes); |
| if (!mBlocklistMetrics.contains(metricName)) { |
| mMetrics.addRunMetric(metricName, metricValue); |
| } |
| } |
| if (TESTMETRIC_TAG.equalsIgnoreCase(name)) { |
| String metricName = getMandatoryAttribute(name, "name", attributes); |
| String metricValue = getMandatoryAttribute(name, "value", attributes); |
| if (!mBlocklistMetrics.contains(metricName)) { |
| mMetrics.addTestMetric(mCurrentTest, metricName, metricValue); |
| } |
| } |
| } |
| |
| private String getMandatoryAttribute(String tagName, String attrName, Attributes attributes) |
| throws SAXException { |
| String value = attributes.getValue(attrName); |
| if (value == null) { |
| throw new SAXException( |
| String.format( |
| "Malformed XML, could not find '%s' attribute in '%s'", |
| attrName, tagName)); |
| } |
| return value; |
| } |
| } |
| |
| /** |
| * Parses xml data contained in given input files. |
| * |
| * @param blocklistMetrics ignore the metrics with these names |
| * @param strictMode whether to throw an exception when metric validation fails |
| * @param metricXmlFiles a list of metric xml files |
| * @return a Metric object containing metrics from all metric files |
| * @throws ParseException if input could not be parsed |
| */ |
| public static Metrics parse( |
| Set<String> blocklistMetrics, boolean strictMode, List<File> metricXmlFiles) |
| throws ParseException { |
| Metrics metrics = new Metrics(strictMode); |
| for (File xml : metricXmlFiles) { |
| try (InputStream is = new BufferedInputStream(new FileInputStream(xml))) { |
| parse(metrics, blocklistMetrics, is); |
| } catch (Exception e) { |
| throw new ParseException("Unable to parse " + xml.getPath(), e); |
| } |
| } |
| metrics.validate(metricXmlFiles.size()); |
| return metrics; |
| } |
| |
| @VisibleForTesting |
| public static Metrics parse(Metrics metrics, Set<String> blocklistMetrics, InputStream is) |
| throws ParseException { |
| try { |
| SAXParserFactory parserFactory = SAXParserFactory.newInstance(); |
| parserFactory.setNamespaceAware(true); |
| SAXParser parser = parserFactory.newSAXParser(); |
| parser.parse(is, new MetricsXmlHandler(metrics, blocklistMetrics)); |
| return metrics; |
| } catch (ParserConfigurationException | SAXException | IOException e) { |
| throw new ParseException(e); |
| } |
| } |
| } |