diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2018-02-06 08:22:18 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-02-06 08:22:18 +0000 |
commit | 6dac9721dfab925c3de7de1ad281c382172dbc05 (patch) | |
tree | ac467ccdddaa99c0cef657ab2b2326ffe3ea42e9 | |
parent | 2ef8f1d053dae9e4371190f090438cd4f193e29c (diff) | |
parent | de090fc728ead9d2297370497060dbc89d1aae22 (diff) | |
download | dashboard-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.java | 358 | ||||
-rw-r--r-- | src/main/java/com/android/vts/util/FilterUtil.java | 22 | ||||
-rw-r--r-- | src/main/webapp/WEB-INF/jsp/show_green_release.jsp | 113 | ||||
-rw-r--r-- | src/main/webapp/WEB-INF/web.xml | 10 |
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> |