summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2018-02-06 08:22:18 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2018-02-06 08:22:18 +0000
commit6dac9721dfab925c3de7de1ad281c382172dbc05 (patch)
treeac467ccdddaa99c0cef657ab2b2326ffe3ea42e9
parent2ef8f1d053dae9e4371190f090438cd4f193e29c (diff)
parentde090fc728ead9d2297370497060dbc89d1aae22 (diff)
downloaddashboard-6dac9721dfab925c3de7de1ad281c382172dbc05.tar.gz
Snap for 4587185 from de090fc728ead9d2297370497060dbc89d1aae22 to pi-release
Change-Id: I4d9620dbdef3b3b44987cf5777cd14f8df266038
-rw-r--r--src/main/java/com/android/vts/servlet/ShowGreenReleaseServlet.java358
-rw-r--r--src/main/java/com/android/vts/util/FilterUtil.java22
-rw-r--r--src/main/webapp/WEB-INF/jsp/show_green_release.jsp113
-rw-r--r--src/main/webapp/WEB-INF/web.xml10
4 files changed, 503 insertions, 0 deletions
diff --git a/src/main/java/com/android/vts/servlet/ShowGreenReleaseServlet.java b/src/main/java/com/android/vts/servlet/ShowGreenReleaseServlet.java
new file mode 100644
index 0000000..8d03224
--- /dev/null
+++ b/src/main/java/com/android/vts/servlet/ShowGreenReleaseServlet.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2018 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.TestPlanEntity;
+import com.android.vts.entity.TestPlanRunEntity;
+import com.android.vts.util.FilterUtil;
+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.Filter;
+import com.google.appengine.api.datastore.Query.FilterOperator;
+import com.google.appengine.api.datastore.Query.FilterPredicate;
+import com.google.appengine.api.datastore.Query.SortDirection;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang.StringUtils;
+
+public class ShowGreenReleaseServlet extends BaseServlet {
+ private static final String PLAN_RELEASE_JSP = "WEB-INF/jsp/show_green_release.jsp";
+ private static final int MAX_RUNS_PER_PAGE = 9999;
+
+ /** Helper class for displaying each device build info on the green build page. */
+ public class DeviceBuildInfo implements Comparable<DeviceBuildInfo>, Cloneable {
+
+ /** Device model name ex) marlin, walleye */
+ private String deviceBuildTarget;
+ /** Candidate Build ID */
+ private String candidateBuildId;
+ /** Candidate Build ID Timestamp for url parameter */
+ private Long candidateBuildIdTimestamp;
+ /** Green Build ID */
+ private String greenBuildId;
+ /** Green Build ID Timestamp for url parameter */
+ private Long greenBuildIdTimestamp;
+
+ /**
+ * Device Build Info constructor.
+ *
+ * @param deviceBuildTarget The key of the test.
+ * @param candidateBuildId The number of tests failing.
+ */
+ public DeviceBuildInfo(String deviceBuildTarget, String candidateBuildId) {
+ this.deviceBuildTarget = deviceBuildTarget;
+ this.candidateBuildId = candidateBuildId;
+ this.candidateBuildIdTimestamp = 0L;
+ this.greenBuildId = "N/A";
+ this.greenBuildIdTimestamp = 0L;
+ }
+
+ /**
+ * Get the device name.
+ *
+ * @return The device name.
+ */
+ public String getDeviceBuildTarget() {
+ return this.deviceBuildTarget;
+ }
+
+ /**
+ * Get the candidate build ID.
+ *
+ * @return The candidate build ID.
+ */
+ public String getCandidateBuildId() {
+ return this.candidateBuildId;
+ }
+
+ /**
+ * Set the candidate build ID.
+ */
+ public void setCandidateBuildId(String candidateBuildId) {
+ this.candidateBuildId = candidateBuildId;
+ }
+
+ /**
+ * Get the candidate build ID timestamp.
+ *
+ * @return The candidate build ID timestamp.
+ */
+ public Long getCandidateBuildIdTimestamp() {
+ return this.candidateBuildIdTimestamp;
+ }
+
+ /**
+ * Set the candidate build ID timestamp.
+ */
+ public void setCandidateBuildIdTimestamp(Long candidateBuildIdTimestamp) {
+ this.candidateBuildIdTimestamp = candidateBuildIdTimestamp;
+ }
+
+ /**
+ * Get the green build ID.
+ *
+ * @return The green build ID.
+ */
+ public String getGreenBuildId() {
+ return this.greenBuildId;
+ }
+
+ /**
+ * Set the green build ID.
+ */
+ public void setGreenBuildId(String greenBuildId) {
+ this.greenBuildId = greenBuildId;
+ }
+
+
+ /**
+ * Get the candidate build ID timestamp.
+ *
+ * @return The candidate build ID timestamp.
+ */
+ public Long getGreenBuildIdTimestamp() {
+ return this.greenBuildIdTimestamp;
+ }
+
+ /**
+ * Set the candidate build ID timestamp.
+ */
+ public void setGreenBuildIdTimestamp(Long greenBuildIdTimestamp) {
+ this.greenBuildIdTimestamp = greenBuildIdTimestamp;
+ }
+
+ @Override
+ public int compareTo(DeviceBuildInfo deviceBuildInfo) {
+ return this.deviceBuildTarget.compareTo(deviceBuildInfo.getDeviceBuildTarget());
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+ }
+
+ @Override
+ public PageType getNavParentType() {
+ return PageType.RELEASE;
+ }
+
+ @Override
+ public List<Page> getBreadcrumbLinks(HttpServletRequest request) {
+ List<Page> links = new ArrayList<>();
+ String planName = request.getParameter("plan");
+ links.add(new Page(PageType.PLAN_RELEASE, planName, "?plan=" + planName));
+ return links;
+ }
+
+ // This function will build and return basic parameter HashMap based on parameter information and
+ // this value will also be used on green build page to show the basic structure.
+ private Map<String, Map<String, List<DeviceBuildInfo>>> getBasicParamMap(
+ Map<String, Map<String, List<String>>> param
+ ) {
+ Map<String, Map<String, List<DeviceBuildInfo>>> basicParamMap = new HashMap<>();
+ param.forEach((branch, productObj) -> {
+ Map<String, List<DeviceBuildInfo>> productMap = new HashMap<>();
+ productObj.forEach((product, buildTargetList) -> {
+ List<DeviceBuildInfo> deviceBuildTargetList = new ArrayList<>();
+ buildTargetList.forEach(buildTargetName -> {
+ deviceBuildTargetList.add(new DeviceBuildInfo(buildTargetName, "N/A"));
+ });
+ productMap.put(product, deviceBuildTargetList);
+ });
+ basicParamMap.put(branch, productMap);
+ });
+ return basicParamMap;
+ }
+
+ @Override
+ public void doGetHandler(HttpServletRequest request, HttpServletResponse response)
+ throws IOException {
+ DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
+
+ Map<String, Map<String, List<String>>> paramInfoMap =
+ new HashMap<String, Map<String, List<String>>>() {{
+ put("master", new HashMap<String, List<String>>()
+ {{
+ put("pixel", Arrays.asList("sailfish-userdebug", "marlin-userdebug"));
+ }}
+ );
+ put("oc-mr1", new HashMap<String, List<String>>()
+ {{
+ put("pixel", Arrays.asList("sailfish-userdebug", "marlin-userdebug"));
+ put("pixel 2", Arrays.asList("taimen-userdebug", "walleye-userdebug"));
+ put("other", Arrays.asList("aosp_arm_a-userdebug"));
+ }}
+ );
+ put("oc", new HashMap<String, List<String>>()
+ {{
+ put("pixel", Arrays.asList("sailfish-userdebug", "marlin-userdebug"));
+ }}
+ );
+ }};
+
+ String testPlan = request.getParameter("plan");
+
+ Map<String, Map<String, List<DeviceBuildInfo>>> baseParamMap = getBasicParamMap(paramInfoMap);
+ baseParamMap.forEach((branchKey, deviceObj) -> {
+ List<List<String>> allPassIdLists = new ArrayList<>();
+ Map<String, List<TestPlanRunEntity>> allTestPlanRunEntityMap = new HashMap<>();
+ logger.log(Level.INFO, "branch name => " + branchKey);
+ deviceObj.forEach((deviceName, deviceBuildInfoList) -> {
+ deviceBuildInfoList.forEach(deviceBuildInfo -> {
+
+ Map<String, Object> paramMap = new HashMap<String, Object>()
+ {{
+ put("branch", new String[]{branchKey});
+ put("device", new String[]{deviceBuildInfo.getDeviceBuildTarget()});
+ put("nonpassing", new String[]{"0"});
+ }};
+
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DATE, -7);
+ Long startTime = cal.getTime().getTime() * 1000;
+ Long endTime = Calendar.getInstance().getTime().getTime() * 1000;
+
+ logger.log(Level.INFO, "startTime => " + startTime);
+ logger.log(Level.INFO, "endTime => " + endTime);
+
+ SortDirection dir = SortDirection.DESCENDING;
+
+ boolean unfiltered = false;
+ boolean showPresubmit = false;
+ boolean showPostsubmit = true;
+ Filter typeFilter = FilterUtil.getTestTypeFilter(
+ showPresubmit,
+ showPostsubmit,
+ unfiltered
+ );
+ Key testPlanKey = KeyFactory.createKey(TestPlanEntity.KIND, testPlan);
+ Filter testPlanRunFilter =
+ FilterUtil.getTimeFilter(
+ testPlanKey, TestPlanRunEntity.KIND, startTime, endTime, typeFilter
+ );
+
+ List<Filter> userTestFilters = FilterUtil.getUserTestFilters(paramMap);
+ userTestFilters.add(0, testPlanRunFilter);
+ Filter userDeviceFilter = FilterUtil.getUserDeviceFilter(paramMap);
+
+ List<Key> matchingKeyList =
+ FilterUtil.getMatchingKeys(
+ testPlanKey, TestPlanRunEntity.KIND, userTestFilters,
+ userDeviceFilter, dir, MAX_RUNS_PER_PAGE
+ );
+
+ logger.log(Level.INFO, "the number of matching key => " + matchingKeyList.size());
+ if (matchingKeyList.size() > 0) {
+ Map<Key, Entity> entityMap = datastore.get(matchingKeyList);
+
+ List<TestPlanRunEntity> testPlanRunEntityList = entityMap.values().stream()
+ .map(entity -> TestPlanRunEntity.fromEntity(entity))
+ .collect(Collectors.toList());
+
+ allTestPlanRunEntityMap.put(
+ deviceName + "-" + deviceBuildInfo.getDeviceBuildTarget(),
+ testPlanRunEntityList
+ );
+
+ // The passBuildIdList containing all passed buildId List for device
+ List<String> passBuildIdList = testPlanRunEntityList.stream()
+ .map(entity -> entity.testBuildId)
+ .collect(Collectors.toList());
+ allPassIdLists.add(passBuildIdList);
+
+ // The logic for candidate build ID is starting from here
+ Comparator<TestPlanRunEntity> byPassing = Comparator
+ .comparingLong(elemFirst -> elemFirst.passCount);
+
+ Comparator<TestPlanRunEntity> byNonPassing = Comparator
+ .comparingLong(elemFirst -> elemFirst.failCount);
+
+ // This will get the TestPlanRunEntity having maximum number of passing and
+ // minimum number of fail
+ Optional<TestPlanRunEntity> testPlanRunEntity = testPlanRunEntityList.stream()
+ .sorted(byPassing.reversed().thenComparing(byNonPassing)).findFirst();
+
+ String buildId = testPlanRunEntity.map(entity -> entity.testBuildId).orElse("");
+ deviceBuildInfo.setCandidateBuildId(buildId);
+ Long buildIdTimestamp = testPlanRunEntity.map(entity -> {
+ return entity.startTimestamp;
+ }).orElse(0L);
+ deviceBuildInfo.setCandidateBuildIdTimestamp(buildIdTimestamp);
+ logger.log(Level.INFO, "passBuildIdList => " + passBuildIdList);
+ } else {
+ allPassIdLists.add(new ArrayList<>());
+ deviceBuildInfo.setCandidateBuildId("No Test Results");
+ }
+ });
+ });
+ Set<String> greenBuildIdList = FilterUtil.getCommonElements(allPassIdLists);
+ if (greenBuildIdList.size() > 0) {
+ String greenBuildId = greenBuildIdList.iterator().next();
+ deviceObj.forEach((deviceName, deviceBuildInfoList) -> {
+ deviceBuildInfoList.forEach(deviceBuildInfo -> {
+ // This is to get the timestamp for greenBuildId
+ Optional<TestPlanRunEntity> testPlanRunEntity = allTestPlanRunEntityMap
+ .get(deviceName + "-" + deviceBuildInfo.getDeviceBuildTarget()).stream()
+ .filter(entity -> entity.testBuildId.equalsIgnoreCase(greenBuildId))
+ .findFirst();
+ // Setting the greenBuildId value and timestamp to deviceBuildInfo object
+ deviceBuildInfo.setGreenBuildId(greenBuildId);
+ Long buildIdTimestamp = testPlanRunEntity.map(entity -> entity.startTimestamp)
+ .orElse(0L);
+ deviceBuildInfo.setGreenBuildIdTimestamp(buildIdTimestamp);
+ });
+ });
+ }
+ });
+
+ request.setAttribute("plan", request.getParameter("plan"));
+ request.setAttribute("greenBuildInfo", baseParamMap);
+ response.setStatus(HttpServletResponse.SC_OK);
+ RequestDispatcher dispatcher = request.getRequestDispatcher(PLAN_RELEASE_JSP);
+ try {
+ dispatcher.forward(request, response);
+ } catch (ServletException e) {
+ logger.log(Level.SEVERE, "Servlet Exception caught : ", e);
+ }
+ }
+}
diff --git a/src/main/java/com/android/vts/util/FilterUtil.java b/src/main/java/com/android/vts/util/FilterUtil.java
index 4bc671a..ebdb479 100644
--- a/src/main/java/com/android/vts/util/FilterUtil.java
+++ b/src/main/java/com/android/vts/util/FilterUtil.java
@@ -30,10 +30,13 @@ import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -176,6 +179,25 @@ public class FilterUtil {
}
/**
+ * Get the common elements among multiple collections.
+ *
+ * @param collections The collections containing all sub collections to find common element.
+ * @return The common elements set found from the collections param.
+ */
+ public static <T> Set<T> getCommonElements(Collection<? extends Collection<T>> collections) {
+
+ Set<T> common = new LinkedHashSet<T>();
+ if (!collections.isEmpty()) {
+ Iterator<? extends Collection<T>> iterator = collections.iterator();
+ common.addAll(iterator.next());
+ while (iterator.hasNext()) {
+ common.retainAll(iterator.next());
+ }
+ }
+ return common;
+ }
+
+ /**
* Get the first value associated with the key in the parameter map.
*
* @param parameterMap The parameter map with string keys and (Object) String[] values.
diff --git a/src/main/webapp/WEB-INF/jsp/show_green_release.jsp b/src/main/webapp/WEB-INF/jsp/show_green_release.jsp
new file mode 100644
index 0000000..d4e4f41
--- /dev/null
+++ b/src/main/webapp/WEB-INF/jsp/show_green_release.jsp
@@ -0,0 +1,113 @@
+<%--
+ ~ 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.
+ --%>
+<%@ page contentType='text/html;charset=UTF-8' language='java' %>
+<%@ taglib prefix='fn' uri='http://java.sun.com/jsp/jstl/functions' %>
+<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%>
+
+<html>
+ <%@ include file="header.jsp" %>
+ <link rel='stylesheet' href='/css/show_plan_release.css'>
+ <link rel='stylesheet' href='/css/plan_runs.css'>
+ <link rel='stylesheet' href='/css/search_header.css'>
+ <script src='https://www.gstatic.com/external_hosted/moment/min/moment-with-locales.min.js'></script>
+ <script src='js/time.js'></script>
+ <script src='js/plan_runs.js'></script>
+ <script src='js/search_header.js'></script>
+ <script type='text/javascript'>
+ var search;
+ $(document).ready(function() {
+
+ });
+ </script>
+
+ <body>
+ <div class='wide container'>
+ <div class='row' id='release-container'>
+ <table class="bordered highlight">
+ <thead>
+ <tr>
+ <th>Branch</th>
+ <th>Device</th>
+ <th>Last Finished Build</th>
+ <th>Last Green Build</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <c:forEach var="branchList" items="${greenBuildInfo}">
+ <tr>
+ <td> <c:out value="${branchList.key}"></c:out> </td>
+ <td>
+ <c:forEach varStatus="deviceLoop" var="deviceList" items="${greenBuildInfo[branchList.key]}">
+ <p> <c:out value="${deviceList.key}"></c:out> </p>
+ <br/>
+ <c:if test="${!deviceLoop.last}">
+ <hr/>
+ </c:if>
+ </c:forEach>
+ </td>
+ <td>
+ <c:forEach varStatus="deviceLoop" var="deviceList" items="${greenBuildInfo[branchList.key]}">
+ <c:forEach varStatus="deviceBuildLoop" var="deviceBuildInfo" items="${deviceList.value}">
+ <p>
+ <c:out value="${deviceBuildInfo.deviceBuildTarget}"></c:out> :
+ <c:choose>
+ <c:when test="${deviceBuildInfo.candidateBuildId eq 'No Test Results'}">
+ <c:out value="${deviceBuildInfo.candidateBuildId}"></c:out>
+ </c:when>
+ <c:otherwise>
+ <a href="/show_plan_run?plan=${plan}&time=${deviceBuildInfo.candidateBuildIdTimestamp}">
+ <c:out value="${deviceBuildInfo.candidateBuildId}"></c:out>
+ </a>
+ </c:otherwise>
+ </c:choose>
+ </p>
+ </c:forEach>
+ <c:if test="${!deviceLoop.last}">
+ <hr/>
+ </c:if>
+ </c:forEach>
+ </td>
+ <td>
+ <c:forEach varStatus="deviceLoop" var="deviceList" items="${greenBuildInfo[branchList.key]}">
+ <c:forEach varStatus="deviceBuildLoop" var="deviceBuildInfo" items="${deviceList.value}">
+ <p>
+ <c:choose>
+ <c:when test="${deviceBuildInfo.greenBuildId eq 'N/A'}">
+ <c:out value="${deviceBuildInfo.greenBuildId}"></c:out>
+ </c:when>
+ <c:otherwise>
+ <a href="/show_plan_run?plan=${plan}&time=${deviceBuildInfo.greenBuildIdTimestamp}">
+ <c:out value="${deviceBuildInfo.greenBuildId}"></c:out>
+ </a>
+ </c:otherwise>
+ </c:choose>
+ </p>
+ </c:forEach>
+ <c:if test="${!deviceLoop.last}">
+ <hr/>
+ </c:if>
+ </c:forEach>
+ </td>
+ </tr>
+ </c:forEach>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <%@ include file="footer.jsp" %>
+ </body>
+</html>
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
index bf8c2ea..dc1cf76 100644
--- a/src/main/webapp/WEB-INF/web.xml
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -26,6 +26,11 @@ Copyright 2016 Google Inc. All Rights Reserved.
</servlet>
<servlet>
+ <servlet-name>show_green_release</servlet-name>
+ <servlet-class>com.android.vts.servlet.ShowGreenReleaseServlet</servlet-class>
+</servlet>
+
+<servlet>
<servlet-name>show_coverage_overview</servlet-name>
<servlet-class>com.android.vts.servlet.ShowCoverageOverviewServlet</servlet-class>
</servlet>
@@ -146,6 +151,11 @@ Copyright 2016 Google Inc. All Rights Reserved.
</servlet-mapping>
<servlet-mapping>
+ <servlet-name>show_green_release</servlet-name>
+ <url-pattern>/show_green_release/*</url-pattern>
+</servlet-mapping>
+
+<servlet-mapping>
<servlet-name>show_coverage_overview</servlet-name>
<url-pattern>/show_coverage_overview/*</url-pattern>
</servlet-mapping>