• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright 2016 Google Inc. All Rights Reserved.
3  *
4  * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  * <p>http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * <p>Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11  * express or implied. See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 package com.android.vts.util;
15 
16 import com.android.vts.entity.CoverageEntity;
17 import com.android.vts.entity.DeviceInfoEntity;
18 import com.android.vts.entity.ProfilingPointRunEntity;
19 import com.android.vts.entity.TestCaseRunEntity;
20 import com.android.vts.entity.TestEntity;
21 import com.android.vts.entity.TestRunEntity;
22 import com.android.vts.entity.TestRunEntity.TestRunType;
23 import com.android.vts.proto.VtsReportMessage.AndroidDeviceInfoMessage;
24 import com.android.vts.proto.VtsReportMessage.CoverageReportMessage;
25 import com.android.vts.proto.VtsReportMessage.LogMessage;
26 import com.android.vts.proto.VtsReportMessage.ProfilingReportMessage;
27 import com.android.vts.proto.VtsReportMessage.TestCaseReportMessage;
28 import com.android.vts.proto.VtsReportMessage.TestCaseResult;
29 import com.android.vts.proto.VtsReportMessage.TestReportMessage;
30 import com.google.appengine.api.datastore.DatastoreService;
31 import com.google.appengine.api.datastore.DatastoreServiceFactory;
32 import com.google.appengine.api.datastore.Entity;
33 import com.google.appengine.api.datastore.EntityNotFoundException;
34 import com.google.appengine.api.datastore.FetchOptions;
35 import com.google.appengine.api.datastore.Key;
36 import com.google.appengine.api.datastore.KeyFactory;
37 import com.google.appengine.api.datastore.KeyRange;
38 import com.google.appengine.api.datastore.PropertyProjection;
39 import com.google.appengine.api.datastore.Query;
40 import com.google.appengine.api.datastore.Query.Filter;
41 import com.google.appengine.api.datastore.Query.FilterOperator;
42 import com.google.appengine.api.datastore.Query.FilterPredicate;
43 import com.google.appengine.api.datastore.Transaction;
44 import java.io.IOException;
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.logging.Level;
48 import java.util.logging.Logger;
49 
50 /** DatastoreHelper, a helper class for interacting with Cloud Datastore. */
51 public class DatastoreHelper {
52     protected static final Logger logger = Logger.getLogger(DatastoreHelper.class.getName());
53 
54     /**
55      * Returns true if there are data points newer than lowerBound in the results table.
56      *
57      * @param testName The test to check.
58      * @param lowerBound The (exclusive) lower time bound, long, microseconds.
59      * @return boolean True if there are newer data points.
60      * @throws IOException
61      */
hasNewer(String testName, long lowerBound)62     public static boolean hasNewer(String testName, long lowerBound) throws IOException {
63         if (lowerBound <= 0)
64             return false;
65         DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
66         Key testKey = KeyFactory.createKey(TestEntity.KIND, testName);
67         Key startKey = KeyFactory.createKey(testKey, TestRunEntity.KIND, lowerBound);
68         Filter startFilter = new FilterPredicate(
69                 Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, startKey);
70         Query q = new Query(TestRunEntity.KIND)
71                           .setAncestor(KeyFactory.createKey(TestEntity.KIND, testName))
72                           .setFilter(startFilter)
73                           .setKeysOnly();
74         return datastore.prepare(q).countEntities(FetchOptions.Builder.withLimit(1)) > 0;
75     }
76 
77     /**
78      * Returns true if there are data points older than upperBound in the table.
79      *
80      * @param testName The test to check.
81      * @param upperBound The (exclusive) upper time bound, long, microseconds.
82      * @return boolean True if there are older data points.
83      * @throws IOException
84      */
hasOlder(String testName, long upperBound)85     public static boolean hasOlder(String testName, long upperBound) throws IOException {
86         if (upperBound <= 0)
87             return false;
88         DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
89         Key testKey = KeyFactory.createKey(TestEntity.KIND, testName);
90         Key endKey = KeyFactory.createKey(testKey, TestRunEntity.KIND, upperBound);
91         Filter endFilter =
92                 new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.LESS_THAN, endKey);
93         Query q = new Query(TestRunEntity.KIND)
94                           .setAncestor(KeyFactory.createKey(TestEntity.KIND, testName))
95                           .setFilter(endFilter)
96                           .setKeysOnly();
97         return datastore.prepare(q).countEntities(FetchOptions.Builder.withLimit(1)) > 0;
98     }
99 
100     /**
101      * Determines if any entities match the provided query.
102      *
103      * @param query The query to test.
104      * @return true if entities match the query.
105      */
hasEntities(Query query)106     public static boolean hasEntities(Query query) {
107         DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
108         FetchOptions limitOne = FetchOptions.Builder.withLimit(1);
109         return datastore.prepare(query).countEntities(limitOne) > 0;
110     }
111 
112     /**
113      * Get all of the target product names.
114      *
115      * @param testName the name of the test whose runs to query.
116      * @return a list of all device product names.
117      */
getAllProducts(String testName)118     public static List<String> getAllProducts(String testName) {
119         DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
120         Query query = new Query(DeviceInfoEntity.KIND)
121                               .setAncestor(KeyFactory.createKey(TestEntity.KIND, testName))
122                               .addProjection(new PropertyProjection(
123                                       DeviceInfoEntity.PRODUCT, String.class))
124                               .setDistinct(true);
125         List<String> devices = new ArrayList<>();
126         for (Entity e : datastore.prepare(query).asIterable()) {
127             devices.add((String) e.getProperty(DeviceInfoEntity.PRODUCT));
128         }
129         return devices;
130     }
131 
132     /**
133      * Upload data from a test report message
134      *
135      * @param report The test report containing data to upload.
136      */
insertData(TestReportMessage report)137     public static void insertData(TestReportMessage report) {
138         DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
139         List<Entity> puts = new ArrayList<>();
140 
141         if (!report.hasStartTimestamp() || !report.hasEndTimestamp() || !report.hasTest()
142                 || !report.hasHostInfo() || !report.hasBuildInfo()) {
143             // missing information
144             return;
145         }
146         long startTimestamp = report.getStartTimestamp();
147         long endTimestamp = report.getEndTimestamp();
148         String testName = report.getTest().toStringUtf8();
149         String testBuildId = report.getBuildInfo().getId().toStringUtf8();
150         String hostName = report.getHostInfo().getHostname().toStringUtf8();
151 
152         TestRunType testRunType = TestRunType.POSTSUBMIT;
153 
154         Entity testEntity = new TestEntity(testName).toEntity();
155         List<Long> testCaseIds = new ArrayList<>();
156 
157         Key testRunKey = KeyFactory.createKey(
158                 testEntity.getKey(), TestRunEntity.KIND, report.getStartTimestamp());
159 
160         long passCount = 0;
161         long failCount = 0;
162         long coveredLineCount = 0;
163         long totalLineCount = 0;
164 
165         // Process test cases
166         for (TestCaseReportMessage testCase : report.getTestCaseList()) {
167             String testCaseName = testCase.getName().toStringUtf8();
168             TestCaseResult result = testCase.getTestResult();
169             // Track global pass/fail counts
170             if (result == TestCaseResult.TEST_CASE_RESULT_PASS) {
171                 ++passCount;
172             } else if (result != TestCaseResult.TEST_CASE_RESULT_SKIP) {
173                 ++failCount;
174             }
175             String systraceLink = null;
176             if (testCase.getSystraceCount() > 0
177                     && testCase.getSystraceList().get(0).getUrlCount() > 0) {
178                 systraceLink = testCase.getSystraceList().get(0).getUrl(0).toStringUtf8();
179             }
180             // Process coverage data for test case
181             for (CoverageReportMessage coverage : testCase.getCoverageList()) {
182                 CoverageEntity coverageEntity =
183                         CoverageEntity.fromCoverageReport(testRunKey, testCaseName, coverage);
184                 if (coverageEntity == null) {
185                     logger.log(Level.WARNING, "Invalid coverage report in test run " + testRunKey);
186                     continue;
187                 }
188                 coveredLineCount += coverageEntity.coveredLineCount;
189                 totalLineCount += coverageEntity.totalLineCount;
190                 puts.add(coverageEntity.toEntity());
191             }
192             // Process profiling data for test case
193             for (ProfilingReportMessage profiling : testCase.getProfilingList()) {
194                 ProfilingPointRunEntity profilingEntity =
195                         ProfilingPointRunEntity.fromProfilingReport(testRunKey, profiling);
196                 if (profilingEntity == null) {
197                     logger.log(Level.WARNING, "Invalid profiling report in test run " + testRunKey);
198                 }
199                 puts.add(profilingEntity.toEntity());
200             }
201             KeyRange keys = datastore.allocateIds(TestCaseRunEntity.KIND, 1);
202             testCaseIds.add(keys.getStart().getId());
203             TestCaseRunEntity testCaseRunEntity = new TestCaseRunEntity(
204                     keys.getStart(), testCaseName, result.getNumber(), systraceLink);
205             datastore.put(testCaseRunEntity.toEntity());
206         }
207 
208         // Process device information
209         for (AndroidDeviceInfoMessage device : report.getDeviceInfoList()) {
210             DeviceInfoEntity deviceInfoEntity =
211                     DeviceInfoEntity.fromDeviceInfoMessage(testRunKey, device);
212             if (deviceInfoEntity == null) {
213                 logger.log(Level.WARNING, "Invalid device info in test run " + testRunKey);
214             }
215             if (deviceInfoEntity.buildId.charAt(0) == 'p') {
216                 testRunType = TestRunType.PRESUBMIT; // pre-submit builds begin with the letter 'p'
217             }
218             puts.add(deviceInfoEntity.toEntity());
219         }
220 
221         // Process global coverage data
222         for (CoverageReportMessage coverage : report.getCoverageList()) {
223             CoverageEntity coverageEntity =
224                     CoverageEntity.fromCoverageReport(testRunKey, new String(), coverage);
225             if (coverageEntity == null) {
226                 logger.log(Level.WARNING, "Invalid coverage report in test run " + testRunKey);
227                 continue;
228             }
229             coveredLineCount += coverageEntity.coveredLineCount;
230             totalLineCount += coverageEntity.totalLineCount;
231             puts.add(coverageEntity.toEntity());
232         }
233 
234         // Process global profiling data
235         for (ProfilingReportMessage profiling : report.getProfilingList()) {
236             ProfilingPointRunEntity profilingEntity =
237                     ProfilingPointRunEntity.fromProfilingReport(testRunKey, profiling);
238             if (profilingEntity == null) {
239                 logger.log(Level.WARNING, "Invalid profiling report in test run " + testRunKey);
240             }
241             puts.add(profilingEntity.toEntity());
242         }
243 
244         List<String> logLinks = new ArrayList<>();
245         // Process log data
246         for (LogMessage log : report.getLogList()) {
247             if (!log.hasUrl())
248                 continue;
249             logLinks.add(log.getUrl().toStringUtf8());
250         }
251 
252         try {
253             Integer.parseInt(testBuildId);
254         } catch (NumberFormatException e) {
255             testRunType = TestRunType.OTHER;
256         }
257         TestRunEntity testRunEntity = new TestRunEntity(testEntity.getKey(), testRunType,
258                 startTimestamp, endTimestamp, testBuildId, hostName, passCount, failCount,
259                 testCaseIds, logLinks, coveredLineCount, totalLineCount);
260         puts.add(testRunEntity.toEntity());
261 
262         Transaction txn = datastore.beginTransaction();
263         try {
264             // Check if test already exists in the database
265             try {
266                 datastore.get(testEntity.getKey());
267             } catch (EntityNotFoundException e) {
268                 puts.add(testEntity);
269             }
270             datastore.put(puts);
271             txn.commit();
272         } finally {
273             if (txn.isActive()) {
274                 logger.log(
275                         Level.WARNING, "Transaction rollback forced for run: " + testRunEntity.key);
276                 txn.rollback();
277             }
278         }
279     }
280 }
281