blob: e0878073deff69762f7d9848789893ed39d8cb8a [file] [log] [blame]
/*
* 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());
}
}
}