Home | History | Annotate | Download | only in entity
      1 /*
      2  * Copyright (c) 2017 Google Inc. All Rights Reserved.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you
      5  * may not use this file except in compliance with the License. You may
      6  * obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
     13  * implied. See the License for the specific language governing
     14  * permissions and limitations under the License.
     15  */
     16 
     17 package com.android.vts.entity;
     18 
     19 import com.android.vts.util.UrlUtil;
     20 import com.android.vts.util.UrlUtil.LinkDisplay;
     21 import com.google.appengine.api.datastore.Entity;
     22 import com.google.appengine.api.datastore.Key;
     23 import com.google.appengine.api.datastore.KeyFactory;
     24 import com.google.gson.Gson;
     25 import com.google.gson.JsonElement;
     26 import com.google.gson.JsonObject;
     27 import com.google.gson.JsonPrimitive;
     28 import java.util.ArrayList;
     29 import java.util.List;
     30 import java.util.logging.Level;
     31 import java.util.logging.Logger;
     32 
     33 /** Entity describing test run information. */
     34 public class TestRunEntity implements DashboardEntity {
     35     protected static final Logger logger = Logger.getLogger(TestRunEntity.class.getName());
     36 
     37     /** Enum for classifying test run types. */
     38     public enum TestRunType {
     39         OTHER(0),
     40         PRESUBMIT(1),
     41         POSTSUBMIT(2);
     42 
     43         private final int value;
     44 
     45         private TestRunType(int value) {
     46             this.value = value;
     47         }
     48 
     49         /**
     50          * Get the ordinal representation of the type.
     51          *
     52          * @return The value associated with the test run type.
     53          */
     54         public int getNumber() {
     55             return value;
     56         }
     57 
     58         /**
     59          * Convert an ordinal value to a TestRunType.
     60          *
     61          * @param value The orginal value to parse.
     62          * @return a TestRunType value.
     63          */
     64         public static TestRunType fromNumber(int value) {
     65             if (value == 1) {
     66                 return TestRunType.PRESUBMIT;
     67             } else if (value == 2) {
     68                 return TestRunType.POSTSUBMIT;
     69             } else {
     70                 return TestRunType.OTHER;
     71             }
     72         }
     73 
     74         /**
     75          * Determine the test run type based on the build ID.
     76          *
     77          * Postsubmit runs are expected to have integer build IDs, while presubmit runs are integers
     78          * prefixed by the character P. All other runs (e.g. local builds) are classified as OTHER.
     79          *
     80          * @param buildId The build ID.
     81          * @return the TestRunType.
     82          */
     83         public static TestRunType fromBuildId(String buildId) {
     84             try {
     85                 Integer.parseInt(buildId);
     86                 return TestRunType.POSTSUBMIT;
     87             } catch (NumberFormatException e) {
     88                 // Not an integer
     89             }
     90             if (Character.toLowerCase(buildId.charAt(0)) == 'p') {
     91                 try {
     92                     Integer.parseInt(buildId.substring(1));
     93                     return TestRunType.PRESUBMIT;
     94                 } catch (NumberFormatException e) {
     95                     // Not an integer
     96                 }
     97             }
     98             return TestRunType.OTHER;
     99         }
    100     }
    101 
    102     public static final String KIND = "TestRun";
    103 
    104     // Property keys
    105     public static final String TEST_NAME = "testName";
    106     public static final String TYPE = "type";
    107     public static final String START_TIMESTAMP = "startTimestamp";
    108     public static final String END_TIMESTAMP = "endTimestamp";
    109     public static final String TEST_BUILD_ID = "testBuildId";
    110     public static final String HOST_NAME = "hostName";
    111     public static final String PASS_COUNT = "passCount";
    112     public static final String FAIL_COUNT = "failCount";
    113     public static final String TEST_CASE_IDS = "testCaseIds";
    114     public static final String LOG_LINKS = "logLinks";
    115     public static final String HAS_COVERAGE = "hasCoverage";
    116     public static final String TOTAL_LINE_COUNT = "totalLineCount";
    117     public static final String COVERED_LINE_COUNT = "coveredLineCount";
    118 
    119     public final Key key;
    120     public final TestRunType type;
    121     public final long startTimestamp;
    122     public final long endTimestamp;
    123     public final String testBuildId;
    124     public final String hostName;
    125     public final long passCount;
    126     public final long failCount;
    127     public final boolean hasCoverage;
    128     public final long coveredLineCount;
    129     public final long totalLineCount;
    130     public final List<Long> testCaseIds;
    131     public final List<String> links;
    132 
    133     /**
    134      * Create a TestRunEntity object describing a test run.
    135      *
    136      * @param parentKey The key to the parent TestEntity.
    137      * @param type The test run type (e.g. presubmit, postsubmit, other)
    138      * @param startTimestamp The time in microseconds when the test run started.
    139      * @param endTimestamp The time in microseconds when the test run ended.
    140      * @param testBuildId The build ID of the VTS test build.
    141      * @param hostName The name of host machine.
    142      * @param passCount The number of passing test cases in the run.
    143      * @param failCount The number of failing test cases in the run.
    144      * @param testCaseIds A list of key IDs to the TestCaseRunEntity objects for the test run.
    145      * @param links A list of links to resource files for the test run, or null if there aren't any.
    146      * @param coveredLineCount The number of lines covered by the test run.
    147      * @param totalLineCount The total number of executable lines by the test in the test run.
    148      */
    149     public TestRunEntity(Key parentKey, TestRunType type, long startTimestamp, long endTimestamp,
    150             String testBuildId, String hostName, long passCount, long failCount,
    151             List<Long> testCaseIds, List<String> links, long coveredLineCount,
    152             long totalLineCount) {
    153         this.key = KeyFactory.createKey(parentKey, KIND, startTimestamp);
    154         this.type = type;
    155         this.startTimestamp = startTimestamp;
    156         this.endTimestamp = endTimestamp;
    157         this.testBuildId = testBuildId;
    158         this.hostName = hostName;
    159         this.passCount = passCount;
    160         this.failCount = failCount;
    161         this.testCaseIds = testCaseIds == null ? new ArrayList<Long>() : testCaseIds;
    162         this.links = links == null ? new ArrayList<String>() : links;
    163         this.coveredLineCount = coveredLineCount;
    164         this.totalLineCount = totalLineCount;
    165         this.hasCoverage = totalLineCount > 0;
    166     }
    167 
    168     /**
    169      * Create a TestRunEntity object describing a test run.
    170      *
    171      * @param parentKey The key to the parent TestEntity.
    172      * @param type The test run type (e.g. presubmit, postsubmit, other)
    173      * @param startTimestamp The time in microseconds when the test run started.
    174      * @param endTimestamp The time in microseconds when the test run ended.
    175      * @param testBuildId The build ID of the VTS test build.
    176      * @param hostName The name of host machine.
    177      * @param passCount The number of passing test cases in the run.
    178      * @param failCount The number of failing test cases in the run.
    179      * @param testCaseIds A list of key IDs to the TestCaseRunEntity objects for the test run.
    180      * @param links A list of links to resource files for the test run, or null if there aren't any.
    181      */
    182     public TestRunEntity(Key parentKey, TestRunType type, long startTimestamp, long endTimestamp,
    183             String testBuildId, String hostName, long passCount, long failCount,
    184             List<Long> testCaseIds, List<String> links) {
    185         this(parentKey, type, startTimestamp, endTimestamp, testBuildId, hostName, passCount,
    186                 failCount, testCaseIds, links, 0, 0);
    187     }
    188 
    189     @Override
    190     public Entity toEntity() {
    191         Entity testRunEntity = new Entity(this.key);
    192         testRunEntity.setProperty(TEST_NAME, this.key.getParent().getName());
    193         testRunEntity.setProperty(TYPE, this.type.getNumber());
    194         testRunEntity.setProperty(START_TIMESTAMP, this.startTimestamp);
    195         testRunEntity.setUnindexedProperty(END_TIMESTAMP, this.endTimestamp);
    196         testRunEntity.setProperty(TEST_BUILD_ID, this.testBuildId.toLowerCase());
    197         testRunEntity.setProperty(HOST_NAME, this.hostName.toLowerCase());
    198         testRunEntity.setProperty(PASS_COUNT, this.passCount);
    199         testRunEntity.setProperty(FAIL_COUNT, this.failCount);
    200         testRunEntity.setUnindexedProperty(TEST_CASE_IDS, this.testCaseIds);
    201         boolean hasCoverage = this.totalLineCount > 0 && this.coveredLineCount >= 0;
    202         testRunEntity.setProperty(HAS_COVERAGE, hasCoverage);
    203         if (hasCoverage) {
    204             testRunEntity.setProperty(COVERED_LINE_COUNT, this.coveredLineCount);
    205             testRunEntity.setProperty(TOTAL_LINE_COUNT, this.totalLineCount);
    206         }
    207         if (this.links != null && this.links.size() > 0) {
    208             testRunEntity.setUnindexedProperty(LOG_LINKS, this.links);
    209         }
    210         return testRunEntity;
    211     }
    212 
    213     /**
    214      * Convert an Entity object to a TestRunEntity.
    215      *
    216      * @param e The entity to process.
    217      * @return TestRunEntity object with the properties from e processed, or null if incompatible.
    218      */
    219     @SuppressWarnings("unchecked")
    220     public static TestRunEntity fromEntity(Entity e) {
    221         if (!e.getKind().equals(KIND) || !e.hasProperty(TYPE) || !e.hasProperty(START_TIMESTAMP)
    222                 || !e.hasProperty(END_TIMESTAMP) || !e.hasProperty(TEST_BUILD_ID)
    223                 || !e.hasProperty(HOST_NAME) || !e.hasProperty(PASS_COUNT)
    224                 || !e.hasProperty(FAIL_COUNT) || !e.hasProperty(TEST_CASE_IDS)) {
    225             logger.log(Level.WARNING, "Missing test run attributes in entity: " + e.toString());
    226             return null;
    227         }
    228         try {
    229             TestRunType type = TestRunType.fromNumber((int) (long) e.getProperty(TYPE));
    230             long startTimestamp = (long) e.getProperty(START_TIMESTAMP);
    231             long endTimestamp = (long) e.getProperty(END_TIMESTAMP);
    232             String testBuildId = (String) e.getProperty(TEST_BUILD_ID);
    233             String hostName = (String) e.getProperty(HOST_NAME);
    234             long passCount = (long) e.getProperty(PASS_COUNT);
    235             long failCount = (long) e.getProperty(FAIL_COUNT);
    236             List<Long> testCaseIds = (List<Long>) e.getProperty(TEST_CASE_IDS);
    237             List<String> links = null;
    238             long coveredLineCount = 0;
    239             long totalLineCount = 0;
    240             if (e.hasProperty(TOTAL_LINE_COUNT) && e.hasProperty(COVERED_LINE_COUNT)) {
    241                 coveredLineCount = (long) e.getProperty(COVERED_LINE_COUNT);
    242                 totalLineCount = (long) e.getProperty(TOTAL_LINE_COUNT);
    243             }
    244             if (e.hasProperty(LOG_LINKS)) {
    245                 links = (List<String>) e.getProperty(LOG_LINKS);
    246             }
    247             return new TestRunEntity(e.getKey().getParent(), type, startTimestamp, endTimestamp,
    248                     testBuildId, hostName, passCount, failCount, testCaseIds, links,
    249                     coveredLineCount, totalLineCount);
    250         } catch (ClassCastException exception) {
    251             // Invalid cast
    252             logger.log(Level.WARNING, "Error parsing test run entity.", exception);
    253         }
    254         return null;
    255     }
    256 
    257     public JsonObject toJson() {
    258         JsonObject json = new JsonObject();
    259         json.add(TEST_NAME, new JsonPrimitive(this.key.getParent().getName()));
    260         json.add(TEST_BUILD_ID, new JsonPrimitive(this.testBuildId));
    261         json.add(HOST_NAME, new JsonPrimitive(this.hostName));
    262         json.add(PASS_COUNT, new JsonPrimitive(this.passCount));
    263         json.add(FAIL_COUNT, new JsonPrimitive(this.failCount));
    264         json.add(START_TIMESTAMP, new JsonPrimitive(this.startTimestamp));
    265         json.add(END_TIMESTAMP, new JsonPrimitive(this.endTimestamp));
    266         if (this.totalLineCount > 0 && this.coveredLineCount >= 0) {
    267             json.add(COVERED_LINE_COUNT, new JsonPrimitive(this.coveredLineCount));
    268             json.add(TOTAL_LINE_COUNT, new JsonPrimitive(this.totalLineCount));
    269         }
    270         if (this.links != null && this.links.size() > 0) {
    271             List<JsonElement> links = new ArrayList<>();
    272             for (String rawUrl : this.links) {
    273                 LinkDisplay validatedLink = UrlUtil.processUrl(rawUrl);
    274                 if (validatedLink == null) {
    275                     logger.log(Level.WARNING, "Invalid logging URL : " + rawUrl);
    276                     continue;
    277                 }
    278                 String[] logInfo = new String[] {validatedLink.name, validatedLink.url};
    279                 links.add(new Gson().toJsonTree(logInfo));
    280             }
    281             if (links.size() > 0) {
    282                 json.add(LOG_LINKS, new Gson().toJsonTree(links));
    283             }
    284         }
    285         return json;
    286     }
    287 }
    288