diff options
author | Young Gyu Park <younggyu@google.com> | 2018-10-11 16:42:21 +0900 |
---|---|---|
committer | Young Gyu Park <younggyu@google.com> | 2018-10-18 16:54:41 +0900 |
commit | 1ca21fadcdba5fd35e135dd07d581171102e007d (patch) | |
tree | 4119807f30a8908acf64a661bc80574599a3d8ee | |
parent | 88aeab12aaf3016ed10edd6fce991986e2df53ed (diff) | |
download | dashboard-1ca21fadcdba5fd35e135dd07d581171102e007d.tar.gz |
Search by branch and device on show_coverage_overview
Test: go/vts-web-staging
Bug: 117586256
Change-Id: Ie5c59ffdf6595a5397bb0c81fa3398ca05adcd40
12 files changed, 835 insertions, 257 deletions
diff --git a/src/main/java/com/android/vts/api/DataRestServlet.java b/src/main/java/com/android/vts/api/DataRestServlet.java new file mode 100644 index 0000000..409818e --- /dev/null +++ b/src/main/java/com/android/vts/api/DataRestServlet.java @@ -0,0 +1,81 @@ +/* + * 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.api; + +import com.android.vts.entity.BranchEntity; +import com.android.vts.entity.TestCoverageStatusEntity; +import com.google.gson.Gson; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.android.vts.entity.BuildTargetEntity; + +/** REST endpoint for getting all branch and buildFlavors information. */ +public class DataRestServlet extends BaseApiServlet { + + private static final Logger logger = Logger.getLogger(DataRestServlet.class.getName()); + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + String pathInfo = request.getPathInfo(); + String json = ""; + if (Objects.nonNull(pathInfo)) { + String schKey = + Objects.isNull(request.getParameter("schKey")) + ? "" + : request.getParameter("schKey"); + if (pathInfo.equalsIgnoreCase("/branch")) { + json = new Gson().toJson(BranchEntity.getByBranch(schKey)); + } else if (pathInfo.equalsIgnoreCase("/device")) { + json = new Gson().toJson(BuildTargetEntity.getByBuildTarget(schKey)); + } else if (pathInfo.startsWith("/code/coverage/status/")) { + List<TestCoverageStatusEntity> testCoverageStatusEntityList = + TestCoverageStatusEntity.getAllTestCoverage(); + if (pathInfo.endsWith("branch")) { + json = + new Gson() + .toJson( + TestCoverageStatusEntity.getBranchSet( + testCoverageStatusEntityList)); + } else { + json = + new Gson() + .toJson( + TestCoverageStatusEntity.getDeviceSet( + testCoverageStatusEntityList)); + } + } else { + json = "{error: 'true', message: 'unexpected path!!!'}"; + logger.log(Level.INFO, "Path Info => " + pathInfo); + logger.log(Level.WARNING, "Unknown path access!"); + } + } else { + json = "{error: 'true', message: 'the path info is not existed!!!'}"; + } + + response.setStatus(HttpServletResponse.SC_OK); + response.setContentType("application/json"); + response.setCharacterEncoding("UTF-8"); + response.getWriter().write(json); + } +} diff --git a/src/main/java/com/android/vts/config/ObjectifyListener.java b/src/main/java/com/android/vts/config/ObjectifyListener.java index 2610ab8..66f1480 100644 --- a/src/main/java/com/android/vts/config/ObjectifyListener.java +++ b/src/main/java/com/android/vts/config/ObjectifyListener.java @@ -17,6 +17,8 @@ package com.android.vts.config; import com.android.vts.entity.ApiCoverageEntity; +import com.android.vts.entity.BranchEntity; +import com.android.vts.entity.BuildTargetEntity; import com.android.vts.entity.CodeCoverageEntity; import com.android.vts.entity.CoverageEntity; import com.android.vts.entity.DeviceInfoEntity; @@ -47,12 +49,10 @@ import javax.servlet.annotation.WebListener; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.util.List; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; -import static com.googlecode.objectify.ObjectifyService.ofy; /** * The @WebListener annotation for registering a class as a listener of a web application. @@ -73,6 +73,9 @@ public class ObjectifyListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { ObjectifyService.init(); + ObjectifyService.register(BranchEntity.class); + ObjectifyService.register(BuildTargetEntity.class); + ObjectifyService.register(ApiCoverageEntity.class); ObjectifyService.register(CodeCoverageEntity.class); ObjectifyService.register(CoverageEntity.class); diff --git a/src/main/java/com/android/vts/entity/BranchEntity.java b/src/main/java/com/android/vts/entity/BranchEntity.java index f1b5a4c..fa3f7a5 100644 --- a/src/main/java/com/android/vts/entity/BranchEntity.java +++ b/src/main/java/com/android/vts/entity/BranchEntity.java @@ -19,9 +19,22 @@ package com.android.vts.entity; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; +import com.googlecode.objectify.annotation.Cache; +import com.googlecode.objectify.annotation.Id; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; + +import static com.googlecode.objectify.ObjectifyService.ofy; +@com.googlecode.objectify.annotation.Entity(name = "Branch") +@Cache +@Data +@NoArgsConstructor /** Entity describing a branch. */ public class BranchEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(BranchEntity.class.getName()); @@ -30,6 +43,8 @@ public class BranchEntity implements DashboardEntity { public Key key; // The key for the entity in the database. + @Id private String name; + /** * Create a BranchEntity object. * @@ -39,6 +54,38 @@ public class BranchEntity implements DashboardEntity { this.key = KeyFactory.createKey(KIND, branchName); } + /** find by branch name */ + public static List<String> getByBranch(String branchName) { + if (branchName.equals("*")) { + return ofy().load() + .type(BranchEntity.class) + .limit(100) + .list() + .stream() + .map(b -> b.name) + .collect(Collectors.toList()); + } else { + com.googlecode.objectify.Key startKey = + com.googlecode.objectify.Key.create(BranchEntity.class, branchName); + + int lastPosition = branchName.length() - 1; + int lastCharValue = branchName.charAt(lastPosition); + String nextChar = String.valueOf((char) (lastCharValue + 1)); + + String nextBranchName = branchName.substring(0, lastPosition) + nextChar; + com.googlecode.objectify.Key endKey = + com.googlecode.objectify.Key.create(BranchEntity.class, nextBranchName); + return ofy().load() + .type(BranchEntity.class) + .filterKey(">=", startKey) + .filterKey("<", endKey) + .list() + .stream() + .map(b -> b.name) + .collect(Collectors.toList()); + } + } + @Override public Entity toEntity() { return new Entity(this.key); diff --git a/src/main/java/com/android/vts/entity/BuildTargetEntity.java b/src/main/java/com/android/vts/entity/BuildTargetEntity.java index ddcd4f8..b204d0c 100644 --- a/src/main/java/com/android/vts/entity/BuildTargetEntity.java +++ b/src/main/java/com/android/vts/entity/BuildTargetEntity.java @@ -19,9 +19,22 @@ package com.android.vts.entity; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; +import com.googlecode.objectify.annotation.Cache; +import com.googlecode.objectify.annotation.Id; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; + +import static com.googlecode.objectify.ObjectifyService.ofy; +@com.googlecode.objectify.annotation.Entity(name = "BuildTarget") +@Cache +@Data +@NoArgsConstructor /** Entity describing a build target. */ public class BuildTargetEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(BuildTargetEntity.class.getName()); @@ -30,6 +43,8 @@ public class BuildTargetEntity implements DashboardEntity { public Key key; // The key for the entity in the database. + @Id private String name; + /** * Create a BuildTargetEntity object. * @@ -44,6 +59,39 @@ public class BuildTargetEntity implements DashboardEntity { return new Entity(this.key); } + /** find by Build Target Name */ + public static List<String> getByBuildTarget(String buildTargetName) { + if (buildTargetName.equals("*")) { + return ofy().load() + .type(BuildTargetEntity.class) + .limit(100) + .list() + .stream() + .map(b -> b.name) + .collect(Collectors.toList()); + } else { + com.googlecode.objectify.Key startKey = + com.googlecode.objectify.Key.create(BuildTargetEntity.class, buildTargetName); + + int lastPosition = buildTargetName.length() - 1; + int lastCharValue = buildTargetName.charAt(lastPosition); + String nextChar = String.valueOf((char) (lastCharValue + 1)); + + String nextBuildTargetName = buildTargetName.substring(0, lastPosition) + nextChar; + com.googlecode.objectify.Key endKey = + com.googlecode.objectify.Key.create( + BuildTargetEntity.class, nextBuildTargetName); + return ofy().load() + .type(BuildTargetEntity.class) + .filterKey(">=", startKey) + .filterKey("<", endKey) + .list() + .stream() + .map(b -> b.name) + .collect(Collectors.toList()); + } + } + /** * Convert an Entity object to a BuildTargetEntity. * diff --git a/src/main/java/com/android/vts/entity/DeviceInfoEntity.java b/src/main/java/com/android/vts/entity/DeviceInfoEntity.java index 6992983..d0c3d00 100644 --- a/src/main/java/com/android/vts/entity/DeviceInfoEntity.java +++ b/src/main/java/com/android/vts/entity/DeviceInfoEntity.java @@ -21,6 +21,7 @@ import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.memcache.MemcacheService; import com.google.appengine.api.memcache.MemcacheServiceFactory; +import com.google.apphosting.api.ApiProxy; import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Ignore; @@ -106,8 +107,14 @@ public class DeviceInfoEntity implements Serializable { * @param abiBitness The abi bitness of the device. * @param abiName The name of the abi. */ - public DeviceInfoEntity(Key parentKey, String branch, String product, String buildFlavor, - String buildID, String abiBitness, String abiName) { + public DeviceInfoEntity( + Key parentKey, + String branch, + String product, + String buildFlavor, + String buildID, + String abiBitness, + String abiName) { this.parentKey = parentKey; this.branch = branch; this.product = product; @@ -118,12 +125,54 @@ public class DeviceInfoEntity implements Serializable { } /** + * Create a DeviceInfoEntity object with objectify Key + * + * @param parent The objectify key for the parent entity in the database. + * @param branch The build branch. + * @param product The device product. + * @param buildFlavor The device build flavor. + * @param buildID The device build ID. + * @param abiBitness The abi bitness of the device. + * @param abiName The name of the abi. + */ + public DeviceInfoEntity( + com.googlecode.objectify.Key parent, + String branch, + String product, + String buildFlavor, + String buildID, + String abiBitness, + String abiName) { + this.parent = parent; + this.branch = branch; + this.product = product; + this.buildFlavor = buildFlavor; + this.buildId = buildID; + this.abiBitness = abiBitness; + this.abiName = abiName; + } + + /** * Get All Branch List from DeviceInfoEntity */ public static List<String> getAllBranches() { - List<String> branchList = (List<String>) syncCache.get("branchList"); - if (Objects.isNull(branchList)) { - branchList = ofy().load() + try { + List<String> branchList = (List<String>) syncCache.get("branchList"); + if (Objects.isNull(branchList)) { + branchList = + ofy().load() + .type(DeviceInfoEntity.class) + .project("branch") + .distinct(true) + .list() + .stream() + .map(device -> device.branch) + .collect(Collectors.toList()); + syncCache.put("branchList", branchList); + } + return branchList; + } catch (ApiProxy.CallNotFoundException e) { + return ofy().load() .type(DeviceInfoEntity.class) .project("branch") .distinct(true) @@ -131,18 +180,30 @@ public class DeviceInfoEntity implements Serializable { .stream() .map(device -> device.branch) .collect(Collectors.toList()); - syncCache.put("branchList", branchList); } - return branchList; } /** * Get All BuildFlavors List from DeviceInfoEntity */ public static List<String> getAllBuildFlavors() { - List<String> buildFlavorList = (List<String>) syncCache.get("buildFlavorList"); - if (Objects.isNull(buildFlavorList)) { - buildFlavorList = ofy().load() + try { + List<String> buildFlavorList = (List<String>) syncCache.get("buildFlavorList"); + if (Objects.isNull(buildFlavorList)) { + buildFlavorList = + ofy().load() + .type(DeviceInfoEntity.class) + .project("buildFlavor") + .distinct(true) + .list() + .stream() + .map(device -> device.buildFlavor) + .collect(Collectors.toList()); + syncCache.put("buildFlavorList", buildFlavorList); + } + return buildFlavorList; + } catch (ApiProxy.CallNotFoundException e) { + return ofy().load() .type(DeviceInfoEntity.class) .project("buildFlavor") .distinct(true) @@ -150,9 +211,12 @@ public class DeviceInfoEntity implements Serializable { .stream() .map(device -> device.buildFlavor) .collect(Collectors.toList()); - syncCache.put("buildFlavorList", buildFlavorList); } - return buildFlavorList; + } + + /** Saving function for the instance of this class */ + public void save() { + ofy().save().entity(this).now(); } public Entity toEntity() { diff --git a/src/main/java/com/android/vts/entity/TestCoverageStatusEntity.java b/src/main/java/com/android/vts/entity/TestCoverageStatusEntity.java index 12572ce..cc630c3 100644 --- a/src/main/java/com/android/vts/entity/TestCoverageStatusEntity.java +++ b/src/main/java/com/android/vts/entity/TestCoverageStatusEntity.java @@ -19,13 +19,16 @@ package com.android.vts.entity; import static com.googlecode.objectify.ObjectifyService.ofy; import com.google.appengine.api.datastore.Entity; +import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Index; import java.io.Serializable; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -50,6 +53,7 @@ public class TestCoverageStatusEntity implements Serializable { public static final String TOTAL_LINE_COUNT = "totalLineCount"; public static final String COVERED_LINE_COUNT = "coveredLineCount"; public static final String UPDATED_TIMESTAMP = "updatedTimestamp"; + public static final String DEVICE_INFO_ID = "deviceInfoId"; /** TestCoverageStatusEntity name field */ @Id @Getter @Setter String testName; @@ -63,6 +67,9 @@ public class TestCoverageStatusEntity implements Serializable { /** TestCoverageStatusEntity updatedTimestamp field */ @Index @Getter @Setter long updatedTimestamp; + /** TestCoverageStatusEntity DeviceInfo Entity ID reference field */ + @Index @Getter @Setter long deviceInfoId; + /** TestCoverageStatusEntity updatedCoveredLineCount field */ @Index @Getter @Setter long updatedCoveredLineCount; @@ -81,11 +88,16 @@ public class TestCoverageStatusEntity implements Serializable { * @param totalLineCount The total number of lines. */ public TestCoverageStatusEntity( - String testName, long timestamp, long coveredLineCount, long totalLineCount) { + String testName, + long timestamp, + long coveredLineCount, + long totalLineCount, + long deviceInfoId) { this.testName = testName; this.updatedTimestamp = timestamp; this.coveredLineCount = coveredLineCount; this.totalLineCount = totalLineCount; + this.deviceInfoId = deviceInfoId; } /** find TestCoverageStatus entity by ID */ @@ -103,11 +115,69 @@ public class TestCoverageStatusEntity implements Serializable { return testCoverageStatusMap; } + /** Get all DeviceInfoEntity List by TestCoverageStatusEntities' key list */ + public static List<Key<DeviceInfoEntity>> getDeviceInfoEntityKeyList( + List<TestCoverageStatusEntity> testCoverageStatusEntityList) { + return testCoverageStatusEntityList + .stream() + .map( + testCoverageStatusEntity -> { + com.googlecode.objectify.Key testKey = + com.googlecode.objectify.Key.create( + TestEntity.class, + testCoverageStatusEntity.getTestName()); + com.googlecode.objectify.Key testRunKey = + com.googlecode.objectify.Key.create( + testKey, + TestRunEntity.class, + testCoverageStatusEntity.getUpdatedTimestamp()); + return com.googlecode.objectify.Key.create( + testRunKey, + DeviceInfoEntity.class, + testCoverageStatusEntity.getDeviceInfoId()); + }) + .collect(Collectors.toList()); + } + /** Get all TestCoverageStatusEntity List */ public static List<TestCoverageStatusEntity> getAllTestCoverage() { return ofy().load().type(TestCoverageStatusEntity.class).list(); } + /** Get all TestCoverageStatusEntities' Branch List */ + public static Set<String> getBranchSet( + List<TestCoverageStatusEntity> testCoverageStatusEntityList) { + List<com.googlecode.objectify.Key<DeviceInfoEntity>> deviceInfoEntityKeyList = + getDeviceInfoEntityKeyList(testCoverageStatusEntityList); + + Collection<DeviceInfoEntity> deviceInfoEntityList = + ofy().load().keys(() -> deviceInfoEntityKeyList.iterator()).values(); + + Set<String> branchSet = + deviceInfoEntityList + .stream() + .map(deviceInfoEntity -> deviceInfoEntity.getBranch()) + .collect(Collectors.toSet()); + return branchSet; + } + + /** Get all TestCoverageStatusEntities' Device List */ + public static Set<String> getDeviceSet( + List<TestCoverageStatusEntity> testCoverageStatusEntityList) { + List<com.googlecode.objectify.Key<DeviceInfoEntity>> deviceInfoEntityKeyList = + getDeviceInfoEntityKeyList(testCoverageStatusEntityList); + + Collection<DeviceInfoEntity> deviceInfoEntityList = + ofy().load().keys(() -> deviceInfoEntityKeyList.iterator()).values(); + + Set<String> deviceSet = + deviceInfoEntityList + .stream() + .map(deviceInfoEntity -> deviceInfoEntity.getBuildFlavor()) + .collect(Collectors.toSet()); + return deviceSet; + } + /** TestRunEntity function to get the related TestRunEntity from id value */ public TestRunEntity getTestRunEntity() { com.googlecode.objectify.Key testKey = @@ -146,7 +216,8 @@ public class TestCoverageStatusEntity implements Serializable { || e.getKey().getName() == null || !e.hasProperty(UPDATED_TIMESTAMP) || !e.hasProperty(COVERED_LINE_COUNT) - || !e.hasProperty(TOTAL_LINE_COUNT)) { + || !e.hasProperty(TOTAL_LINE_COUNT) + || !e.hasProperty(DEVICE_INFO_ID)) { logger.log(Level.WARNING, "Missing test attributes in entity: " + e.toString()); return null; } @@ -154,15 +225,18 @@ public class TestCoverageStatusEntity implements Serializable { long timestamp = 0; long coveredLineCount = -1; long totalLineCount = -1; + long deviceInfoId = 0; try { timestamp = (long) e.getProperty(UPDATED_TIMESTAMP); coveredLineCount = (Long) e.getProperty(COVERED_LINE_COUNT); totalLineCount = (Long) e.getProperty(TOTAL_LINE_COUNT); + deviceInfoId = (Long) e.getProperty(DEVICE_INFO_ID); } catch (ClassCastException exception) { // Invalid contents or null values logger.log(Level.WARNING, "Error parsing test entity.", exception); return null; } - return new TestCoverageStatusEntity(testName, timestamp, coveredLineCount, totalLineCount); + return new TestCoverageStatusEntity( + testName, timestamp, coveredLineCount, totalLineCount, deviceInfoId); } } diff --git a/src/main/java/com/android/vts/job/VtsCoverageAlertJobServlet.java b/src/main/java/com/android/vts/job/VtsCoverageAlertJobServlet.java index 106ffb5..b5453b7 100644 --- a/src/main/java/com/android/vts/job/VtsCoverageAlertJobServlet.java +++ b/src/main/java/com/android/vts/job/VtsCoverageAlertJobServlet.java @@ -261,11 +261,12 @@ public class VtsCoverageAlertJobServlet extends BaseJobServlet { logger.log(Level.WARNING, "Error composing email : ", e); } } - return new TestCoverageStatusEntity( - testName, - testRunEntity.getStartTimestamp(), - codeCoverageEntity.getCoveredLineCount(), - codeCoverageEntity.getTotalLineCount()); + return new TestCoverageStatusEntity( + testName, + testRunEntity.getStartTimestamp(), + codeCoverageEntity.getCoveredLineCount(), + codeCoverageEntity.getTotalLineCount(), + devices.size() > 0 ? devices.get(0).getId() : 0); } /** @@ -299,7 +300,7 @@ public class VtsCoverageAlertJobServlet extends BaseJobServlet { TestCoverageStatusEntity status = ofy().load().type(TestCoverageStatusEntity.class).id(testName) .now(); if (status == null) { - status = new TestCoverageStatusEntity(testName, 0, -1, -1); + status = new TestCoverageStatusEntity(testName, 0, -1, -1, 0); } StringBuffer fullUrl = request.getRequestURL(); diff --git a/src/main/java/com/android/vts/servlet/ShowCoverageOverviewServlet.java b/src/main/java/com/android/vts/servlet/ShowCoverageOverviewServlet.java index 719ade9..210c980 100644 --- a/src/main/java/com/android/vts/servlet/ShowCoverageOverviewServlet.java +++ b/src/main/java/com/android/vts/servlet/ShowCoverageOverviewServlet.java @@ -17,6 +17,7 @@ package com.android.vts.servlet; import com.android.vts.entity.CodeCoverageEntity; +import com.android.vts.entity.DeviceInfoEntity; import com.android.vts.entity.TestCoverageStatusEntity; import com.android.vts.entity.TestEntity; import com.android.vts.entity.TestRunEntity; @@ -30,7 +31,6 @@ import com.google.cloud.datastore.StructuredQuery.CompositeFilter; import com.google.cloud.datastore.StructuredQuery.Filter; import com.google.cloud.datastore.StructuredQuery.PropertyFilter; import com.google.gson.Gson; -import com.google.gson.JsonObject; import com.google.visualization.datasource.DataSourceHelper; import com.google.visualization.datasource.DataSourceRequest; import com.google.visualization.datasource.base.DataSourceException; @@ -50,15 +50,19 @@ import com.ibm.icu.util.TimeZone; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; -import java.time.Duration; -import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; +import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; +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; @@ -69,79 +73,82 @@ import org.joda.time.format.DateTimeFormatter; import static com.googlecode.objectify.ObjectifyService.ofy; -/** - * Represents the servlet that is invoked on loading the coverage overview page. - */ +/** Represents the servlet that is invoked on loading the coverage overview page. */ public class ShowCoverageOverviewServlet extends BaseServlet { - @Override - public PageType getNavParentType() { - return PageType.COVERAGE_OVERVIEW; - } - - @Override - public List<Page> getBreadcrumbLinks(HttpServletRequest request) { - return null; - } - - @Override - public void doGetHandler(HttpServletRequest request, HttpServletResponse response) - throws IOException { + @Override + public PageType getNavParentType() { + return PageType.COVERAGE_OVERVIEW; + } - String pageType = - request.getParameter("pageType") == null ? "html" : request.getParameter("pageType"); + @Override + public List<Page> getBreadcrumbLinks(HttpServletRequest request) { + return null; + } - RequestDispatcher dispatcher; - if (pageType.equalsIgnoreCase("html")) { - dispatcher = this.getCoverageDispatcher(request, response); - try { - request.setAttribute("pageType", pageType); - response.setStatus(HttpServletResponse.SC_OK); - dispatcher.forward(request, response); - } catch (ServletException e) { - logger.log(Level.SEVERE, "Servlet Exception caught : ", e); - } - } else { - - String testName = request.getParameter("testName"); - - DataTable data = getCoverageDataTable(testName); - DataSourceRequest dsRequest = null; - - try { - // Extract the datasource request parameters. - dsRequest = new DataSourceRequest(request); - - // NOTE: If you want to work in restricted mode, which means that only - // requests from the same domain can access the data source, uncomment the following call. - // - // DataSourceHelper.verifyAccessApproved(dsRequest); - - // Apply the query to the data table. - DataTable newData = DataSourceHelper.applyQuery(dsRequest.getQuery(), data, - dsRequest.getUserLocale()); - - // Set the response. - DataSourceHelper.setServletResponse(newData, dsRequest, response); - } catch (RuntimeException rte) { - logger.log(Level.SEVERE, "A runtime exception has occured", rte); - ResponseStatus status = new ResponseStatus(StatusType.ERROR, ReasonType.INTERNAL_ERROR, - rte.getMessage()); - if (dsRequest == null) { - dsRequest = DataSourceRequest.getDefaultDataSourceRequest(request); - } - DataSourceHelper.setServletErrorResponse(status, dsRequest, response); - } catch (DataSourceException e) { - if (dsRequest != null) { - DataSourceHelper.setServletErrorResponse(e, dsRequest, response); + @Override + public void doGetHandler(HttpServletRequest request, HttpServletResponse response) + throws IOException { + + String pageType = + request.getParameter("pageType") == null + ? "html" + : request.getParameter("pageType"); + + RequestDispatcher dispatcher; + if (pageType.equalsIgnoreCase("html")) { + dispatcher = this.getCoverageDispatcher(request, response); + try { + request.setAttribute("pageType", pageType); + response.setStatus(HttpServletResponse.SC_OK); + dispatcher.forward(request, response); + } catch (ServletException e) { + logger.log(Level.SEVERE, "Servlet Exception caught : ", e); + } } else { - DataSourceHelper.setServletErrorResponse(e, request, response); + + String testName = request.getParameter("testName"); + + DataTable data = getCoverageDataTable(testName); + DataSourceRequest dsRequest = null; + + try { + // Extract the datasource request parameters. + dsRequest = new DataSourceRequest(request); + + // NOTE: If you want to work in restricted mode, which means that only + // requests from the same domain can access the data source, uncomment the following + // call. + // + // DataSourceHelper.verifyAccessApproved(dsRequest); + + // Apply the query to the data table. + DataTable newData = + DataSourceHelper.applyQuery( + dsRequest.getQuery(), data, dsRequest.getUserLocale()); + + // Set the response. + DataSourceHelper.setServletResponse(newData, dsRequest, response); + } catch (RuntimeException rte) { + logger.log(Level.SEVERE, "A runtime exception has occured", rte); + ResponseStatus status = + new ResponseStatus( + StatusType.ERROR, ReasonType.INTERNAL_ERROR, rte.getMessage()); + if (dsRequest == null) { + dsRequest = DataSourceRequest.getDefaultDataSourceRequest(request); + } + DataSourceHelper.setServletErrorResponse(status, dsRequest, response); + } catch (DataSourceException e) { + if (dsRequest != null) { + DataSourceHelper.setServletErrorResponse(e, dsRequest, response); + } else { + DataSourceHelper.setServletErrorResponse(e, request, response); + } + } } - } } - } - private List<Key<TestRunEntity>> getTestCoverageStatusEntityKeyList( + private List<Key<TestRunEntity>> getTestRunEntityKeyList( List<TestCoverageStatusEntity> testCoverageStatusEntityList) { return testCoverageStatusEntityList.stream() .map( @@ -158,43 +165,102 @@ public class ShowCoverageOverviewServlet extends BaseServlet { .collect(Collectors.toList()); } - private RequestDispatcher getCoverageDispatcher( - HttpServletRequest request, HttpServletResponse response) { - - String COVERAGE_OVERVIEW_JSP = "WEB-INF/jsp/show_coverage_overview.jsp"; - - RequestDispatcher dispatcher = null; - boolean unfiltered = request.getParameter("unfiltered") != null; - boolean showPresubmit = request.getParameter("showPresubmit") != null; - boolean showPostsubmit = request.getParameter("showPostsubmit") != null; - - // If no params are specified, set to default of postsubmit-only. - if (!(showPresubmit || showPostsubmit)) { - showPostsubmit = true; + private Predicate<DeviceInfoEntity> isBranchAndDevice(String branch, String device) { + return d -> d.getBranch().equals(branch) && d.getBuildFlavor().equals(device); } - // If unfiltered, set showPre- and Post-submit to true for accurate UI. - if (unfiltered) { - showPostsubmit = true; - showPresubmit = true; + private Predicate<DeviceInfoEntity> isBranch(String branch) { + return d -> d.getBranch().equals(branch); } - // Add test names to list - List<String> resultNames = new ArrayList<>(); - for (VtsReportMessage.TestCaseResult r : VtsReportMessage.TestCaseResult.values()) { - resultNames.add(r.name()); + private Predicate<DeviceInfoEntity> isDevice(String device) { + return d -> d.getBuildFlavor().equals(device); } - Map<String, String[]> parameterMap = request.getParameterMap(); + private RequestDispatcher getCoverageDispatcher( + HttpServletRequest request, HttpServletResponse response) { - List<TestCoverageStatusEntity> testCoverageStatusEntityList = - TestCoverageStatusEntity.getAllTestCoverage(); + String COVERAGE_OVERVIEW_JSP = "WEB-INF/jsp/show_coverage_overview.jsp"; - List<com.googlecode.objectify.Key<TestRunEntity>> testCoverageStatusEntityKeyList = - this.getTestCoverageStatusEntityKeyList(testCoverageStatusEntityList); + RequestDispatcher dispatcher = null; + boolean unfiltered = request.getParameter("unfiltered") != null; + boolean showPresubmit = request.getParameter("showPresubmit") != null; + boolean showPostsubmit = request.getParameter("showPostsubmit") != 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 test names to list + List<String> resultNames = + Arrays.stream(VtsReportMessage.TestCaseResult.values()) + .map(testCaseResult -> testCaseResult.name()) + .collect(Collectors.toList()); + + Map<String, String[]> parameterMap = request.getParameterMap(); + + List<TestCoverageStatusEntity> testCoverageStatusEntityList = + TestCoverageStatusEntity.getAllTestCoverage(); + + List<com.googlecode.objectify.Key<TestRunEntity>> testRunEntityKeyList = new ArrayList<>(); + + if (Objects.nonNull(parameterMap.get("branch")) + || Objects.nonNull(parameterMap.get("device"))) { + List<com.googlecode.objectify.Key<DeviceInfoEntity>> deviceInfoEntityKeyList = + TestCoverageStatusEntity.getDeviceInfoEntityKeyList( + testCoverageStatusEntityList); + + Collection<DeviceInfoEntity> deviceInfoEntityMap = + ofy().load().keys(() -> deviceInfoEntityKeyList.iterator()).values(); + + Stream<DeviceInfoEntity> deviceInfoEntityStream = Stream.empty(); + if (Objects.nonNull(parameterMap.get("branch")) + && Objects.nonNull(parameterMap.get("device"))) { + String branch = parameterMap.get("branch")[0]; + String device = parameterMap.get("device")[0]; + deviceInfoEntityStream = + deviceInfoEntityMap.stream().filter(isBranchAndDevice(branch, device)); + } else if (Objects.nonNull(parameterMap.get("branch"))) { + String branch = parameterMap.get("branch")[0]; + deviceInfoEntityStream = deviceInfoEntityMap.stream().filter(isBranch(branch)); + } else if (Objects.nonNull(parameterMap.get("device"))) { + String device = parameterMap.get("device")[0]; + deviceInfoEntityStream = deviceInfoEntityMap.stream().filter(isDevice(device)); + } else { + logger.log(Level.WARNING, "unmet search condition!"); + } + testRunEntityKeyList = + deviceInfoEntityStream + .map( + deviceInfoEntity -> { + com.googlecode.objectify.Key testKey = + com.googlecode.objectify.Key.create( + TestEntity.class, + deviceInfoEntity + .getParent() + .getParent() + .getName()); + return com.googlecode.objectify.Key.create( + testKey, + TestRunEntity.class, + deviceInfoEntity.getParent().getId()); + }) + .collect(Collectors.toList()); + logger.log(Level.INFO, "testRunEntityKeyList size => " + testRunEntityKeyList.size()); + } else { + testRunEntityKeyList = this.getTestRunEntityKeyList(testCoverageStatusEntityList); + } + Iterator<Key<TestRunEntity>> testRunEntityKeyIterator = testRunEntityKeyList.iterator(); Map<Key<TestRunEntity>, TestRunEntity> keyTestRunEntityMap = - ofy().load().keys(() -> testCoverageStatusEntityKeyList.iterator()); + ofy().load().keys(() -> testRunEntityKeyIterator); List<com.googlecode.objectify.Key<CodeCoverageEntity>> codeCoverageEntityKeyList = new ArrayList<>(); @@ -212,100 +278,120 @@ public class ShowCoverageOverviewServlet extends BaseServlet { keyCodeCoverageEntityMap = ofy().load().keys(() -> codeCoverageEntityKeyList.iterator()); - Map<Long, CodeCoverageEntity> codeCoverageEntityMap = new HashMap<>(); + Map<Long, CodeCoverageEntity> codeCoverageEntityMap = new HashMap<>(); for (Map.Entry<com.googlecode.objectify.Key<CodeCoverageEntity>, CodeCoverageEntity> entry : keyCodeCoverageEntityMap.entrySet()) { codeCoverageEntityMap.put(entry.getValue().getId(), entry.getValue()); } - int coveredLines = 0; - int uncoveredLines = 0; - int passCount = 0; - int failCount = 0; - for (Map.Entry<Long, CodeCoverageEntity> entry : codeCoverageEntityMap.entrySet()) { - TestRunEntity testRunEntity = testRunEntityMap.get(entry.getKey()); + int coveredLines = 0; + int uncoveredLines = 0; + int passCount = 0; + int failCount = 0; + for (Map.Entry<Long, CodeCoverageEntity> entry : codeCoverageEntityMap.entrySet()) { + TestRunEntity testRunEntity = testRunEntityMap.get(entry.getKey()); + + CodeCoverageEntity codeCoverageEntity = entry.getValue(); + + coveredLines += codeCoverageEntity.getCoveredLineCount(); + uncoveredLines += + codeCoverageEntity.getTotalLineCount() + - codeCoverageEntity.getCoveredLineCount(); + passCount += testRunEntity.getPassCount(); + failCount += testRunEntity.getFailCount(); + } + + FilterUtil.setAttributes(request, parameterMap); - CodeCoverageEntity codeCoverageEntity = entry.getValue(); + int[] testStats = new int[VtsReportMessage.TestCaseResult.values().length]; + testStats[VtsReportMessage.TestCaseResult.TEST_CASE_RESULT_PASS.getNumber()] = passCount; + testStats[VtsReportMessage.TestCaseResult.TEST_CASE_RESULT_FAIL.getNumber()] = failCount; - coveredLines += codeCoverageEntity.getCoveredLineCount(); - uncoveredLines += codeCoverageEntity.getTotalLineCount() - codeCoverageEntity.getCoveredLineCount(); - passCount += testRunEntity.getPassCount(); - failCount += testRunEntity.getFailCount(); + response.setStatus(HttpServletResponse.SC_OK); + request.setAttribute("resultNames", resultNames); + request.setAttribute("resultNamesJson", new Gson().toJson(resultNames)); + request.setAttribute("testRunEntityList", testRunEntityMap.values()); + request.setAttribute("codeCoverageEntityMap", codeCoverageEntityMap); + request.setAttribute("coveredLines", new Gson().toJson(coveredLines)); + request.setAttribute("uncoveredLines", new Gson().toJson(uncoveredLines)); + request.setAttribute("testStats", new Gson().toJson(testStats)); + + request.setAttribute("unfiltered", unfiltered); + request.setAttribute("showPresubmit", showPresubmit); + request.setAttribute("showPostsubmit", showPostsubmit); + + request.setAttribute( + "deviceOptions", + TestCoverageStatusEntity.getDeviceSet(testCoverageStatusEntityList)); + request.setAttribute( + "branchOptions", + TestCoverageStatusEntity.getBranchSet(testCoverageStatusEntityList)); + dispatcher = request.getRequestDispatcher(COVERAGE_OVERVIEW_JSP); + return dispatcher; } - FilterUtil.setAttributes(request, parameterMap); - - int[] testStats = new int[VtsReportMessage.TestCaseResult.values().length]; - testStats[VtsReportMessage.TestCaseResult.TEST_CASE_RESULT_PASS.getNumber()] = passCount; - testStats[VtsReportMessage.TestCaseResult.TEST_CASE_RESULT_FAIL.getNumber()] = failCount; - - response.setStatus(HttpServletResponse.SC_OK); - request.setAttribute("resultNames", resultNames); - request.setAttribute("resultNamesJson", new Gson().toJson(resultNames)); - request.setAttribute("testRunEntityList", testRunEntityMap.values()); - request.setAttribute("codeCoverageEntityMap", codeCoverageEntityMap); - request.setAttribute("coveredLines", new Gson().toJson(coveredLines)); - request.setAttribute("uncoveredLines", new Gson().toJson(uncoveredLines)); - request.setAttribute("testStats", new Gson().toJson(testStats)); - - request.setAttribute("unfiltered", unfiltered); - request.setAttribute("showPresubmit", showPresubmit); - request.setAttribute("showPostsubmit", showPostsubmit); - request.setAttribute("branches", new Gson().toJson(new ArrayList<>())); // DeviceInfoEntity.getAllBranches() - request.setAttribute("devices", new Gson().toJson(new ArrayList<>())); // DeviceInfoEntity.getAllBuildFlavors() - dispatcher = request.getRequestDispatcher(COVERAGE_OVERVIEW_JSP); - return dispatcher; - } - - private DataTable getCoverageDataTable(String testName) { - - Datastore datastore = DatastoreOptions.getDefaultInstance().getService(); - - DataTable dataTable = new DataTable(); - ArrayList<ColumnDescription> cd = new ArrayList<>(); - ColumnDescription startDate = new ColumnDescription("startDate", ValueType.DATETIME, "Date"); - startDate.setPattern("yyyy-MM-dd"); - cd.add(startDate); - cd.add(new ColumnDescription("coveredLineCount", ValueType.NUMBER, - "Covered Source Code Line Count")); - cd.add( - new ColumnDescription("totalLineCount", ValueType.NUMBER, "Total Source Code Line Count")); - cd.add(new ColumnDescription("percentage", ValueType.NUMBER, "Coverage Ratio (%)")); - - dataTable.addColumns(cd); - - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.MONTH, -6); - Long startTime = cal.getTime().getTime() * 1000; - Long endTime = Calendar.getInstance().getTime().getTime() * 1000; - - com.google.cloud.datastore.Key startKey = datastore.newKeyFactory() - .setKind(TestRunEntity.KIND) - .addAncestors(PathElement.of(TestEntity.KIND, testName), - PathElement.of(TestRunEntity.KIND, startTime)) - .newKey(startTime); - - com.google.cloud.datastore.Key endKey = datastore.newKeyFactory() - .setKind(TestRunEntity.KIND) - .addAncestors(PathElement.of(TestEntity.KIND, testName), - PathElement.of(TestRunEntity.KIND, endTime)) - .newKey(endTime); - - Filter codeCoverageFilter = CompositeFilter.and( - PropertyFilter.lt("__key__", endKey), - PropertyFilter.gt("__key__", startKey) - ); - - List<CodeCoverageEntity> codeCoverageEntityList = ofy().load() - .type(CodeCoverageEntity.class) - .filter(codeCoverageFilter) - .limit(10) - .list(); - - DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd"); - Map<String, List<CodeCoverageEntity>> codeCoverageEntityListMap = codeCoverageEntityList.stream().collect( - Collectors.groupingBy(v -> dateTimeFormatter.print(v.getId() / 1000)) - ); + private DataTable getCoverageDataTable(String testName) { + + Datastore datastore = DatastoreOptions.getDefaultInstance().getService(); + + DataTable dataTable = new DataTable(); + ArrayList<ColumnDescription> cd = new ArrayList<>(); + ColumnDescription startDate = + new ColumnDescription("startDate", ValueType.DATETIME, "Date"); + startDate.setPattern("yyyy-MM-dd"); + cd.add(startDate); + cd.add( + new ColumnDescription( + "coveredLineCount", ValueType.NUMBER, "Covered Source Code Line Count")); + cd.add( + new ColumnDescription( + "totalLineCount", ValueType.NUMBER, "Total Source Code Line Count")); + cd.add(new ColumnDescription("percentage", ValueType.NUMBER, "Coverage Ratio (%)")); + + dataTable.addColumns(cd); + + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.MONTH, -6); + Long startTime = cal.getTime().getTime() * 1000; + Long endTime = Calendar.getInstance().getTime().getTime() * 1000; + + com.google.cloud.datastore.Key startKey = + datastore + .newKeyFactory() + .setKind(TestRunEntity.KIND) + .addAncestors( + PathElement.of(TestEntity.KIND, testName), + PathElement.of(TestRunEntity.KIND, startTime)) + .newKey(startTime); + + com.google.cloud.datastore.Key endKey = + datastore + .newKeyFactory() + .setKind(TestRunEntity.KIND) + .addAncestors( + PathElement.of(TestEntity.KIND, testName), + PathElement.of(TestRunEntity.KIND, endTime)) + .newKey(endTime); + + Filter codeCoverageFilter = + CompositeFilter.and( + PropertyFilter.lt("__key__", endKey), + PropertyFilter.gt("__key__", startKey)); + + List<CodeCoverageEntity> codeCoverageEntityList = + ofy().load() + .type(CodeCoverageEntity.class) + .filter(codeCoverageFilter) + .limit(10) + .list(); + + DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd"); + Map<String, List<CodeCoverageEntity>> codeCoverageEntityListMap = + codeCoverageEntityList + .stream() + .collect( + Collectors.groupingBy( + v -> dateTimeFormatter.print(v.getId() / 1000))); codeCoverageEntityListMap.forEach( (key, entityList) -> { @@ -341,6 +427,6 @@ public class ShowCoverageOverviewServlet extends BaseServlet { } }); - return dataTable; - } + return dataTable; + } } diff --git a/src/main/webapp/WEB-INF/jsp/header.jsp b/src/main/webapp/WEB-INF/jsp/header.jsp index 77ec600..1d7de87 100644 --- a/src/main/webapp/WEB-INF/jsp/header.jsp +++ b/src/main/webapp/WEB-INF/jsp/header.jsp @@ -23,8 +23,8 @@ <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700'> <link rel='stylesheet' href='/css/navbar.css'> <link rel='stylesheet' href='/css/common.css'> - <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js'></script> - <script src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js'></script> + <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script> + <script src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js'></script> <script src='https://www.gstatic.com/external_hosted/materialize/materialize.min.js'></script> <script type='text/javascript'> if (${analyticsID}) { diff --git a/src/main/webapp/WEB-INF/jsp/show_coverage_overview.jsp b/src/main/webapp/WEB-INF/jsp/show_coverage_overview.jsp index 6641b7e..0516c9d 100644 --- a/src/main/webapp/WEB-INF/jsp/show_coverage_overview.jsp +++ b/src/main/webapp/WEB-INF/jsp/show_coverage_overview.jsp @@ -26,57 +26,55 @@ <link rel='stylesheet' href='/css/search_header.css'> <script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script> <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/common.js'></script> - <script src='js/search_header.js'></script> + <script src='js/time.js'></script> <script type='text/javascript'> google.charts.load('current', {'packages':['table', 'corechart']}); google.charts.setOnLoadCallback(drawStatsChart); google.charts.setOnLoadCallback(drawCoverageCharts); - var search; - $(document).ready(function() { - // $('#test-results-container').showTests(${testRuns}, true); - search = $('#filter-bar').createSearchHeader('Code Coverage', '', refresh); - search.addFilter('Branch', 'branch', { - corpus: ${branches} - }, ${branch}); - search.addFilter('Device', 'device', { - corpus: ${devices} - }, ${device}); - search.addFilter('Device Build ID', 'deviceBuildId', {}, ${deviceBuildId}); - search.addFilter('Test Build ID', 'testBuildId', {}, ${testBuildId}); - search.addFilter('Host', 'hostname', {}, ${hostname}); - search.addFilter('Passing Count', 'passing', { - type: 'number', - width: 's2' - }, ${passing}); - search.addFilter('Non-Passing Count', 'nonpassing', { - type: 'number', - width: 's2' - }, ${nonpassing}); - search.addRunTypeCheckboxes(${showPresubmit}, ${showPostsubmit}); - search.display(); + + $('select').material_select(); + + $(".search-icon-wrapper").click(function() { + $(".search-wrapper").toggle(); + }); + + var inputIdList = ["device", "branch"]; + + $("#schBtn").click(function (evt) { + if($(this).hasClass('disabled')) return; + var queryParam = "?"; + $.each(inputIdList, function( index, value ) { + var selectId = value.charAt(0).toUpperCase() + value.slice(1) + var result = $("#search" + selectId).val(); + if ( !$.isEmptyObject(result) ) { + queryParam += value + "=" + result.trim() + "&"; + } + }); + var link = '${pageContext.request.contextPath}' + '/show_coverage_overview' + queryParam; + window.open(link, '_self'); + }); var no_data_msg = "NO DATA"; $('#coverageModalGraph').modal({ - width: '75%', - dismissible: true, // Modal can be dismissed by clicking outside of the modal - opacity: .5, // Opacity of modal background - inDuration: 300, // Transition in duration - outDuration: 200, // Transition out duration - startingTop: '4%', // Starting top style attribute - endingTop: '10%', // Ending top style attribute - ready: function(modal, trigger) { // Callback for Modal open. Modal and trigger parameters available. + width: '75%', + dismissible: true, // Modal can be dismissed by clicking outside of the modal + opacity: .5, // Opacity of modal background + inDuration: 300, // Transition in duration + outDuration: 200, // Transition out duration + startingTop: '4%', // Starting top style attribute + endingTop: '10%', // Ending top style attribute + ready: function(modal, trigger) { // Callback for Modal open. Modal and trigger parameters available. var testname = modal.data('testname'); $('#coverageModalTitle').text("Code Coverage Chart : " + testname); var query = new google.visualization.Query('show_coverage_overview?pageType=datatable&testName=' + testname); // Send the query with a callback function. query.send(handleQueryResponse); - }, - complete: function() { + }, + complete: function() { $('#coverage_combo_chart_div').empty(); $('#coverage_line_chart_div').empty(); $('#coverage_table_chart_div').empty(); @@ -90,9 +88,8 @@ }); $('#dataTableLoading').show("slow"); - } // Callback for Modal close - } - ); + } // Callback for Modal close + }); // Handle the query response. function handleQueryResponse(response) { @@ -339,7 +336,50 @@ <body> <div class='wide container'> - <div id='filter-bar'></div> + <div id="filter-bar"> + <div class="row card search-bar expanded"> + <div class="header-wrapper"> + <h5 class="section-header"> + <b>Code Coverage</b> + </h5> + <div class="search-icon-wrapper"> + <i class="material-icons">search</i> + </div> + </div> + <div class="search-wrapper" ${empty branch and empty device ? 'style="display: none"' : ''}> + <div class="row"> + <div class="col s9"> + <div class="input-field col s4"> + <c:set var="branchVal" value='${fn:replace(branch, "\\\"", "")}'></c:set> + <select id="searchBranch"> + <option value="" <c:if test="${empty branch}">disabled selected</c:if> >Choose your branch</option> + <c:forEach items='${branchOptions}' var='branchOption'> + <option value="${branchOption}" ${branchVal == branchOption ? 'selected' : ''}>${branchOption}</option> + </c:forEach> + </select> + <label>Branch Select</label> + </div> + <div class="input-field col s4"> + <c:set var="deviceVal" value='${fn:replace(device, "\\\"", "")}'></c:set> + <select id="searchDevice"> + <option value="" <c:if test="${empty device}">disabled selected</c:if> >Choose your device</option> + <c:forEach items='${deviceOptions}' var='deviceOption'> + <option value="${deviceOption}" ${deviceVal == deviceOption ? 'selected' : ''}>${deviceOption}</option> + </c:forEach> + </select> + <label>Device Select</label> + </div> + <div class="col s4"></div> + </div> + <div class="refresh-wrapper col s3"> + <a id="schBtn" class="btn-floating btn-medium red waves-effect waves-light" style="margin-right: 30px;"> + <i class="medium material-icons">cached</i> + </a> + </div> + </div> + </div> + </div> + </div> <div class='row'> <div class='col s12'> <div class='col s12 card center-align'> @@ -460,17 +500,6 @@ </div> </div> - <!-- Link Modal Structure --> - <div id="info-modal" class="modal modal-fixed-footer"> - <div class="modal-content"> - <h4>Links</h4> - <div class="info-container"></div> - </div> - <div class="modal-footer"> - <a class="btn-flat modal-close">Close</a> - </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 be4fb26..3942fe4 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -116,6 +116,11 @@ Copyright 2016 Google Inc. All Rights Reserved. </servlet> <servlet> + <servlet-name>data_api</servlet-name> + <servlet-class>com.android.vts.api.DataRestServlet</servlet-class> +</servlet> + +<servlet> <servlet-name>coverage_api</servlet-name> <servlet-class>com.android.vts.api.CoverageRestServlet</servlet-class> </servlet> @@ -266,6 +271,11 @@ Copyright 2016 Google Inc. All Rights Reserved. </servlet-mapping> <servlet-mapping> + <servlet-name>data_api</servlet-name> + <url-pattern>/api/data/*</url-pattern> +</servlet-mapping> + +<servlet-mapping> <servlet-name>coverage_api</servlet-name> <url-pattern>/api/coverage/*</url-pattern> </servlet-mapping> diff --git a/src/test/java/com/android/vts/api/DataRestServletTest.java b/src/test/java/com/android/vts/api/DataRestServletTest.java new file mode 100644 index 0000000..7d21707 --- /dev/null +++ b/src/test/java/com/android/vts/api/DataRestServletTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * 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.api; + +import com.android.vts.entity.DeviceInfoEntity; +import com.android.vts.entity.TestEntity; +import com.android.vts.entity.TestRunEntity; +import com.android.vts.util.ObjectifyTestBase; +import com.google.gson.Gson; +import com.googlecode.objectify.Key; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.LinkedList; + +import static com.googlecode.objectify.ObjectifyService.factory; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +public class DataRestServletTest extends ObjectifyTestBase { + + private Gson gson; + + @Mock private HttpServletRequest request; + + @Mock private HttpServletResponse response; + + /** It be executed before each @Test method */ + @BeforeEach + void setUpExtra() { + gson = new Gson(); + + factory().register(TestEntity.class); + factory().register(TestRunEntity.class); + factory().register(DeviceInfoEntity.class); + + Key testParentKey = Key.create(TestEntity.class, "test1"); + Key testRunParentKey = Key.create(testParentKey, TestRunEntity.class, 1); + DeviceInfoEntity deviceInfoEntity1 = + new DeviceInfoEntity( + testRunParentKey, + "pi", + "sailfish", + "sailfish-userdebug", + "4585723", + "64", + "arm64-v8a"); + deviceInfoEntity1.setId(2384723984L); + deviceInfoEntity1.save(); + + DeviceInfoEntity deviceInfoEntity2 = + new DeviceInfoEntity( + testRunParentKey, + "master", + "walleye", + "aosp_arm64_ab-userdebug", + "4585723", + "64", + "arm64-v8a"); + deviceInfoEntity2.setId(2384723422L); + deviceInfoEntity2.save(); + } + + @Test + public void testBranchData() throws IOException, ServletException { + + when(request.getPathInfo()).thenReturn("/branch"); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + when(response.getWriter()).thenReturn(pw); + + DataRestServlet dataRestServlet = new DataRestServlet(); + dataRestServlet.doGet(request, response); + String result = sw.getBuffer().toString().trim(); + + LinkedList resultList = gson.fromJson(result, LinkedList.class); + + assertEquals(resultList.size(), 2); + assertEquals(resultList.get(0), "master"); + assertEquals(resultList.get(1), "pi"); + } + + @Test + public void testDeviceData() throws IOException, ServletException { + + when(request.getPathInfo()).thenReturn("/device"); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + when(response.getWriter()).thenReturn(pw); + + DataRestServlet dataRestServlet = new DataRestServlet(); + dataRestServlet.doGet(request, response); + String result = sw.getBuffer().toString().trim(); + + LinkedList resultList = gson.fromJson(result, LinkedList.class); + + String value = "1"; + int charValue = value.charAt(0); + String next = String.valueOf((char) (charValue + 1)); + System.out.println(next); + + String s = "asb"; + System.out.println(s.replace("[.]$", "")); + assertTrue(s.charAt(0) == s.charAt(s.length() - 1)); + assertEquals(resultList.size(), 2); + assertEquals(resultList.get(0), "aosp_arm64_ab-userdebug"); + assertEquals(resultList.get(1), "sailfish-userdebug"); + } +} |