summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYoung Gyu Park <younggyu@google.com>2018-10-18 23:34:52 -0700
committerandroid-build-merger <android-build-merger@google.com>2018-10-18 23:34:52 -0700
commitefe3bb05ca058018bd966b3fa27cb8f28e15fe41 (patch)
tree4119807f30a8908acf64a661bc80574599a3d8ee
parent282a94b439430e8db2bcf52450d42ccc6a89943e (diff)
parent1ca21fadcdba5fd35e135dd07d581171102e007d (diff)
downloaddashboard-efe3bb05ca058018bd966b3fa27cb8f28e15fe41.tar.gz
Search by branch and device on show_coverage_overview
am: 1ca21fadcd Change-Id: Id4aa6d419aa739ab8dde31ea723a88b74debee27
-rw-r--r--src/main/java/com/android/vts/api/DataRestServlet.java81
-rw-r--r--src/main/java/com/android/vts/config/ObjectifyListener.java7
-rw-r--r--src/main/java/com/android/vts/entity/BranchEntity.java47
-rw-r--r--src/main/java/com/android/vts/entity/BuildTargetEntity.java48
-rw-r--r--src/main/java/com/android/vts/entity/DeviceInfoEntity.java88
-rw-r--r--src/main/java/com/android/vts/entity/TestCoverageStatusEntity.java80
-rw-r--r--src/main/java/com/android/vts/job/VtsCoverageAlertJobServlet.java13
-rw-r--r--src/main/java/com/android/vts/servlet/ShowCoverageOverviewServlet.java450
-rw-r--r--src/main/webapp/WEB-INF/jsp/header.jsp4
-rw-r--r--src/main/webapp/WEB-INF/jsp/show_coverage_overview.jsp129
-rw-r--r--src/main/webapp/WEB-INF/web.xml10
-rw-r--r--src/test/java/com/android/vts/api/DataRestServletTest.java135
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");
+ }
+}