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.servlet; 18 19 import com.android.vts.entity.DeviceInfoEntity; 20 import com.android.vts.entity.ProfilingPointRunEntity; 21 import com.android.vts.entity.TestCaseRunEntity; 22 import com.android.vts.entity.TestEntity; 23 import com.android.vts.entity.TestRunEntity; 24 import com.android.vts.proto.VtsReportMessage.TestCaseResult; 25 import com.android.vts.util.DatastoreHelper; 26 import com.android.vts.util.FilterUtil; 27 import com.android.vts.util.TestRunDetails; 28 import com.android.vts.util.TestRunMetadata; 29 import com.google.appengine.api.datastore.DatastoreService; 30 import com.google.appengine.api.datastore.DatastoreServiceFactory; 31 import com.google.appengine.api.datastore.Entity; 32 import com.google.appengine.api.datastore.Key; 33 import com.google.appengine.api.datastore.KeyFactory; 34 import com.google.appengine.api.datastore.Query; 35 import com.google.appengine.api.datastore.Query.Filter; 36 import com.google.appengine.api.datastore.Query.SortDirection; 37 import com.google.gson.Gson; 38 import com.google.gson.JsonObject; 39 import java.io.IOException; 40 import java.util.ArrayList; 41 import java.util.Comparator; 42 import java.util.HashMap; 43 import java.util.HashSet; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Set; 47 import java.util.logging.Level; 48 import javax.servlet.RequestDispatcher; 49 import javax.servlet.ServletException; 50 import javax.servlet.http.HttpServletRequest; 51 import javax.servlet.http.HttpServletResponse; 52 53 /** Servlet for handling requests to load individual tables. */ 54 public class ShowTreeServlet extends BaseServlet { 55 private static final String TABLE_JSP = "WEB-INF/jsp/show_tree.jsp"; 56 // Error message displayed on the webpage is tableName passed is null. 57 private static final String TABLE_NAME_ERROR = "Error : Table name must be passed!"; 58 private static final String PROFILING_DATA_ALERT = "No profiling data was found."; 59 private static final int MAX_RESULT_COUNT = 60; 60 private static final int MAX_PREFETCH_COUNT = 10; 61 62 @Override 63 public PageType getNavParentType() { 64 return PageType.TOT; 65 } 66 67 @Override 68 public List<Page> getBreadcrumbLinks(HttpServletRequest request) { 69 List<Page> links = new ArrayList<>(); 70 String testName = request.getParameter("testName"); 71 links.add(new Page(PageType.TREE, testName, "?testName=" + testName)); 72 return links; 73 } 74 75 /** 76 * Get the test run details for a test run. 77 * 78 * @param metadata The metadata for the test run whose details will be fetched. 79 * @return The TestRunDetails object for the provided test run. 80 */ 81 public static TestRunDetails processTestDetails(TestRunMetadata metadata) { 82 DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 83 TestRunDetails details = new TestRunDetails(); 84 List<Key> gets = new ArrayList<>(); 85 for (long testCaseId : metadata.testRun.testCaseIds) { 86 gets.add(KeyFactory.createKey(TestCaseRunEntity.KIND, testCaseId)); 87 } 88 Map<Key, Entity> entityMap = datastore.get(gets); 89 for (int i = 0; i < 1; i++) { 90 for (Key key : entityMap.keySet()) { 91 TestCaseRunEntity testCaseRun = TestCaseRunEntity.fromEntity(entityMap.get(key)); 92 if (testCaseRun == null) { 93 continue; 94 } 95 details.addTestCase(testCaseRun); 96 } 97 } 98 return details; 99 } 100 101 @Override 102 public void doGetHandler(HttpServletRequest request, HttpServletResponse response) 103 throws IOException { 104 boolean unfiltered = request.getParameter("unfiltered") != null; 105 boolean showPresubmit = request.getParameter("showPresubmit") != null; 106 boolean showPostsubmit = request.getParameter("showPostsubmit") != null; 107 Long startTime = null; // time in microseconds 108 Long endTime = null; // time in microseconds 109 DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 110 RequestDispatcher dispatcher = null; 111 112 // message to display if profiling point data is not available 113 String profilingDataAlert = ""; 114 115 if (request.getParameter("testName") == null) { 116 request.setAttribute("testName", TABLE_NAME_ERROR); 117 return; 118 } 119 String testName = request.getParameter("testName"); 120 121 if (request.getParameter("startTime") != null) { 122 String time = request.getParameter("startTime"); 123 try { 124 startTime = Long.parseLong(time); 125 startTime = startTime > 0 ? startTime : null; 126 } catch (NumberFormatException e) { 127 startTime = null; 128 } 129 } 130 if (request.getParameter("endTime") != null) { 131 String time = request.getParameter("endTime"); 132 try { 133 endTime = Long.parseLong(time); 134 endTime = endTime > 0 ? endTime : null; 135 } catch (NumberFormatException e) { 136 endTime = null; 137 } 138 } 139 140 // If no params are specified, set to default of postsubmit-only. 141 if (!(showPresubmit || showPostsubmit)) { 142 showPostsubmit = true; 143 } 144 145 // If unfiltered, set showPre- and Post-submit to true for accurate UI. 146 if (unfiltered) { 147 showPostsubmit = true; 148 showPresubmit = true; 149 } 150 151 // Add result names to list 152 List<String> resultNames = new ArrayList<>(); 153 for (TestCaseResult r : TestCaseResult.values()) { 154 resultNames.add(r.name()); 155 } 156 157 SortDirection dir = SortDirection.DESCENDING; 158 if (startTime != null && endTime == null) { 159 dir = SortDirection.ASCENDING; 160 } 161 Key testKey = KeyFactory.createKey(TestEntity.KIND, testName); 162 163 Filter typeFilter = FilterUtil.getTestTypeFilter(showPresubmit, showPostsubmit, unfiltered); 164 Filter testFilter = 165 FilterUtil.getTimeFilter( 166 testKey, TestRunEntity.KIND, startTime, endTime, typeFilter); 167 168 Map<String, Object> parameterMap = request.getParameterMap(); 169 List<Filter> userTestFilters = FilterUtil.getUserTestFilters(parameterMap); 170 userTestFilters.add(0, testFilter); 171 Filter userDeviceFilter = FilterUtil.getUserDeviceFilter(parameterMap); 172 173 List<TestRunMetadata> testRunMetadata = new ArrayList<>(); 174 Map<Key, TestRunMetadata> metadataMap = new HashMap<>(); 175 Key minKey = null; 176 Key maxKey = null; 177 List<Key> gets = 178 FilterUtil.getMatchingKeys( 179 testKey, 180 TestRunEntity.KIND, 181 userTestFilters, 182 userDeviceFilter, 183 dir, 184 MAX_RESULT_COUNT); 185 Map<Key, Entity> entityMap = datastore.get(gets); 186 for (Key key : gets) { 187 if (!entityMap.containsKey(key)) { 188 continue; 189 } 190 TestRunEntity testRunEntity = TestRunEntity.fromEntity(entityMap.get(key)); 191 if (testRunEntity == null) { 192 continue; 193 } 194 if (minKey == null || key.compareTo(minKey) < 0) { 195 minKey = key; 196 } 197 if (maxKey == null || key.compareTo(maxKey) > 0) { 198 maxKey = key; 199 } 200 TestRunMetadata metadata = new TestRunMetadata(testName, testRunEntity); 201 testRunMetadata.add(metadata); 202 metadataMap.put(key, metadata); 203 } 204 205 List<String> profilingPointNames = new ArrayList<>(); 206 if (minKey != null && maxKey != null) { 207 Filter deviceFilter = 208 FilterUtil.getDeviceTimeFilter( 209 testKey, TestRunEntity.KIND, minKey.getId(), maxKey.getId()); 210 Query deviceQuery = 211 new Query(DeviceInfoEntity.KIND) 212 .setAncestor(testKey) 213 .setFilter(deviceFilter) 214 .setKeysOnly(); 215 List<Key> deviceGets = new ArrayList<>(); 216 for (Entity device : 217 datastore 218 .prepare(deviceQuery) 219 .asIterable(DatastoreHelper.getLargeBatchOptions())) { 220 if (metadataMap.containsKey(device.getParent())) { 221 deviceGets.add(device.getKey()); 222 } 223 } 224 Map<Key, Entity> devices = datastore.get(deviceGets); 225 for (Key key : devices.keySet()) { 226 if (!metadataMap.containsKey(key.getParent())) continue; 227 DeviceInfoEntity device = DeviceInfoEntity.fromEntity(devices.get(key)); 228 if (device == null) continue; 229 TestRunMetadata metadata = metadataMap.get(key.getParent()); 230 metadata.addDevice(device); 231 } 232 233 Filter profilingFilter = 234 FilterUtil.getProfilingTimeFilter( 235 testKey, TestRunEntity.KIND, minKey.getId(), maxKey.getId()); 236 237 Set<String> profilingPoints = new HashSet<>(); 238 Query profilingPointQuery = 239 new Query(ProfilingPointRunEntity.KIND) 240 .setAncestor(testKey) 241 .setFilter(profilingFilter) 242 .setKeysOnly(); 243 for (Entity e : datastore.prepare(profilingPointQuery).asIterable()) { 244 profilingPoints.add(e.getKey().getName()); 245 } 246 247 if (profilingPoints.size() == 0) { 248 profilingDataAlert = PROFILING_DATA_ALERT; 249 } 250 profilingPointNames.addAll(profilingPoints); 251 profilingPointNames.sort(Comparator.naturalOrder()); 252 } 253 254 testRunMetadata.sort( 255 (t1, t2) -> 256 new Long(t2.testRun.startTimestamp).compareTo(t1.testRun.startTimestamp)); 257 List<JsonObject> testRunObjects = new ArrayList<>(); 258 259 int prefetchCount = 0; 260 for (TestRunMetadata metadata : testRunMetadata) { 261 if (metadata.testRun.failCount > 0 && prefetchCount < MAX_PREFETCH_COUNT) { 262 // process 263 metadata.addDetails(processTestDetails(metadata)); 264 ++prefetchCount; 265 } 266 testRunObjects.add(metadata.toJson()); 267 } 268 269 int[] topBuildResultCounts = null; 270 String topBuild = ""; 271 if (testRunMetadata.size() > 0) { 272 TestRunMetadata firstRun = testRunMetadata.get(0); 273 topBuild = firstRun.getDeviceInfo(); 274 endTime = firstRun.testRun.startTimestamp; 275 TestRunDetails topDetails = firstRun.getDetails(); 276 if (topDetails == null) { 277 topDetails = processTestDetails(firstRun); 278 } 279 topBuildResultCounts = topDetails.resultCounts; 280 281 TestRunMetadata lastRun = testRunMetadata.get(testRunMetadata.size() - 1); 282 startTime = lastRun.testRun.startTimestamp; 283 } 284 285 FilterUtil.setAttributes(request, parameterMap); 286 287 request.setAttribute("testName", request.getParameter("testName")); 288 289 request.setAttribute("error", profilingDataAlert); 290 291 request.setAttribute("profilingPointNames", profilingPointNames); 292 request.setAttribute("resultNames", resultNames); 293 request.setAttribute("resultNamesJson", new Gson().toJson(resultNames)); 294 request.setAttribute("testRuns", new Gson().toJson(testRunObjects)); 295 296 // data for pie chart 297 request.setAttribute("topBuildResultCounts", new Gson().toJson(topBuildResultCounts)); 298 request.setAttribute("topBuildId", topBuild); 299 request.setAttribute("startTime", new Gson().toJson(startTime)); 300 request.setAttribute("endTime", new Gson().toJson(endTime)); 301 request.setAttribute( 302 "hasNewer", 303 new Gson().toJson(DatastoreHelper.hasNewer(testKey, TestRunEntity.KIND, endTime))); 304 request.setAttribute( 305 "hasOlder", 306 new Gson() 307 .toJson(DatastoreHelper.hasOlder(testKey, TestRunEntity.KIND, startTime))); 308 request.setAttribute("unfiltered", unfiltered); 309 request.setAttribute("showPresubmit", showPresubmit); 310 request.setAttribute("showPostsubmit", showPostsubmit); 311 312 request.setAttribute("branches", new Gson().toJson(DatastoreHelper.getAllBranches())); 313 request.setAttribute("devices", new Gson().toJson(DatastoreHelper.getAllBuildFlavors())); 314 315 dispatcher = request.getRequestDispatcher(TABLE_JSP); 316 try { 317 dispatcher.forward(request, response); 318 } catch (ServletException e) { 319 logger.log(Level.SEVERE, "Servlet Exception caught : " + e.toString()); 320 } 321 } 322 } 323