| /* |
| * Copyright (c) 2017 Google Inc. All Rights Reserved. |
| * |
| * 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.vts.servlet; |
| |
| import com.android.vts.entity.DeviceInfoEntity; |
| import com.android.vts.entity.ProfilingPointRunEntity; |
| import com.android.vts.entity.TestCaseRunEntity; |
| import com.android.vts.entity.TestEntity; |
| import com.android.vts.entity.TestRunEntity; |
| import com.android.vts.proto.VtsReportMessage.TestCaseResult; |
| import com.android.vts.util.DatastoreHelper; |
| import com.android.vts.util.FilterUtil; |
| import com.android.vts.util.TestRunDetails; |
| import com.android.vts.util.TestRunMetadata; |
| import com.google.appengine.api.datastore.DatastoreService; |
| import com.google.appengine.api.datastore.DatastoreServiceFactory; |
| import com.google.appengine.api.datastore.Entity; |
| import com.google.appengine.api.datastore.Key; |
| import com.google.appengine.api.datastore.KeyFactory; |
| import com.google.appengine.api.datastore.Query; |
| import com.google.appengine.api.datastore.Query.Filter; |
| import com.google.appengine.api.datastore.Query.SortDirection; |
| import com.google.gson.Gson; |
| import com.google.gson.JsonObject; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.logging.Level; |
| import javax.servlet.RequestDispatcher; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| /** |
| * Servlet for handling requests to load individual tables. |
| */ |
| public class ShowTreeServlet extends BaseServlet { |
| |
| private static final String TABLE_JSP = "WEB-INF/jsp/show_tree.jsp"; |
| // Error message displayed on the webpage is tableName passed is null. |
| private static final String TABLE_NAME_ERROR = "Error : Table name must be passed!"; |
| private static final String PROFILING_DATA_ALERT = "No profiling data was found."; |
| private static final int MAX_RESULT_COUNT = 60; |
| private static final int MAX_PREFETCH_COUNT = 10; |
| |
| @Override |
| public PageType getNavParentType() { |
| return PageType.TOT; |
| } |
| |
| @Override |
| public List<Page> getBreadcrumbLinks(HttpServletRequest request) { |
| List<Page> links = new ArrayList<>(); |
| String testName = request.getParameter("testName"); |
| links.add(new Page(PageType.TREE, testName, "?testName=" + testName)); |
| return links; |
| } |
| |
| /** |
| * Get the test run details for a test run. |
| * |
| * @param metadata The metadata for the test run whose details will be fetched. |
| * @return The TestRunDetails object for the provided test run. |
| */ |
| public static TestRunDetails processTestDetails(TestRunMetadata metadata) { |
| DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); |
| TestRunDetails details = new TestRunDetails(); |
| List<Key> gets = new ArrayList<>(); |
| for (long testCaseId : metadata.testRun.getTestCaseIds()) { |
| gets.add(KeyFactory.createKey(TestCaseRunEntity.KIND, testCaseId)); |
| } |
| Map<Key, Entity> entityMap = datastore.get(gets); |
| for (int i = 0; i < 1; i++) { |
| for (Key key : entityMap.keySet()) { |
| TestCaseRunEntity testCaseRun = TestCaseRunEntity.fromEntity(entityMap.get(key)); |
| if (testCaseRun == null) { |
| continue; |
| } |
| details.addTestCase(testCaseRun); |
| } |
| } |
| return details; |
| } |
| |
| @Override |
| public void doGetHandler(HttpServletRequest request, HttpServletResponse response) |
| throws IOException { |
| boolean unfiltered = request.getParameter("unfiltered") != null; |
| boolean showPresubmit = request.getParameter("showPresubmit") != null; |
| boolean showPostsubmit = request.getParameter("showPostsubmit") != null; |
| Long startTime = null; // time in microseconds |
| Long endTime = null; // time in microseconds |
| DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); |
| RequestDispatcher dispatcher = null; |
| |
| // message to display if profiling point data is not available |
| String profilingDataAlert = ""; |
| |
| if (request.getParameter("testName") == null) { |
| request.setAttribute("testName", TABLE_NAME_ERROR); |
| return; |
| } |
| String testName = request.getParameter("testName"); |
| |
| if (request.getParameter("startTime") != null) { |
| String time = request.getParameter("startTime"); |
| try { |
| startTime = Long.parseLong(time); |
| startTime = startTime > 0 ? startTime : null; |
| } catch (NumberFormatException e) { |
| startTime = null; |
| } |
| } |
| if (request.getParameter("endTime") != null) { |
| String time = request.getParameter("endTime"); |
| try { |
| endTime = Long.parseLong(time); |
| endTime = endTime > 0 ? endTime : null; |
| } catch (NumberFormatException e) { |
| endTime = null; |
| } |
| } |
| |
| // If no params are specified, set to default of postsubmit-only. |
| if (!(showPresubmit || showPostsubmit)) { |
| showPostsubmit = true; |
| } |
| |
| // If unfiltered, set showPre- and Post-submit to true for accurate UI. |
| if (unfiltered) { |
| showPostsubmit = true; |
| showPresubmit = true; |
| } |
| |
| // Add result names to list |
| List<String> resultNames = new ArrayList<>(); |
| for (TestCaseResult r : TestCaseResult.values()) { |
| resultNames.add(r.name()); |
| } |
| |
| SortDirection dir = SortDirection.DESCENDING; |
| if (startTime != null && endTime == null) { |
| dir = SortDirection.ASCENDING; |
| } |
| Key testKey = KeyFactory.createKey(TestEntity.KIND, testName); |
| |
| Filter typeFilter = FilterUtil.getTestTypeFilter(showPresubmit, showPostsubmit, unfiltered); |
| Filter testFilter = |
| FilterUtil.getTimeFilter( |
| testKey, TestRunEntity.KIND, startTime, endTime, typeFilter); |
| |
| Map<String, String[]> parameterMap = request.getParameterMap(); |
| List<Filter> userTestFilters = FilterUtil.getUserTestFilters(parameterMap); |
| userTestFilters.add(0, testFilter); |
| Filter userDeviceFilter = FilterUtil.getUserDeviceFilter(parameterMap); |
| |
| List<TestRunMetadata> testRunMetadata = new ArrayList<>(); |
| Map<Key, TestRunMetadata> metadataMap = new HashMap<>(); |
| Key minKey = null; |
| Key maxKey = null; |
| List<Key> gets = |
| FilterUtil.getMatchingKeys( |
| testKey, |
| TestRunEntity.KIND, |
| userTestFilters, |
| userDeviceFilter, |
| dir, |
| MAX_RESULT_COUNT); |
| Map<Key, Entity> entityMap = datastore.get(gets); |
| for (Key key : gets) { |
| if (!entityMap.containsKey(key)) { |
| continue; |
| } |
| TestRunEntity testRunEntity = TestRunEntity.fromEntity(entityMap.get(key)); |
| if (testRunEntity == null) { |
| continue; |
| } |
| if (minKey == null || key.compareTo(minKey) < 0) { |
| minKey = key; |
| } |
| if (maxKey == null || key.compareTo(maxKey) > 0) { |
| maxKey = key; |
| } |
| TestRunMetadata metadata = new TestRunMetadata(testName, testRunEntity); |
| testRunMetadata.add(metadata); |
| metadataMap.put(key, metadata); |
| } |
| |
| List<String> profilingPointNames = new ArrayList<>(); |
| if (minKey != null && maxKey != null) { |
| Filter deviceFilter = |
| FilterUtil.getDeviceTimeFilter( |
| testKey, TestRunEntity.KIND, minKey.getId(), maxKey.getId()); |
| Query deviceQuery = |
| new Query(DeviceInfoEntity.KIND) |
| .setAncestor(testKey) |
| .setFilter(deviceFilter) |
| .setKeysOnly(); |
| List<Key> deviceGets = new ArrayList<>(); |
| for (Entity device : |
| datastore |
| .prepare(deviceQuery) |
| .asIterable(DatastoreHelper.getLargeBatchOptions())) { |
| if (metadataMap.containsKey(device.getParent())) { |
| deviceGets.add(device.getKey()); |
| } |
| } |
| Map<Key, Entity> devices = datastore.get(deviceGets); |
| for (Key key : devices.keySet()) { |
| if (!metadataMap.containsKey(key.getParent())) { |
| continue; |
| } |
| DeviceInfoEntity device = DeviceInfoEntity.fromEntity(devices.get(key)); |
| if (device == null) { |
| continue; |
| } |
| TestRunMetadata metadata = metadataMap.get(key.getParent()); |
| metadata.addDevice(device); |
| } |
| |
| Filter profilingFilter = |
| FilterUtil.getProfilingTimeFilter( |
| testKey, TestRunEntity.KIND, minKey.getId(), maxKey.getId()); |
| |
| Set<String> profilingPoints = new HashSet<>(); |
| Query profilingPointQuery = |
| new Query(ProfilingPointRunEntity.KIND) |
| .setAncestor(testKey) |
| .setFilter(profilingFilter) |
| .setKeysOnly(); |
| for (Entity e : datastore.prepare(profilingPointQuery).asIterable()) { |
| profilingPoints.add(e.getKey().getName()); |
| } |
| |
| if (profilingPoints.size() == 0) { |
| profilingDataAlert = PROFILING_DATA_ALERT; |
| } |
| profilingPointNames.addAll(profilingPoints); |
| profilingPointNames.sort(Comparator.naturalOrder()); |
| } |
| |
| testRunMetadata.sort( |
| (t1, t2) -> |
| new Long(t2.testRun.getStartTimestamp()).compareTo(t1.testRun.getStartTimestamp())); |
| List<JsonObject> testRunObjects = new ArrayList<>(); |
| |
| int prefetchCount = 0; |
| for (TestRunMetadata metadata : testRunMetadata) { |
| if (metadata.testRun.getFailCount() > 0 && prefetchCount < MAX_PREFETCH_COUNT) { |
| // process |
| metadata.addDetails(processTestDetails(metadata)); |
| ++prefetchCount; |
| } |
| testRunObjects.add(metadata.toJson()); |
| } |
| |
| int[] topBuildResultCounts = null; |
| String topBuild = ""; |
| if (testRunMetadata.size() > 0) { |
| TestRunMetadata firstRun = testRunMetadata.get(0); |
| topBuild = firstRun.getDeviceInfo(); |
| endTime = firstRun.testRun.getStartTimestamp(); |
| TestRunDetails topDetails = firstRun.getDetails(); |
| if (topDetails == null) { |
| topDetails = processTestDetails(firstRun); |
| } |
| topBuildResultCounts = topDetails.resultCounts; |
| |
| TestRunMetadata lastRun = testRunMetadata.get(testRunMetadata.size() - 1); |
| startTime = lastRun.testRun.getStartTimestamp(); |
| } |
| |
| FilterUtil.setAttributes(request, parameterMap); |
| |
| request.setAttribute("testName", request.getParameter("testName")); |
| |
| request.setAttribute("error", profilingDataAlert); |
| |
| request.setAttribute("profilingPointNames", profilingPointNames); |
| request.setAttribute("resultNames", resultNames); |
| request.setAttribute("resultNamesJson", new Gson().toJson(resultNames)); |
| request.setAttribute("testRuns", new Gson().toJson(testRunObjects)); |
| |
| // data for pie chart |
| request.setAttribute("topBuildResultCounts", new Gson().toJson(topBuildResultCounts)); |
| request.setAttribute("topBuildId", topBuild); |
| request.setAttribute("startTime", new Gson().toJson(startTime)); |
| request.setAttribute("endTime", new Gson().toJson(endTime)); |
| request.setAttribute( |
| "hasNewer", |
| new Gson().toJson(DatastoreHelper.hasNewer(testKey, TestRunEntity.KIND, endTime))); |
| request.setAttribute( |
| "hasOlder", |
| new Gson() |
| .toJson(DatastoreHelper.hasOlder(testKey, TestRunEntity.KIND, startTime))); |
| request.setAttribute("unfiltered", unfiltered); |
| request.setAttribute("showPresubmit", showPresubmit); |
| request.setAttribute("showPostsubmit", showPostsubmit); |
| |
| request.setAttribute("branches", new Gson().toJson(DatastoreHelper.getAllBranches())); |
| request.setAttribute("devices", new Gson().toJson(DatastoreHelper.getAllBuildFlavors())); |
| |
| dispatcher = request.getRequestDispatcher(TABLE_JSP); |
| try { |
| dispatcher.forward(request, response); |
| } catch (ServletException e) { |
| logger.log(Level.SEVERE, "Servlet Exception caught : " + e.toString()); |
| } |
| } |
| } |