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 static com.googlecode.objectify.ObjectifyService.ofy; 20 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.appengine.api.taskqueue.Queue; 25 import com.google.appengine.api.taskqueue.QueueFactory; 26 import com.google.appengine.api.taskqueue.TaskOptions; 27 import com.google.gson.JsonObject; 28 import com.google.gson.JsonPrimitive; 29 import com.googlecode.objectify.annotation.Cache; 30 import com.googlecode.objectify.annotation.Id; 31 import com.googlecode.objectify.annotation.Ignore; 32 import com.googlecode.objectify.annotation.Index; 33 import com.googlecode.objectify.annotation.Parent; 34 import java.util.Date; 35 import java.util.List; 36 import java.util.Objects; 37 import java.util.logging.Level; 38 import java.util.logging.Logger; 39 import java.util.stream.Collectors; 40 import lombok.Data; 41 import lombok.NoArgsConstructor; 42 43 @com.googlecode.objectify.annotation.Entity(name = "TestPlanRun") 44 @Cache 45 @Data 46 @NoArgsConstructor 47 /** Entity describing test plan run information. */ 48 public class TestPlanRunEntity implements DashboardEntity { 49 50 protected static final Logger logger = Logger.getLogger(TestPlanRunEntity.class.getName()); 51 52 private final String QUEUE_NAME = "coverageApiQueue"; 53 54 public static final String KIND = "TestPlanRun"; 55 56 private static final String COVERAGE_API_URL = "/api/coverage/api/sum"; 57 58 // Property keys 59 public static final String TEST_PLAN_NAME = "testPlanName"; 60 public static final String TYPE = "type"; 61 public static final String START_TIMESTAMP = "startTimestamp"; 62 public static final String END_TIMESTAMP = "endTimestamp"; 63 public static final String TEST_BUILD_ID = "testBuildId"; 64 public static final String PASS_COUNT = "passCount"; 65 public static final String FAIL_COUNT = "failCount"; 66 public static final String TOTAL_API_COUNT = "totalApiCount"; 67 public static final String TOTAL_COVERED_API_COUNT = "coveredApiCount"; 68 public static final String TEST_RUNS = "testRuns"; 69 70 @Ignore public Key key; 71 72 @Id private Long id; 73 74 @Parent private com.googlecode.objectify.Key<TestPlanEntity> parent; 75 76 @Index private String testPlanName; 77 78 @Index private long type; 79 80 @Index private long startTimestamp; 81 82 @Index private long endTimestamp; 83 84 @Index private String testBuildId; 85 86 @Index private long passCount; 87 88 @Index private long failCount; 89 90 @Index private long totalApiCount; 91 92 @Index private long coveredApiCount; 93 94 @Ignore private List<Key> oldTestRuns; 95 96 private List<com.googlecode.objectify.Key<TestRunEntity>> testRuns; 97 98 /** When this record was created or updated */ 99 @Index Date updated; 100 101 /** 102 * Create a TestPlanRunEntity object describing a test plan run. 103 * 104 * @param testPlanKey The key for the parent entity in the database. 105 * @param type The test run type (e.g. presubmit, postsubmit, other) 106 * @param startTimestamp The time in microseconds when the test plan run started. 107 * @param endTimestamp The time in microseconds when the test plan run ended. 108 * @param testBuildId The build ID of the VTS test build. 109 * @param passCount The number of passing test cases in the run. 110 * @param failCount The number of failing test cases in the run. 111 * @param testRuns A list of keys to the TestRunEntity objects for the plan run run. 112 */ TestPlanRunEntity( Key testPlanKey, String testPlanName, long type, long startTimestamp, long endTimestamp, String testBuildId, long passCount, long failCount, long totalApiCount, long coveredApiCount, List<Key> testRuns)113 public TestPlanRunEntity( 114 Key testPlanKey, 115 String testPlanName, 116 long type, 117 long startTimestamp, 118 long endTimestamp, 119 String testBuildId, 120 long passCount, 121 long failCount, 122 long totalApiCount, 123 long coveredApiCount, 124 List<Key> testRuns) { 125 this.id = startTimestamp; 126 this.key = KeyFactory.createKey(testPlanKey, KIND, startTimestamp); 127 this.testPlanName = testPlanName; 128 this.type = type; 129 this.startTimestamp = startTimestamp; 130 this.endTimestamp = endTimestamp; 131 this.testBuildId = testBuildId; 132 this.passCount = passCount; 133 this.failCount = failCount; 134 this.totalApiCount = totalApiCount; 135 this.coveredApiCount = coveredApiCount; 136 this.oldTestRuns = testRuns; 137 this.testRuns = 138 testRuns.stream() 139 .map( 140 testRun -> { 141 com.googlecode.objectify.Key testParentKey = 142 com.googlecode.objectify.Key.create( 143 TestEntity.class, 144 testRun.getParent().getName()); 145 return com.googlecode.objectify.Key.create( 146 testParentKey, TestRunEntity.class, testRun.getId()); 147 }) 148 .collect(Collectors.toList()); 149 } 150 151 /** 152 * Create a TestPlanRunEntity object describing a test plan run. 153 * 154 * @param testPlanKey The key for the parent entity in the database. 155 * @param type The test run type (e.g. presubmit, postsubmit, other) 156 * @param startTimestamp The time in microseconds when the test plan run started. 157 * @param endTimestamp The time in microseconds when the test plan run ended. 158 * @param testBuildId The build ID of the VTS test build. 159 * @param passCount The number of passing test cases in the run. 160 * @param failCount The number of failing test cases in the run. 161 * @param testRuns A list of keys to the TestRunEntity objects for the plan run run. 162 */ TestPlanRunEntity( com.googlecode.objectify.Key<TestPlanEntity> testPlanKey, String testPlanName, long type, long startTimestamp, long endTimestamp, String testBuildId, long passCount, long failCount, long totalApiCount, long coveredApiCount, List<com.googlecode.objectify.Key<TestRunEntity>> testRuns)163 public TestPlanRunEntity( 164 com.googlecode.objectify.Key<TestPlanEntity> testPlanKey, 165 String testPlanName, 166 long type, 167 long startTimestamp, 168 long endTimestamp, 169 String testBuildId, 170 long passCount, 171 long failCount, 172 long totalApiCount, 173 long coveredApiCount, 174 List<com.googlecode.objectify.Key<TestRunEntity>> testRuns) { 175 this.id = startTimestamp; 176 this.parent = testPlanKey; 177 this.testPlanName = testPlanName; 178 this.type = type; 179 this.startTimestamp = startTimestamp; 180 this.endTimestamp = endTimestamp; 181 this.testBuildId = testBuildId; 182 this.passCount = passCount; 183 this.failCount = failCount; 184 this.totalApiCount = totalApiCount; 185 this.coveredApiCount = coveredApiCount; 186 this.testRuns = testRuns; 187 } 188 toEntity()189 public Entity toEntity() { 190 Entity planRun = new Entity(this.key); 191 planRun.setProperty(TEST_PLAN_NAME, this.testPlanName); 192 planRun.setProperty(TYPE, this.type); 193 planRun.setProperty(START_TIMESTAMP, this.startTimestamp); 194 planRun.setProperty(END_TIMESTAMP, this.endTimestamp); 195 planRun.setProperty(TEST_BUILD_ID, this.testBuildId.toLowerCase()); 196 planRun.setProperty(PASS_COUNT, this.passCount); 197 planRun.setProperty(FAIL_COUNT, this.failCount); 198 if (this.oldTestRuns != null && this.oldTestRuns.size() > 0) { 199 planRun.setUnindexedProperty(TEST_RUNS, this.oldTestRuns); 200 } 201 return planRun; 202 } 203 204 /** Saving function for the instance of this class */ 205 @Override save()206 public com.googlecode.objectify.Key<TestPlanRunEntity> save() { 207 this.updated = new Date(); 208 return ofy().save().entity(this).now(); 209 } 210 211 /** Get UrlSafeKey from this class */ getUrlSafeKey()212 public String getUrlSafeKey() { 213 return this.getOfyKey().toUrlSafe(); 214 } 215 216 /** Add a task to calculate the total number of coverage API */ addCoverageApiTask()217 public void addCoverageApiTask() { 218 if (Objects.isNull(this.testRuns)) { 219 logger.log(Level.WARNING, "testRuns is null so adding task to the queue is skipped!"); 220 } else { 221 Queue queue = QueueFactory.getQueue(QUEUE_NAME); 222 queue.add( 223 TaskOptions.Builder.withUrl(COVERAGE_API_URL) 224 .param("urlSafeKey", this.getUrlSafeKey()) 225 .method(TaskOptions.Method.POST)); 226 } 227 } 228 229 /** 230 * Get key info from appengine based library. 231 * 232 * @param parentKey parent key. 233 */ getOldKey(Key parentKey)234 public Key getOldKey(Key parentKey) { 235 return KeyFactory.createKey(parentKey, KIND, startTimestamp); 236 } 237 238 /** Get key info from objecitfy library. */ getOfyKey()239 public com.googlecode.objectify.Key getOfyKey() { 240 return com.googlecode.objectify.Key.create( 241 this.parent, TestPlanRunEntity.class, this.startTimestamp); 242 } 243 244 /** 245 * Convert an Entity object to a TestPlanRunEntity. 246 * 247 * @param e The entity to process. 248 * @return TestPlanRunEntity object with the properties from e processed, or null if 249 * incompatible. 250 */ 251 @SuppressWarnings("unchecked") fromEntity(Entity e)252 public static TestPlanRunEntity fromEntity(Entity e) { 253 if (!e.getKind().equals(KIND) 254 || !e.hasProperty(TEST_PLAN_NAME) 255 || !e.hasProperty(TYPE) 256 || !e.hasProperty(START_TIMESTAMP) 257 || !e.hasProperty(END_TIMESTAMP) 258 || !e.hasProperty(TEST_BUILD_ID) 259 || !e.hasProperty(PASS_COUNT) 260 || !e.hasProperty(FAIL_COUNT) 261 || !e.hasProperty(TEST_RUNS)) { 262 logger.log(Level.WARNING, "Missing test run attributes in entity: " + e.toString()); 263 return null; 264 } 265 try { 266 String testPlanName = (String) e.getProperty(TEST_PLAN_NAME); 267 long type = (long) e.getProperty(TYPE); 268 long startTimestamp = (long) e.getProperty(START_TIMESTAMP); 269 long endTimestamp = (long) e.getProperty(END_TIMESTAMP); 270 String testBuildId = (String) e.getProperty(TEST_BUILD_ID); 271 long passCount = (long) e.getProperty(PASS_COUNT); 272 long failCount = (long) e.getProperty(FAIL_COUNT); 273 274 long totalApiCount = 275 e.hasProperty(TOTAL_API_COUNT) ? (long) e.getProperty(TOTAL_API_COUNT) : 0L; 276 long coveredApiCount = 277 e.hasProperty(TOTAL_COVERED_API_COUNT) 278 ? (long) e.getProperty(TOTAL_COVERED_API_COUNT) 279 : 0L; 280 List<Key> oldTestRuns = (List<Key>) e.getProperty(TEST_RUNS); 281 return new TestPlanRunEntity( 282 e.getKey().getParent(), 283 testPlanName, 284 type, 285 startTimestamp, 286 endTimestamp, 287 testBuildId, 288 passCount, 289 failCount, 290 totalApiCount, 291 coveredApiCount, 292 oldTestRuns); 293 } catch (ClassCastException exception) { 294 // Invalid cast 295 logger.log(Level.WARNING, "Error parsing test plan run entity.", exception); 296 } 297 return null; 298 } 299 toJson()300 public JsonObject toJson() { 301 JsonObject json = new JsonObject(); 302 json.add(TEST_PLAN_NAME, new JsonPrimitive(this.testPlanName)); 303 json.add(TEST_BUILD_ID, new JsonPrimitive(this.testBuildId)); 304 json.add(PASS_COUNT, new JsonPrimitive(this.passCount)); 305 json.add(FAIL_COUNT, new JsonPrimitive(this.failCount)); 306 json.add(START_TIMESTAMP, new JsonPrimitive(this.startTimestamp)); 307 json.add(END_TIMESTAMP, new JsonPrimitive(this.endTimestamp)); 308 return json; 309 } 310 } 311