diff options
37 files changed, 1665 insertions, 987 deletions
diff --git a/src/main/java/com/android/vts/api/BaseApiServlet.java b/src/main/java/com/android/vts/api/BaseApiServlet.java index 1ad0237..b7384a7 100644 --- a/src/main/java/com/android/vts/api/BaseApiServlet.java +++ b/src/main/java/com/android/vts/api/BaseApiServlet.java @@ -17,61 +17,51 @@ package com.android.vts.api; import com.google.apphosting.api.ApiProxy; -import com.google.gson.Gson; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; import java.util.Properties; import java.util.logging.Logger; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -/** - * An abstract class to be subclassed to create API Servlet - */ +/** An abstract class to be subclassed to create API Servlet */ public class BaseApiServlet extends HttpServlet { - private static final Logger logger = - Logger.getLogger(BaseApiServlet.class.getName()); + private static final Logger logger = Logger.getLogger(BaseApiServlet.class.getName()); + + /** System Configuration Property class */ + protected Properties systemConfigProp = new Properties(); - /** - * System Configuration Property class - */ - protected Properties systemConfigProp = new Properties(); + /** Appengine server host name */ + protected String hostName; - /** - * Appengine server host name - */ - protected String hostName; + /** + * This variable is for maximum number of entities per transaction You can find the detail here + * (https://cloud.google.com/datastore/docs/concepts/limits) + */ + protected int MAX_ENTITY_SIZE_PER_TRANSACTION = 300; - @Override - public void init(ServletConfig cfg) throws ServletException { - super.init(cfg); + @Override + public void init(ServletConfig cfg) throws ServletException { + super.init(cfg); - ApiProxy.Environment env = ApiProxy.getCurrentEnvironment(); - hostName = env.getAttributes().get("com.google.appengine.runtime.default_version_hostname") - .toString(); - try { - InputStream defaultInputStream = - BaseApiServlet.class - .getClassLoader() - .getResourceAsStream("config.properties"); - systemConfigProp.load(defaultInputStream); + systemConfigProp = + Properties.class.cast(cfg.getServletContext().getAttribute("systemConfigProp")); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); + this.MAX_ENTITY_SIZE_PER_TRANSACTION = + Integer.parseInt(systemConfigProp.getProperty("datastore.maxEntitySize")); + + ApiProxy.Environment env = ApiProxy.getCurrentEnvironment(); + hostName = + env.getAttributes() + .get("com.google.appengine.runtime.default_version_hostname") + .toString(); } - } - protected void setAccessControlHeaders(HttpServletResponse resp) { - resp.setHeader("Access-Control-Allow-Origin", hostName); - resp.setHeader("Access-Control-Allow-Methods", "GET, PUT, POST, OPTIONS, DELETE"); - resp.addHeader("Access-Control-Allow-Headers", "Content-Type"); - resp.addHeader("Access-Control-Max-Age", "86400"); - } + protected void setAccessControlHeaders(HttpServletResponse resp) { + resp.setHeader("Access-Control-Allow-Origin", hostName); + resp.setHeader("Access-Control-Allow-Methods", "GET, PUT, POST, OPTIONS, DELETE"); + resp.addHeader("Access-Control-Allow-Headers", "Content-Type"); + resp.addHeader("Access-Control-Max-Age", "86400"); + } } diff --git a/src/main/java/com/android/vts/api/BigtableLegacyJsonServlet.java b/src/main/java/com/android/vts/api/BigtableLegacyJsonServlet.java index ac69b84..d331d4b 100644 --- a/src/main/java/com/android/vts/api/BigtableLegacyJsonServlet.java +++ b/src/main/java/com/android/vts/api/BigtableLegacyJsonServlet.java @@ -150,7 +150,6 @@ public class BigtableLegacyJsonServlet extends HttpServlet { try { byte[] value = Base64.decodeBase64(payloadJson.getString("value")); TestReportMessage testReportMessage = TestReportMessage.parseFrom(value); - DatastoreHelper.insertTestReport(testReportMessage); } catch (InvalidProtocolBufferException e) { logger.log(Level.WARNING, "Invalid report posted to dashboard."); } diff --git a/src/main/java/com/android/vts/api/CoverageRestServlet.java b/src/main/java/com/android/vts/api/CoverageRestServlet.java index 217b740..165db8d 100644 --- a/src/main/java/com/android/vts/api/CoverageRestServlet.java +++ b/src/main/java/com/android/vts/api/CoverageRestServlet.java @@ -20,8 +20,10 @@ import com.android.vts.entity.ApiCoverageEntity; import com.android.vts.entity.CoverageEntity; import com.android.vts.entity.TestCoverageStatusEntity; import com.android.vts.entity.TestPlanRunEntity; +import com.android.vts.entity.TestRunEntity; import com.google.gson.Gson; import com.googlecode.objectify.Key; + import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -104,9 +106,11 @@ public class CoverageRestServlet extends BaseApiServlet { List<List<String>> allCoveredHalApiList = new ArrayList(); Key<TestPlanRunEntity> key = Key.create(urlSafeKey); - TestPlanRunEntity testPlanRunEntity = ofy().load().key(key).now(); + System.out.println("urlSafekey => " + urlSafeKey); + TestPlanRunEntity testPlanRunEntity = ofy().load().key(key).safe(); - for (Key testRunKey : testPlanRunEntity.getTestRuns()) { + System.out.println("testPlanRunEntity => " + testPlanRunEntity); + for (Key<TestRunEntity> testRunKey : testPlanRunEntity.getTestRuns()) { List<ApiCoverageEntity> apiCoverageEntityList = ofy().load().type(ApiCoverageEntity.class).ancestor(testRunKey).list(); for (ApiCoverageEntity apiCoverageEntity : apiCoverageEntityList) { diff --git a/src/main/java/com/android/vts/api/DatastoreRestServlet.java b/src/main/java/com/android/vts/api/DatastoreRestServlet.java index 42d7a60..5ddf18d 100644 --- a/src/main/java/com/android/vts/api/DatastoreRestServlet.java +++ b/src/main/java/com/android/vts/api/DatastoreRestServlet.java @@ -16,58 +16,60 @@ package com.android.vts.api; +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.DashboardEntity; +import com.android.vts.entity.DeviceInfoEntity; +import com.android.vts.entity.HalApiEntity; +import com.android.vts.entity.ProfilingPointRunEntity; +import com.android.vts.entity.TestCaseRunEntity; +import com.android.vts.entity.TestEntity; +import com.android.vts.entity.TestPlanEntity; +import com.android.vts.entity.TestPlanRunEntity; +import com.android.vts.entity.TestRunEntity; +import com.android.vts.proto.VtsReportMessage; import com.android.vts.proto.VtsReportMessage.DashboardPostMessage; import com.android.vts.proto.VtsReportMessage.TestPlanReportMessage; import com.android.vts.proto.VtsReportMessage.TestReportMessage; -import com.android.vts.servlet.BaseServlet; -import com.android.vts.util.DatastoreHelper; -import com.android.vts.util.EmailHelper; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson.JacksonFactory; import com.google.api.services.oauth2.Oauth2; import com.google.api.services.oauth2.model.Tokeninfo; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.servlet.ServletConfig; import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + +import com.google.appengine.api.datastore.Key; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; +import static com.googlecode.objectify.ObjectifyService.ofy; + +@Slf4j /** REST endpoint for posting data to the Dashboard. */ -public class DatastoreRestServlet extends HttpServlet { +public class DatastoreRestServlet extends BaseApiServlet { private static String SERVICE_CLIENT_ID; private static final String SERVICE_NAME = "VTS Dashboard"; - private static final Logger logger = Logger.getLogger(DatastoreRestServlet.class.getName()); - - /** System Configuration Property class */ - protected Properties systemConfigProp = new Properties(); @Override public void init(ServletConfig cfg) throws ServletException { super.init(cfg); - try { - InputStream defaultInputStream = - DatastoreRestServlet.class - .getClassLoader() - .getResourceAsStream("config.properties"); - systemConfigProp.load(defaultInputStream); - - SERVICE_CLIENT_ID = systemConfigProp.getProperty("appengine.serviceClientID"); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } + SERVICE_CLIENT_ID = this.systemConfigProp.getProperty("appengine.serviceClientID"); } @Override @@ -81,14 +83,15 @@ public class DatastoreRestServlet extends HttpServlet { postMessage = DashboardPostMessage.parseFrom(value); } catch (IOException e) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - logger.log(Level.WARNING, "Invalid proto: " + e.getLocalizedMessage()); + log.error("Invalid proto: " + e.getLocalizedMessage()); return; } // Verify service account access token. if (postMessage.hasAccessToken()) { String accessToken = postMessage.getAccessToken(); - logger.log(Level.INFO, "accessToken => " + accessToken); + log.debug("accessToken => " + accessToken); + GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken); Oauth2 oauth2 = new Oauth2.Builder(new NetHttpTransport(), new JacksonFactory(), credential) @@ -97,23 +100,461 @@ public class DatastoreRestServlet extends HttpServlet { Tokeninfo tokenInfo = oauth2.tokeninfo().setAccessToken(accessToken).execute(); if (tokenInfo.getIssuedTo().equals(SERVICE_CLIENT_ID)) { for (TestReportMessage testReportMessage : postMessage.getTestReportList()) { - DatastoreHelper.insertTestReport(testReportMessage); + this.insertTestReport(testReportMessage); } for (TestPlanReportMessage planReportMessage : postMessage.getTestPlanReportList()) { - DatastoreHelper.insertTestPlanReport(planReportMessage); + this.insertTestPlanReport(planReportMessage); } response.setStatus(HttpServletResponse.SC_OK); } else { - logger.log(Level.WARNING, "service_client_id didn't match!"); - logger.log(Level.INFO, "SERVICE_CLIENT_ID => " + tokenInfo.getIssuedTo()); + log.warn("service_client_id didn't match!"); + log.debug("SERVICE_CLIENT_ID => " + tokenInfo.getIssuedTo()); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } } else { - logger.log(Level.WARNING, "postMessage do not contain any accessToken!"); + log.error("postMessage do not contain any accessToken!"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } } + + /** + * Upload data from a test report message + * + * @param report The test report containing data to upload. + */ + private void insertTestReport(TestReportMessage report) { + + if (!report.hasStartTimestamp() + || !report.hasEndTimestamp() + || !report.hasTest() + || !report.hasHostInfo() + || !report.hasBuildInfo()) { + // missing information + log.error("Missing information in report !"); + return; + } + + List<TestEntity> testEntityList = new ArrayList<>(); + List<TestRunEntity> testRunEntityList = new ArrayList<>(); + List<BranchEntity> branchEntityList = new ArrayList<>(); + List<BuildTargetEntity> buildTargetEntityList = new ArrayList<>(); + List<CoverageEntity> coverageEntityList = new ArrayList<>(); + List<CodeCoverageEntity> codeCoverageEntityList = new ArrayList<>(); + List<DeviceInfoEntity> deviceInfoEntityList = new ArrayList<>(); + List<ProfilingPointRunEntity> profilingPointRunEntityList = new ArrayList<>(); + List<TestCaseRunEntity> testCaseRunEntityList = new ArrayList<>(); + List<ApiCoverageEntity> apiCoverageEntityList = new ArrayList<>(); + + List<?> allEntityList = + Arrays.asList( + testEntityList, + branchEntityList, + buildTargetEntityList, + coverageEntityList, + codeCoverageEntityList, + deviceInfoEntityList, + profilingPointRunEntityList, + testCaseRunEntityList, + apiCoverageEntityList, + testRunEntityList); + + long passCount = 0; + long failCount = 0; + long coveredLineCount = 0; + long totalLineCount = 0; + + Set<Key> buildTargetKeys = new HashSet<>(); + Set<Key> branchKeys = new HashSet<>(); + List<Key> profilingPointKeyList = new ArrayList<>(); + List<String> linkList = new ArrayList<>(); + + long startTimestamp = report.getStartTimestamp(); + long endTimestamp = report.getEndTimestamp(); + String testName = report.getTest().toStringUtf8(); + String testBuildId = report.getBuildInfo().getId().toStringUtf8(); + String hostName = report.getHostInfo().getHostname().toStringUtf8(); + + TestEntity testEntity = new TestEntity(testName); + + com.googlecode.objectify.Key testRunKey = + testEntity.getTestRunKey(report.getStartTimestamp()); + + testEntityList.add(testEntity); + + int testCaseRunEntityIndex = 0; + testCaseRunEntityList.add(new TestCaseRunEntity()); + // Process test cases + for (VtsReportMessage.TestCaseReportMessage testCase : report.getTestCaseList()) { + String testCaseName = testCase.getName().toStringUtf8(); + VtsReportMessage.TestCaseResult result = testCase.getTestResult(); + // Track global pass/fail counts + if (result == VtsReportMessage.TestCaseResult.TEST_CASE_RESULT_PASS) { + ++passCount; + } else if (result != VtsReportMessage.TestCaseResult.TEST_CASE_RESULT_SKIP) { + ++failCount; + } + if (testCase.getSystraceCount() > 0 + && testCase.getSystraceList().get(0).getUrlCount() > 0) { + String systraceLink = testCase.getSystraceList().get(0).getUrl(0).toStringUtf8(); + linkList.add(systraceLink); + } + + // Process coverage data for test case + for (VtsReportMessage.CoverageReportMessage coverage : testCase.getCoverageList()) { + CoverageEntity coverageEntity = + CoverageEntity.fromCoverageReport(testRunKey, testCaseName, coverage); + if (coverageEntity == null) { + log.warn("Invalid coverage report in test run " + testRunKey); + } else { + coveredLineCount += coverageEntity.getCoveredCount(); + totalLineCount += coverageEntity.getTotalCount(); + coverageEntityList.add(coverageEntity); + } + } + + // Process profiling data for test case + for (VtsReportMessage.ProfilingReportMessage profiling : testCase.getProfilingList()) { + ProfilingPointRunEntity profilingPointRunEntity = + ProfilingPointRunEntity.fromProfilingReport(testRunKey, profiling); + if (profilingPointRunEntity == null) { + log.warn("Invalid profiling report in test run " + testRunKey); + } else { + profilingPointRunEntityList.add(profilingPointRunEntity); + profilingPointKeyList.add(profilingPointRunEntity.getKey()); + testEntity.setHasProfilingData(true); + } + } + + TestCaseRunEntity testCaseRunEntity = testCaseRunEntityList.get(testCaseRunEntityIndex); + if (!testCaseRunEntity.addTestCase(testCaseName, result.getNumber())) { + testCaseRunEntity = new TestCaseRunEntity(); + testCaseRunEntity.addTestCase(testCaseName, result.getNumber()); + testCaseRunEntityList.add(testCaseRunEntity); + testCaseRunEntityIndex++; + } + } + + // Process device information + long testRunType = 0; + for (VtsReportMessage.AndroidDeviceInfoMessage device : report.getDeviceInfoList()) { + DeviceInfoEntity deviceInfoEntity = + DeviceInfoEntity.fromDeviceInfoMessage(testRunKey, device); + if (deviceInfoEntity == null) { + log.warn("Invalid device info in test run " + testRunKey); + } else { + // Run type on devices must be the same, else set to OTHER + TestRunEntity.TestRunType runType = + TestRunEntity.TestRunType.fromBuildId(deviceInfoEntity.getBuildId()); + if (runType == null) { + testRunType = TestRunEntity.TestRunType.OTHER.getNumber(); + } else { + testRunType = runType.getNumber(); + } + deviceInfoEntityList.add(deviceInfoEntity); + BuildTargetEntity target = new BuildTargetEntity(deviceInfoEntity.getBuildFlavor()); + if (buildTargetKeys.add(target.getKey())) { + buildTargetEntityList.add(target); + } + BranchEntity branch = new BranchEntity(deviceInfoEntity.getBranch()); + if (branchKeys.add(branch.getKey())) { + branchEntityList.add(branch); + } + } + } + + // Overall run type should be determined by the device builds unless test build is OTHER + if (testRunType == TestRunEntity.TestRunType.OTHER.getNumber()) { + testRunType = TestRunEntity.TestRunType.fromBuildId(testBuildId).getNumber(); + } else if (TestRunEntity.TestRunType.fromBuildId(testBuildId) + == TestRunEntity.TestRunType.OTHER) { + testRunType = TestRunEntity.TestRunType.OTHER.getNumber(); + } + + // Process global coverage data + for (VtsReportMessage.CoverageReportMessage coverage : report.getCoverageList()) { + CoverageEntity coverageEntity = + CoverageEntity.fromCoverageReport(testRunKey, new String(), coverage); + if (coverageEntity == null) { + log.warn("Invalid coverage report in test run " + testRunKey); + } else { + coveredLineCount += coverageEntity.getCoveredCount(); + totalLineCount += coverageEntity.getTotalCount(); + coverageEntityList.add(coverageEntity); + } + } + + // Process global API coverage data + for (VtsReportMessage.ApiCoverageReportMessage apiCoverage : report.getApiCoverageList()) { + VtsReportMessage.HalInterfaceMessage halInterfaceMessage = + apiCoverage.getHalInterface(); + List<String> halApiList = + apiCoverage + .getHalApiList() + .stream() + .map(h -> h.toStringUtf8()) + .collect(Collectors.toList()); + List<String> coveredHalApiList = + apiCoverage + .getCoveredHalApiList() + .stream() + .map(h -> h.toStringUtf8()) + .collect(Collectors.toList()); + ApiCoverageEntity apiCoverageEntity = + new ApiCoverageEntity( + testRunKey, + halInterfaceMessage.getHalPackageName().toStringUtf8(), + halInterfaceMessage.getHalVersionMajor(), + halInterfaceMessage.getHalVersionMinor(), + halInterfaceMessage.getHalInterfaceName().toStringUtf8(), + halApiList, + coveredHalApiList); + apiCoverageEntityList.add(apiCoverageEntity); + } + + // Process global profiling data + for (VtsReportMessage.ProfilingReportMessage profiling : report.getProfilingList()) { + ProfilingPointRunEntity profilingPointRunEntity = + ProfilingPointRunEntity.fromProfilingReport(testRunKey, profiling); + if (profilingPointRunEntity == null) { + log.warn("Invalid profiling report in test run " + testRunKey); + } else { + profilingPointRunEntityList.add(profilingPointRunEntity); + profilingPointKeyList.add(profilingPointRunEntity.getKey()); + testEntity.setHasProfilingData(true); + } + } + + // Process log data + for (VtsReportMessage.LogMessage log : report.getLogList()) { + if (log.hasUrl()) { + linkList.add(log.getUrl().toStringUtf8()); + } + } + // Process url resource + for (VtsReportMessage.UrlResourceMessage resource : report.getLinkResourceList()) { + if (resource.hasUrl()) { + linkList.add(resource.getUrl().toStringUtf8()); + } + } + + boolean hasCodeCoverage = totalLineCount > 0 && coveredLineCount >= 0; + TestRunEntity testRunEntity = + new TestRunEntity( + testEntity.getOldKey(), + testRunType, + startTimestamp, + endTimestamp, + testBuildId, + hostName, + passCount, + failCount, + hasCodeCoverage, + new ArrayList<>(), + linkList); + testRunEntityList.add(testRunEntity); + + CodeCoverageEntity codeCoverageEntity = + new CodeCoverageEntity( + testRunEntity.getId(), + testRunEntity.getKey(), + coveredLineCount, + totalLineCount); + codeCoverageEntityList.add(codeCoverageEntity); + + ofy().transact( + () -> { + List<Long> testCaseIds = new ArrayList<>(); + for (Object entity : allEntityList) { + if (entity instanceof List) { + List listEntity = (List) entity; + if (listEntity.size() > 0 + && listEntity.get(0) instanceof TestCaseRunEntity) { + List<TestCaseRunEntity> dashboardEntityList = + (List<TestCaseRunEntity>) entity; + Map< + com.googlecode.objectify.Key< + TestCaseRunEntity>, + TestCaseRunEntity> + testCaseRunEntityMap = + DashboardEntity.saveAll( + dashboardEntityList, + this + .MAX_ENTITY_SIZE_PER_TRANSACTION); + + testCaseIds = + testCaseRunEntityMap + .values() + .stream() + .map( + testCaseRunEntity -> + testCaseRunEntity.getId()) + .collect(Collectors.toList()); + } else if (listEntity.size() > 0 + && listEntity.get(0) instanceof TestRunEntity) { + List<TestRunEntity> dashboardEntityList = + (List<TestRunEntity>) entity; + dashboardEntityList.get(0).setTestCaseIds(testCaseIds); + DashboardEntity.saveAll( + dashboardEntityList, + this.MAX_ENTITY_SIZE_PER_TRANSACTION); + } else { + List<DashboardEntity> dashboardEntityList = + (List<DashboardEntity>) entity; + DashboardEntity.saveAll( + dashboardEntityList, + this.MAX_ENTITY_SIZE_PER_TRANSACTION); + } + } + } + }); + } + + /** + * Upload data from a test plan report message + * + * @param report The test plan report containing data to upload. + */ + private void insertTestPlanReport(TestPlanReportMessage report) { + List<DeviceInfoEntity> deviceInfoEntityList = new ArrayList<>(); + List<HalApiEntity> halApiEntityList = new ArrayList<>(); + + List allEntityList = Arrays.asList(deviceInfoEntityList, halApiEntityList); + + List<String> testModules = report.getTestModuleNameList(); + List<Long> testTimes = report.getTestModuleStartTimestampList(); + if (testModules.size() != testTimes.size() || !report.hasTestPlanName()) { + log.error("TestPlanReportMessage is missing information."); + return; + } + + String testPlanName = report.getTestPlanName(); + TestPlanEntity testPlanEntity = new TestPlanEntity(testPlanName); + List<com.googlecode.objectify.Key<TestRunEntity>> testRunKeyList = new ArrayList<>(); + for (int index = 0; index < testModules.size(); index++) { + String test = testModules.get(index); + long time = testTimes.get(index); + com.googlecode.objectify.Key testKey = + com.googlecode.objectify.Key.create(TestEntity.class, test); + com.googlecode.objectify.Key testRunKey = + com.googlecode.objectify.Key.create(testKey, TestRunEntity.class, time); + testRunKeyList.add(testRunKey); + } + + Map<com.googlecode.objectify.Key<TestRunEntity>, TestRunEntity> testRunEntityMap = + ofy().load().keys(() -> testRunKeyList.iterator()); + + testRunKeyList.forEach( + (v) -> { + log.debug("TestRunEntity key value => " + v); + }); + log.debug("testRunEntityMap value => " + testRunEntityMap.values()); + log.debug("testRunEntityMap keySet => " + testRunEntityMap.keySet()); + + long passCount = 0; + long failCount = 0; + long startTimestamp = -1; + long endTimestamp = -1; + String testBuildId = null; + long testType = -1; + Set<DeviceInfoEntity> deviceInfoEntitySet = new HashSet<>(); + for (TestRunEntity testRunEntity : testRunEntityMap.values()) { + passCount += testRunEntity.getPassCount(); + failCount += testRunEntity.getFailCount(); + if (startTimestamp < 0 || testRunEntity.getStartTimestamp() < startTimestamp) { + startTimestamp = testRunEntity.getStartTimestamp(); + } + if (endTimestamp < 0 || testRunEntity.getEndTimestamp() > endTimestamp) { + endTimestamp = testRunEntity.getEndTimestamp(); + } + testType = testRunEntity.getType(); + testBuildId = testRunEntity.getTestBuildId(); + + List<DeviceInfoEntity> deviceInfoEntityListWithTestRunKey = + ofy().load() + .type(DeviceInfoEntity.class) + .ancestor(testRunEntity.getOfyKey()) + .list(); + + for (DeviceInfoEntity deviceInfoEntity : deviceInfoEntityListWithTestRunKey) { + deviceInfoEntitySet.add(deviceInfoEntity); + } + } + + if (startTimestamp < 0 || testBuildId == null || testType == -1) { + log.debug("startTimestamp => " + startTimestamp); + log.debug("testBuildId => " + testBuildId); + log.debug("type => " + testType); + log.error("Couldn't infer test run information from runs."); + return; + } + + TestPlanRunEntity testPlanRunEntity = + new TestPlanRunEntity( + testPlanEntity.getKey(), + testPlanName, + testType, + startTimestamp, + endTimestamp, + testBuildId, + passCount, + failCount, + 0L, + 0L, + testRunKeyList); + + // Create the device infos. + for (DeviceInfoEntity device : deviceInfoEntitySet) { + deviceInfoEntityList.add(device.copyWithParent(testPlanRunEntity.getOfyKey())); + } + + // Process global HAL API coverage data + for (VtsReportMessage.ApiCoverageReportMessage apiCoverage : report.getHalApiReportList()) { + VtsReportMessage.HalInterfaceMessage halInterfaceMessage = + apiCoverage.getHalInterface(); + List<String> halApiList = + apiCoverage + .getHalApiList() + .stream() + .map(h -> h.toStringUtf8()) + .collect(Collectors.toList()); + List<String> coveredHalApiList = + apiCoverage + .getCoveredHalApiList() + .stream() + .map(h -> h.toStringUtf8()) + .collect(Collectors.toList()); + HalApiEntity halApiEntity = + new HalApiEntity( + testPlanRunEntity.getOfyKey(), + halInterfaceMessage.getHalReleaseLevel().toStringUtf8(), + halInterfaceMessage.getHalPackageName().toStringUtf8(), + halInterfaceMessage.getHalVersionMajor(), + halInterfaceMessage.getHalVersionMinor(), + halInterfaceMessage.getHalInterfaceName().toStringUtf8(), + halApiList, + coveredHalApiList); + halApiEntityList.add(halApiEntity); + } + + ofy().transact( + () -> { + testPlanEntity.save(); + testPlanRunEntity.save(); + for (Object entity : allEntityList) { + List<DashboardEntity> dashboardEntityList = + (List<DashboardEntity>) entity; + Map<com.googlecode.objectify.Key<DashboardEntity>, DashboardEntity> + mapInfo = + DashboardEntity.saveAll( + dashboardEntityList, + this.MAX_ENTITY_SIZE_PER_TRANSACTION); + } + }); + + // Add the task to calculate total number API list. + testPlanRunEntity.addCoverageApiTask(); + } } diff --git a/src/main/java/com/android/vts/api/TestDataForDevServlet.java b/src/main/java/com/android/vts/api/TestDataForDevServlet.java index 7fc067f..2ed387b 100644 --- a/src/main/java/com/android/vts/api/TestDataForDevServlet.java +++ b/src/main/java/com/android/vts/api/TestDataForDevServlet.java @@ -490,14 +490,14 @@ public class TestDataForDevServlet extends HttpServlet { BuildTargetEntity buildTargetEntity = new BuildTargetEntity( buildTarget.targetName); - datastore.put(buildTargetEntity.toEntity()); + buildTargetEntity.save(); }); testRun.branchList.forEach( branch -> { BranchEntity branchEntity = new BranchEntity(branch.branchName); - datastore.put(branchEntity.toEntity()); + branchEntity.save(); }); boolean hasCodeCoverage = @@ -630,13 +630,14 @@ public class TestDataForDevServlet extends HttpServlet { testBuildId, passCount, failCount, - 0L, - 0L, + 0L, + 0L, testRunKeys); // Create the device infos. for (DeviceInfoEntity device : devices) { - datastore.put(device.copyWithParent(testPlanRun.key).toEntity()); + datastore.put( + device.copyWithParent(testPlanRun.getOfyKey()).toEntity()); } datastore.put(testPlanRun.toEntity()); diff --git a/src/main/java/com/android/vts/config/ObjectifyListener.java b/src/main/java/com/android/vts/config/ObjectifyListener.java index 9c4a705..5f35abd 100644 --- a/src/main/java/com/android/vts/config/ObjectifyListener.java +++ b/src/main/java/com/android/vts/config/ObjectifyListener.java @@ -23,6 +23,7 @@ import com.android.vts.entity.ApiCoverageExcludedEntity; import com.android.vts.entity.CodeCoverageEntity; import com.android.vts.entity.CoverageEntity; import com.android.vts.entity.DeviceInfoEntity; +import com.android.vts.entity.HalApiEntity; import com.android.vts.entity.ProfilingPointEntity; import com.android.vts.entity.ProfilingPointRunEntity; import com.android.vts.entity.ProfilingPointSummaryEntity; @@ -87,6 +88,7 @@ public class ObjectifyListener implements ServletContextListener { ObjectifyService.register(BranchEntity.class); ObjectifyService.register(BuildTargetEntity.class); + ObjectifyService.register(HalApiEntity.class); ObjectifyService.register(ApiCoverageEntity.class); ObjectifyService.register(ApiCoverageExcludedEntity.class); ObjectifyService.register(CodeCoverageEntity.class); @@ -106,6 +108,7 @@ public class ObjectifyListener implements ServletContextListener { ObjectifyService.register(TestStatusEntity.class); ObjectifyService.register(TestSuiteFileEntity.class); ObjectifyService.register(TestSuiteResultEntity.class); + ObjectifyService.register(TestAcknowledgmentEntity.class); ObjectifyService.register(RoleEntity.class); ObjectifyService.register(UserEntity.class); ObjectifyService.begin(); @@ -123,6 +126,9 @@ public class ObjectifyListener implements ServletContextListener { servletContextEvent .getServletContext() + .setAttribute("systemConfigProp", systemConfigProp); + servletContextEvent + .getServletContext() .setAttribute("dataStoreFactory", DATA_STORE_FACTORY); servletContextEvent .getServletContext() diff --git a/src/main/java/com/android/vts/entity/ApiCoverageEntity.java b/src/main/java/com/android/vts/entity/ApiCoverageEntity.java index 0a41743..9c74032 100644 --- a/src/main/java/com/android/vts/entity/ApiCoverageEntity.java +++ b/src/main/java/com/android/vts/entity/ApiCoverageEntity.java @@ -35,167 +35,125 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -/** - * Entity Class for ApiCoverageEntity - */ +/** Entity Class for ApiCoverageEntity */ @Cache @Entity(name = "ApiCoverage") @EqualsAndHashCode(of = "id") @NoArgsConstructor @JsonAutoDetect(fieldVisibility = Visibility.ANY) @JsonIgnoreProperties({"id", "parent"}) -public class ApiCoverageEntity { - - /** - * ApiCoverageEntity id field - */ - @Id - @Getter - @Setter - String id; - - @Parent - @Getter - Key<?> parent; - - /** - * HAL package name. e.g. android.hardware.foo. - */ - @Index - @Getter - @Setter - String halPackageName; - - /** - * HAL (major) version. e.g. 1. - */ - @Index - @Getter - @Setter - int halMajorVersion; - - /** - * HAL (minor) version. e.g. 0. - */ - @Index - @Getter - @Setter - int halMinorVersion; - - /** - * HAL interface name. e.g. IFoo. - */ - @Index - @Getter - @Setter - String halInterfaceName; - - /** - * List of HAL API - */ - @Getter - @Setter - List<String> halApi; - - /** - * List of HAL covered API - */ - @Getter - @Setter - List<String> coveredHalApi; - - /** - * When this record was created or updated - */ - @Index - Date updated; - - /** - * Constructor function for ApiCoverageEntity Class - */ - public ApiCoverageEntity(com.google.appengine.api.datastore.Key testRunKey, String halPackageName, - int halVersionMajor, int halVersionMinor, String halInterfaceName, List<String> halApi, - List<String> coveredHalApi) { - - this.parent = getParentKey(testRunKey); - - this.halPackageName = halPackageName; - this.halMajorVersion = halVersionMajor; - this.halMinorVersion = halVersionMinor; - this.halInterfaceName = halInterfaceName; - this.halApi = halApi; - this.coveredHalApi = coveredHalApi; - } - - /** - * Constructor function for ApiCoverageEntity Class with objectify Key. - */ - public ApiCoverageEntity(Key testRunKey, String halPackageName, - int halVersionMajor, int halVersionMinor, String halInterfaceName, - List<String> halApi, List<String> coveredHalApi) { - this.parent = testRunKey; - - this.halPackageName = halPackageName; - this.halMajorVersion = halVersionMajor; - this.halMinorVersion = halVersionMinor; - this.halInterfaceName = halInterfaceName; - this.halApi = halApi; - this.coveredHalApi = coveredHalApi; - } - - /** - * Get objectify Key from datastore Key type - */ - private Key getParentKey(com.google.appengine.api.datastore.Key testRunKey) { - Key testParentKey = Key.create(TestEntity.class, testRunKey.getParent().getName()); - return Key.create(testParentKey, TestRunEntity.class, testRunKey.getId()); - } - - /** - * Get UrlSafeKey from ApiCoverageEntity Information - */ - public String getUrlSafeKey() { - Key uuidKey = Key.create(this.parent, ApiCoverageEntity.class, this.id); - return uuidKey.toUrlSafe(); - } - - /** - * Saving function for the instance of this class - */ - public Key<ApiCoverageEntity> save() { - this.id = UUID.randomUUID().toString(); - this.updated = new Date(); - return ofy().save().entity(this).now(); - } - - /** - * Get List of ApiCoverageEntity by HAL interface name - */ - public static ApiCoverageEntity getByUrlSafeKey(String urlSafeKey) { - return ofy().load() - .type(ApiCoverageEntity.class) - .filterKey(com.google.cloud.datastore.Key.fromUrlSafe(urlSafeKey)) - .first() - .now(); - } - - /** - * Get List of ApiCoverageEntity by HAL interface name - */ - public static List<ApiCoverageEntity> getByInterfaceNameList(String halInterfaceName) { - return ofy().load() - .type(ApiCoverageEntity.class) - .filter("halInterfaceName", halInterfaceName) - .list(); - } - - /** - * Get List of ApiCoverageEntity by HAL package name - */ - public static List<ApiCoverageEntity> getByPackageNameList(String packageName) { - return ofy().load() - .type(ApiCoverageEntity.class) - .filter("halPackageName", packageName) - .list(); - } +public class ApiCoverageEntity implements DashboardEntity { + + /** ApiCoverageEntity id field */ + @Id @Getter @Setter String id; + + @Parent @Getter Key<?> parent; + + /** HAL package name. e.g. android.hardware.foo. */ + @Index @Getter @Setter String halPackageName; + + /** HAL (major) version. e.g. 1. */ + @Index @Getter @Setter int halMajorVersion; + + /** HAL (minor) version. e.g. 0. */ + @Index @Getter @Setter int halMinorVersion; + + /** HAL interface name. e.g. IFoo. */ + @Index @Getter @Setter String halInterfaceName; + + /** List of HAL API */ + @Getter @Setter List<String> halApi; + + /** List of HAL covered API */ + @Getter @Setter List<String> coveredHalApi; + + /** When this record was created or updated */ + @Index Date updated; + + /** Constructor function for ApiCoverageEntity Class */ + public ApiCoverageEntity( + com.google.appengine.api.datastore.Key testRunKey, + String halPackageName, + int halVersionMajor, + int halVersionMinor, + String halInterfaceName, + List<String> halApi, + List<String> coveredHalApi) { + this.id = UUID.randomUUID().toString(); + this.parent = getParentKey(testRunKey); + + this.halPackageName = halPackageName; + this.halMajorVersion = halVersionMajor; + this.halMinorVersion = halVersionMinor; + this.halInterfaceName = halInterfaceName; + this.halApi = halApi; + this.coveredHalApi = coveredHalApi; + this.updated = new Date(); + } + + /** Constructor function for ApiCoverageEntity Class with objectify Key. */ + public ApiCoverageEntity( + Key testRunKey, + String halPackageName, + int halVersionMajor, + int halVersionMinor, + String halInterfaceName, + List<String> halApi, + List<String> coveredHalApi) { + this.id = UUID.randomUUID().toString(); + this.parent = testRunKey; + + this.halPackageName = halPackageName; + this.halMajorVersion = halVersionMajor; + this.halMinorVersion = halVersionMinor; + this.halInterfaceName = halInterfaceName; + this.halApi = halApi; + this.coveredHalApi = coveredHalApi; + this.updated = new Date(); + } + + /** Get objectify Key from datastore Key type */ + private Key getParentKey(com.google.appengine.api.datastore.Key testRunKey) { + Key testParentKey = Key.create(TestEntity.class, testRunKey.getParent().getName()); + return Key.create(testParentKey, TestRunEntity.class, testRunKey.getId()); + } + + /** Get UrlSafeKey from ApiCoverageEntity Information */ + public String getUrlSafeKey() { + Key uuidKey = Key.create(this.parent, ApiCoverageEntity.class, this.id); + return uuidKey.toUrlSafe(); + } + + /** Saving function for the instance of this class */ + @Override + public Key<ApiCoverageEntity> save() { + this.id = UUID.randomUUID().toString(); + this.updated = new Date(); + return ofy().save().entity(this).now(); + } + + /** Get List of ApiCoverageEntity by HAL interface name */ + public static ApiCoverageEntity getByUrlSafeKey(String urlSafeKey) { + return ofy().load() + .type(ApiCoverageEntity.class) + .filterKey(com.google.cloud.datastore.Key.fromUrlSafe(urlSafeKey)) + .first() + .now(); + } + + /** Get List of ApiCoverageEntity by HAL interface name */ + public static List<ApiCoverageEntity> getByInterfaceNameList(String halInterfaceName) { + return ofy().load() + .type(ApiCoverageEntity.class) + .filter("halInterfaceName", halInterfaceName) + .list(); + } + + /** Get List of ApiCoverageEntity by HAL package name */ + public static List<ApiCoverageEntity> getByPackageNameList(String packageName) { + return ofy().load() + .type(ApiCoverageEntity.class) + .filter("halPackageName", packageName) + .list(); + } } diff --git a/src/main/java/com/android/vts/entity/ApiCoverageExcludedEntity.java b/src/main/java/com/android/vts/entity/ApiCoverageExcludedEntity.java index 834f8cc..61bd6a7 100644 --- a/src/main/java/com/android/vts/entity/ApiCoverageExcludedEntity.java +++ b/src/main/java/com/android/vts/entity/ApiCoverageExcludedEntity.java @@ -28,11 +28,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import java.util.Collection; import java.util.Date; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import static com.googlecode.objectify.ObjectifyService.ofy; @@ -46,10 +43,7 @@ import static com.googlecode.objectify.ObjectifyService.ofy; @NoArgsConstructor @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) @JsonIgnoreProperties({"id", "parent"}) -public class ApiCoverageExcludedEntity { - - // The maximum number of entity list size to insert datastore - private static final int maxNumEntitySize = 500; +public class ApiCoverageExcludedEntity implements DashboardEntity { /** ApiCoverageEntity id field */ @Id @Getter @Setter private String id; @@ -82,11 +76,12 @@ public class ApiCoverageExcludedEntity { String interfaceName, String apiName, String comment) { - + this.id = this.getObjectifyId(); this.packageName = packageName; this.interfaceName = interfaceName; this.apiName = apiName; this.comment = comment; + this.updated = new Date(); this.setVersions(version); } @@ -125,44 +120,14 @@ public class ApiCoverageExcludedEntity { } /** Saving function for the instance of this class */ + @Override public Key<ApiCoverageExcludedEntity> save() { - this.id = this.getObjectifyId(); - this.updated = new Date(); return ofy().save().entity(this).now(); } - /** Spliting a list based on a given size */ - public static <T> Collection<List<T>> partitionBasedOnSize(List<T> inputList, int size) { - final AtomicInteger counter = new AtomicInteger(0); - return inputList - .stream() - .collect(Collectors.groupingBy(s -> counter.getAndIncrement() / size)) - .values(); - } - - /** Saving function with parameter of this entity List */ - public static void saveAll(List<ApiCoverageExcludedEntity> apiCoverageExcludedEntityList) { - List<ApiCoverageExcludedEntity> entityWithIdList = - apiCoverageExcludedEntityList - .stream() - .map( - entity -> { - entity.setId(entity.getObjectifyId()); - entity.setUpdated(new Date()); - return entity; - }) - .collect(Collectors.toList()); - - partitionBasedOnSize(entityWithIdList, maxNumEntitySize) - .stream() - .forEach( - entityList -> { - ofy().save().entities(entityList).now(); - }); - } - /** Get All Key List of ApiCoverageExcludedEntity */ public static List<Key<ApiCoverageExcludedEntity>> getAllKeyList() { return ofy().load().type(ApiCoverageExcludedEntity.class).keys().list(); } + } diff --git a/src/main/java/com/android/vts/entity/BranchEntity.java b/src/main/java/com/android/vts/entity/BranchEntity.java index fa3f7a5..1c9ea52 100644 --- a/src/main/java/com/android/vts/entity/BranchEntity.java +++ b/src/main/java/com/android/vts/entity/BranchEntity.java @@ -41,8 +41,6 @@ public class BranchEntity implements DashboardEntity { public static final String KIND = "Branch"; // The entity kind. - public Key key; // The key for the entity in the database. - @Id private String name; /** @@ -51,7 +49,11 @@ public class BranchEntity implements DashboardEntity { * @param branchName The name of the branch. */ public BranchEntity(String branchName) { - this.key = KeyFactory.createKey(KIND, branchName); + this.name = branchName; + } + + public Key getKey() { + return KeyFactory.createKey(KIND, this.name); } /** find by branch name */ @@ -86,9 +88,10 @@ public class BranchEntity implements DashboardEntity { } } + /** Saving function for the instance of this class */ @Override - public Entity toEntity() { - return new Entity(this.key); + public com.googlecode.objectify.Key<BranchEntity> save() { + return ofy().save().entity(this).now(); } /** diff --git a/src/main/java/com/android/vts/entity/BuildTargetEntity.java b/src/main/java/com/android/vts/entity/BuildTargetEntity.java index b204d0c..4180213 100644 --- a/src/main/java/com/android/vts/entity/BuildTargetEntity.java +++ b/src/main/java/com/android/vts/entity/BuildTargetEntity.java @@ -41,8 +41,6 @@ public class BuildTargetEntity implements DashboardEntity { public static final String KIND = "BuildTarget"; // The entity kind. - public Key key; // The key for the entity in the database. - @Id private String name; /** @@ -51,12 +49,16 @@ public class BuildTargetEntity implements DashboardEntity { * @param targetName The name of the build target. */ public BuildTargetEntity(String targetName) { - this.key = KeyFactory.createKey(KIND, targetName); + this.name = targetName; } + public Key getKey() { + return KeyFactory.createKey(KIND, this.name); + } + /** Saving function for the instance of this class */ @Override - public Entity toEntity() { - return new Entity(this.key); + public com.googlecode.objectify.Key<BuildTargetEntity> save() { + return ofy().save().entity(this).now(); } /** find by Build Target Name */ diff --git a/src/main/java/com/android/vts/entity/CodeCoverageEntity.java b/src/main/java/com/android/vts/entity/CodeCoverageEntity.java index 63034ef..2a46b1f 100644 --- a/src/main/java/com/android/vts/entity/CodeCoverageEntity.java +++ b/src/main/java/com/android/vts/entity/CodeCoverageEntity.java @@ -16,13 +16,10 @@ package com.android.vts.entity; -import com.android.vts.util.UrlUtil; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.google.appengine.api.datastore.KeyFactory; -import com.google.gson.Gson; -import com.google.gson.JsonElement; import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Cache; @@ -35,10 +32,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import java.util.ArrayList; import java.util.Date; -import java.util.List; -import java.util.logging.Level; import java.util.logging.Logger; import static com.googlecode.objectify.ObjectifyService.ofy; @@ -50,7 +44,7 @@ import static com.googlecode.objectify.ObjectifyService.ofy; @NoArgsConstructor @JsonAutoDetect(fieldVisibility = Visibility.ANY) @JsonIgnoreProperties({"id", "parent"}) -public class CodeCoverageEntity { +public class CodeCoverageEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(CodeCoverageEntity.class.getName()); public static final String KIND = "CodeCoverage"; @@ -59,7 +53,7 @@ public class CodeCoverageEntity { public static final String TOTAL_LINE_COUNT = "totalLineCount"; /** CodeCoverageEntity id field */ - @Id @Getter @Setter long id; + @Id @Getter @Setter Long id; @Parent @Getter Key<?> parent; @@ -82,6 +76,20 @@ public class CodeCoverageEntity { this.totalLineCount = totalLineCount; } + /** Constructor function for ApiCoverageEntity Class */ + public CodeCoverageEntity( + long id, + com.google.appengine.api.datastore.Key testRunKey, + long coveredLineCount, + long totalLineCount) { + this.id = id; + + this.parent = getParentKey(testRunKey); + + this.coveredLineCount = coveredLineCount; + this.totalLineCount = totalLineCount; + } + /** Constructor function for ApiCoverageEntity Class with objectify key*/ public CodeCoverageEntity(Key testRunKey, long coveredLineCount, long totalLineCount) { this.parent = testRunKey; @@ -102,6 +110,7 @@ public class CodeCoverageEntity { } /** Saving function for the instance of this class */ + @Override public Key<CodeCoverageEntity> save() { this.id = this.getParent().getId(); this.updated = new Date(); diff --git a/src/main/java/com/android/vts/entity/CodeCoverageFileEntity.java b/src/main/java/com/android/vts/entity/CodeCoverageFileEntity.java index cb6d06c..fe4e8a0 100644 --- a/src/main/java/com/android/vts/entity/CodeCoverageFileEntity.java +++ b/src/main/java/com/android/vts/entity/CodeCoverageFileEntity.java @@ -37,11 +37,10 @@ import static com.googlecode.objectify.ObjectifyService.ofy; @Entity(name = "CodeCoverageFile") @EqualsAndHashCode(of = "id") @NoArgsConstructor -public class CodeCoverageFileEntity { +public class CodeCoverageFileEntity implements DashboardEntity { /** CodeCoverageFileEntity testName field */ - @Id - @Getter @Setter long id; + @Id @Getter @Setter Long id; @Parent @Getter @Setter private Key<?> coverageParent; @@ -94,8 +93,9 @@ public class CodeCoverageFileEntity { } /** Saving function for the instance of this class */ - public void save() { + @Override + public Key<CodeCoverageFileEntity> save() { this.isIgnored = false; - ofy().save().entity(this).now(); + return ofy().save().entity(this).now(); } } diff --git a/src/main/java/com/android/vts/entity/CoverageEntity.java b/src/main/java/com/android/vts/entity/CoverageEntity.java index 82b6690..50d2f4e 100644 --- a/src/main/java/com/android/vts/entity/CoverageEntity.java +++ b/src/main/java/com/android/vts/entity/CoverageEntity.java @@ -21,14 +21,11 @@ import static com.googlecode.objectify.ObjectifyService.ofy; import com.android.vts.proto.VtsReportMessage.CoverageReportMessage; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; -import com.google.cloud.datastore.PathElement; -import com.googlecode.objectify.LoadResult; import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Parent; -import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; @@ -48,231 +45,268 @@ import lombok.Setter; @Data @NoArgsConstructor /** Object describing coverage data gathered for a file. */ -public class CoverageEntity implements Serializable { - - protected static final Logger logger = Logger.getLogger(CoverageEntity.class.getName()); - - public static final String KIND = "Coverage"; - - public static String GERRIT_URI; - - // Property keys - public static final String GROUP = "group"; - public static final String COVERED_LINE_COUNT = "coveredCount"; - public static final String TOTAL_LINE_COUNT = "totalCount"; - public static final String FILE_PATH = "filePath"; - public static final String PROJECT_NAME = "projectName"; - public static final String PROJECT_VERSION = "projectVersion"; - public static final String LINE_COVERAGE = "lineCoverage"; - - @Ignore - @Getter - @Setter - private Key parentKey; - - @Id - @Getter - @Setter - private Long id; - - @Parent - @Getter - @Setter - private com.googlecode.objectify.Key<?> testParent; - - @Index - @Getter - @Setter - private String group; - - @Getter - @Setter - private long coveredCount; - - @Getter - @Setter - private long totalCount; - - @Index - @Getter - @Setter - private String filePath; - - @Getter - @Setter - private String projectName; - - @Getter - @Setter - private String projectVersion; - - @Getter - @Setter - private List<Long> lineCoverage; - - /** - * CoverageEntity isIgnored field - */ - @Index - @Getter - @Setter - Boolean isIgnored; - - /** - * Create a CoverageEntity object for a file. - * - * @param parentKey The key to the parent TestRunEntity object in the database. - * @param group The group within the test run describing the coverage. - * @param coveredLineCount The total number of covered lines in the file. - * @param totalLineCount The total number of uncovered executable lines in the file. - * @param filePath The path to the file. - * @param projectName The name of the git project. - * @param projectVersion The commit hash of the project at the time the test was executed. - * @param lineCoverage List of coverage counts per executable line in the file. - */ - public CoverageEntity(Key parentKey, String group, long coveredLineCount, long totalLineCount, - String filePath, String projectName, String projectVersion, List<Long> lineCoverage) { - this.parentKey = parentKey; - this.group = group; - this.coveredCount = coveredLineCount; - this.totalCount = totalLineCount; - this.filePath = filePath; - this.projectName = projectName; - this.projectVersion = projectVersion; - this.lineCoverage = lineCoverage; - } - - /** - * find coverage entity by ID - */ - public static CoverageEntity findById(String testName, String testRunId, String id) { - com.googlecode.objectify.Key testKey = com.googlecode.objectify.Key - .create(TestEntity.class, testName); - com.googlecode.objectify.Key testRunKey = com.googlecode.objectify.Key - .create(testKey, TestRunEntity.class, Long.parseLong(testRunId)); - return ofy().load().type(CoverageEntity.class).parent(testRunKey).id(Long.parseLong(id)).now(); - } - - public static void setPropertyValues(Properties newSystemConfigProp) { - GERRIT_URI = newSystemConfigProp.getProperty("gerrit.uri"); - } - - /** - * Saving function for the instance of this class - */ - public void save() { - ofy().save().entity(this).now(); - } - - /** - * Get percentage from calculating coveredCount and totalCount values - */ - public Double getPercentage() { - return Math.round(coveredCount * 10000d / totalCount) / 100d; - } - - /** - * Get Gerrit Url function from the attributes of this class - */ - public String getGerritUrl() throws UnsupportedEncodingException { - String gerritPath = GERRIT_URI + "/projects/" + - URLEncoder.encode(projectName, "UTF-8") + "/commits/" + - URLEncoder.encode(projectVersion, "UTF-8") + "/files/" + - URLEncoder.encode(filePath, "UTF-8") + "/content"; - return gerritPath; - } - - /* Comparator for sorting the list by isIgnored field */ - public static Comparator<CoverageEntity> isIgnoredComparator = new Comparator<CoverageEntity>() { - - public int compare(CoverageEntity coverageEntity1, CoverageEntity coverageEntity2) { - Boolean isIgnored1 = - Objects.isNull(coverageEntity1.getIsIgnored()) ? false : coverageEntity1.getIsIgnored(); - Boolean isIgnored2 = - Objects.isNull(coverageEntity2.getIsIgnored()) ? false : coverageEntity2.getIsIgnored(); - - // ascending order - return isIgnored1.compareTo(isIgnored2); +public class CoverageEntity implements DashboardEntity { + + protected static final Logger logger = Logger.getLogger(CoverageEntity.class.getName()); + + public static final String KIND = "Coverage"; + + public static String GERRIT_URI; + + // Property keys + public static final String GROUP = "group"; + public static final String COVERED_LINE_COUNT = "coveredCount"; + public static final String TOTAL_LINE_COUNT = "totalCount"; + public static final String FILE_PATH = "filePath"; + public static final String PROJECT_NAME = "projectName"; + public static final String PROJECT_VERSION = "projectVersion"; + public static final String LINE_COVERAGE = "lineCoverage"; + + @Ignore @Getter @Setter private Key parentKey; + + @Id @Getter @Setter private Long id; + + @Parent @Getter @Setter private com.googlecode.objectify.Key<?> testParent; + + @Index @Getter @Setter private String group; + + @Getter @Setter private long coveredCount; + + @Getter @Setter private long totalCount; + + @Index @Getter @Setter private String filePath; + + @Getter @Setter private String projectName; + + @Getter @Setter private String projectVersion; + + @Getter @Setter private List<Long> lineCoverage; + + /** CoverageEntity isIgnored field */ + @Index @Getter @Setter Boolean isIgnored; + + /** + * Create a CoverageEntity object for a file. + * + * @param parentKey The key to the parent TestRunEntity object in the database. + * @param group The group within the test run describing the coverage. + * @param coveredLineCount The total number of covered lines in the file. + * @param totalLineCount The total number of uncovered executable lines in the file. + * @param filePath The path to the file. + * @param projectName The name of the git project. + * @param projectVersion The commit hash of the project at the time the test was executed. + * @param lineCoverage List of coverage counts per executable line in the file. + */ + public CoverageEntity( + Key parentKey, + String group, + long coveredLineCount, + long totalLineCount, + String filePath, + String projectName, + String projectVersion, + List<Long> lineCoverage) { + this.parentKey = parentKey; + this.group = group; + this.coveredCount = coveredLineCount; + this.totalCount = totalLineCount; + this.filePath = filePath; + this.projectName = projectName; + this.projectVersion = projectVersion; + this.lineCoverage = lineCoverage; } - }; - - public Entity toEntity() { - Entity coverageEntity = new Entity(KIND, parentKey); - coverageEntity.setProperty(GROUP, group); - coverageEntity.setUnindexedProperty(COVERED_LINE_COUNT, coveredCount); - coverageEntity.setUnindexedProperty(TOTAL_LINE_COUNT, totalCount); - coverageEntity.setProperty(FILE_PATH, filePath); - coverageEntity.setUnindexedProperty(PROJECT_NAME, projectName); - coverageEntity.setUnindexedProperty(PROJECT_VERSION, projectVersion); - if (lineCoverage != null && lineCoverage.size() > 0) { - coverageEntity.setUnindexedProperty(LINE_COVERAGE, lineCoverage); + + /** + * Create a CoverageEntity object for a file. + * + * @param testParent The objectify key to the parent TestRunEntity object in the database. + * @param group The group within the test run describing the coverage. + * @param coveredLineCount The total number of covered lines in the file. + * @param totalLineCount The total number of uncovered executable lines in the file. + * @param filePath The path to the file. + * @param projectName The name of the git project. + * @param projectVersion The commit hash of the project at the time the test was executed. + * @param lineCoverage List of coverage counts per executable line in the file. + */ + public CoverageEntity( + com.googlecode.objectify.Key testParent, + String group, + long coveredLineCount, + long totalLineCount, + String filePath, + String projectName, + String projectVersion, + List<Long> lineCoverage) { + this.testParent = testParent; + this.group = group; + this.coveredCount = coveredLineCount; + this.totalCount = totalLineCount; + this.filePath = filePath; + this.projectName = projectName; + this.projectVersion = projectVersion; + this.lineCoverage = lineCoverage; + } + + /** find coverage entity by ID */ + public static CoverageEntity findById(String testName, String testRunId, String id) { + com.googlecode.objectify.Key testKey = + com.googlecode.objectify.Key.create(TestEntity.class, testName); + com.googlecode.objectify.Key testRunKey = + com.googlecode.objectify.Key.create( + testKey, TestRunEntity.class, Long.parseLong(testRunId)); + return ofy().load() + .type(CoverageEntity.class) + .parent(testRunKey) + .id(Long.parseLong(id)) + .now(); + } + + public static void setPropertyValues(Properties newSystemConfigProp) { + GERRIT_URI = newSystemConfigProp.getProperty("gerrit.uri"); + } + + /** Saving function for the instance of this class */ + @Override + public com.googlecode.objectify.Key<CoverageEntity> save() { + return ofy().save().entity(this).now(); + } + + /** Get percentage from calculating coveredCount and totalCount values */ + public Double getPercentage() { + return Math.round(coveredCount * 10000d / totalCount) / 100d; } - return coverageEntity; - } - - /** - * Convert an Entity object to a CoverageEntity. - * - * @param e The entity to process. - * @return CoverageEntity object with the properties from e, or null if incompatible. - */ - @SuppressWarnings("unchecked") - public static CoverageEntity fromEntity(Entity e) { - if (!e.getKind().equals(KIND) || !e.hasProperty(GROUP) || !e.hasProperty(COVERED_LINE_COUNT) - || !e.hasProperty(TOTAL_LINE_COUNT) || !e.hasProperty(FILE_PATH) - || !e.hasProperty(PROJECT_NAME) || !e.hasProperty(PROJECT_VERSION)) { - logger.log(Level.WARNING, "Missing coverage attributes in entity: " + e.toString()); - return null; + + /** Get Gerrit Url function from the attributes of this class */ + public String getGerritUrl() throws UnsupportedEncodingException { + String gerritPath = + GERRIT_URI + + "/projects/" + + URLEncoder.encode(projectName, "UTF-8") + + "/commits/" + + URLEncoder.encode(projectVersion, "UTF-8") + + "/files/" + + URLEncoder.encode(filePath, "UTF-8") + + "/content"; + return gerritPath; } - try { - String group = (String) e.getProperty(GROUP); - long coveredLineCount = (long) e.getProperty(COVERED_LINE_COUNT); - long totalLineCount = (long) e.getProperty(TOTAL_LINE_COUNT); - String filePath = (String) e.getProperty(FILE_PATH); - String projectName = (String) e.getProperty(PROJECT_NAME); - String projectVersion = (String) e.getProperty(PROJECT_VERSION); - List<Long> lineCoverage; - if (e.hasProperty(LINE_COVERAGE)) { - lineCoverage = (List<Long>) e.getProperty(LINE_COVERAGE); - } else { - lineCoverage = new ArrayList<>(); - } - return new CoverageEntity(e.getKey().getParent(), group, coveredLineCount, - totalLineCount, filePath, projectName, projectVersion, lineCoverage); - } catch (ClassCastException exception) { - // Invalid contents or null values - logger.log(Level.WARNING, "Error parsing coverage entity.", exception); + + /* Comparator for sorting the list by isIgnored field */ + public static Comparator<CoverageEntity> isIgnoredComparator = + new Comparator<CoverageEntity>() { + + public int compare(CoverageEntity coverageEntity1, CoverageEntity coverageEntity2) { + Boolean isIgnored1 = + Objects.isNull(coverageEntity1.getIsIgnored()) + ? false + : coverageEntity1.getIsIgnored(); + Boolean isIgnored2 = + Objects.isNull(coverageEntity2.getIsIgnored()) + ? false + : coverageEntity2.getIsIgnored(); + + // ascending order + return isIgnored1.compareTo(isIgnored2); + } + }; + + public Entity toEntity() { + Entity coverageEntity = new Entity(KIND, parentKey); + coverageEntity.setProperty(GROUP, group); + coverageEntity.setUnindexedProperty(COVERED_LINE_COUNT, coveredCount); + coverageEntity.setUnindexedProperty(TOTAL_LINE_COUNT, totalCount); + coverageEntity.setProperty(FILE_PATH, filePath); + coverageEntity.setUnindexedProperty(PROJECT_NAME, projectName); + coverageEntity.setUnindexedProperty(PROJECT_VERSION, projectVersion); + if (lineCoverage != null && lineCoverage.size() > 0) { + coverageEntity.setUnindexedProperty(LINE_COVERAGE, lineCoverage); + } + return coverageEntity; } - return null; - } - - /** - * Convert a coverage report to a CoverageEntity. - * - * @param parentKey The ancestor key for the coverage entity. - * @param group The group to display the coverage report with. - * @param coverage The coverage report containing coverage data. - * @return The CoverageEntity for the coverage report message, or null if not compatible. - */ - public static CoverageEntity fromCoverageReport( - Key parentKey, String group, CoverageReportMessage coverage) { - if (!coverage.hasFilePath() || !coverage.hasProjectName() || !coverage.hasRevision() - || !coverage.hasTotalLineCount() || !coverage.hasCoveredLineCount()) { - return null; // invalid coverage report; + + /** + * Convert an Entity object to a CoverageEntity. + * + * @param e The entity to process. + * @return CoverageEntity object with the properties from e, or null if incompatible. + */ + @SuppressWarnings("unchecked") + public static CoverageEntity fromEntity(Entity e) { + if (!e.getKind().equals(KIND) + || !e.hasProperty(GROUP) + || !e.hasProperty(COVERED_LINE_COUNT) + || !e.hasProperty(TOTAL_LINE_COUNT) + || !e.hasProperty(FILE_PATH) + || !e.hasProperty(PROJECT_NAME) + || !e.hasProperty(PROJECT_VERSION)) { + logger.log(Level.WARNING, "Missing coverage attributes in entity: " + e.toString()); + return null; + } + try { + String group = (String) e.getProperty(GROUP); + long coveredLineCount = (long) e.getProperty(COVERED_LINE_COUNT); + long totalLineCount = (long) e.getProperty(TOTAL_LINE_COUNT); + String filePath = (String) e.getProperty(FILE_PATH); + String projectName = (String) e.getProperty(PROJECT_NAME); + String projectVersion = (String) e.getProperty(PROJECT_VERSION); + List<Long> lineCoverage; + if (e.hasProperty(LINE_COVERAGE)) { + lineCoverage = (List<Long>) e.getProperty(LINE_COVERAGE); + } else { + lineCoverage = new ArrayList<>(); + } + return new CoverageEntity( + e.getKey().getParent(), + group, + coveredLineCount, + totalLineCount, + filePath, + projectName, + projectVersion, + lineCoverage); + } catch (ClassCastException exception) { + // Invalid contents or null values + logger.log(Level.WARNING, "Error parsing coverage entity.", exception); + } + return null; } - long coveredLineCount = coverage.getCoveredLineCount(); - long totalLineCount = coverage.getTotalLineCount(); - String filePath = coverage.getFilePath().toStringUtf8(); - String projectName = coverage.getProjectName().toStringUtf8(); - String projectVersion = coverage.getRevision().toStringUtf8(); - List<Long> lineCoverage = null; - if (coverage.getLineCoverageVectorCount() > 0) { - lineCoverage = new ArrayList<>(); - for (long count : coverage.getLineCoverageVectorList()) { - lineCoverage.add(count); - } + + /** + * Convert a coverage report to a CoverageEntity. + * + * @param parentKey The ancestor key for the coverage entity. + * @param group The group to display the coverage report with. + * @param coverage The coverage report containing coverage data. + * @return The CoverageEntity for the coverage report message, or null if not compatible. + */ + public static CoverageEntity fromCoverageReport( + com.googlecode.objectify.Key parentKey, String group, CoverageReportMessage coverage) { + if (!coverage.hasFilePath() + || !coverage.hasProjectName() + || !coverage.hasRevision() + || !coverage.hasTotalLineCount() + || !coverage.hasCoveredLineCount()) { + return null; // invalid coverage report; + } + long coveredLineCount = coverage.getCoveredLineCount(); + long totalLineCount = coverage.getTotalLineCount(); + String filePath = coverage.getFilePath().toStringUtf8(); + String projectName = coverage.getProjectName().toStringUtf8(); + String projectVersion = coverage.getRevision().toStringUtf8(); + List<Long> lineCoverage = null; + if (coverage.getLineCoverageVectorCount() > 0) { + lineCoverage = new ArrayList<>(); + for (long count : coverage.getLineCoverageVectorList()) { + lineCoverage.add(count); + } + } + return new CoverageEntity( + parentKey, + group, + coveredLineCount, + totalLineCount, + filePath, + projectName, + projectVersion, + lineCoverage); } - return new CoverageEntity(parentKey, group, coveredLineCount, totalLineCount, filePath, - projectName, projectVersion, lineCoverage); - } } diff --git a/src/main/java/com/android/vts/entity/DashboardEntity.java b/src/main/java/com/android/vts/entity/DashboardEntity.java index 402a1e5..a42c1eb 100644 --- a/src/main/java/com/android/vts/entity/DashboardEntity.java +++ b/src/main/java/com/android/vts/entity/DashboardEntity.java @@ -16,14 +16,41 @@ package com.android.vts.entity; -import com.google.appengine.api.datastore.Entity; +import com.google.common.collect.Lists; +import com.googlecode.objectify.Key; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.googlecode.objectify.ObjectifyService.ofy; /** Interface for interacting with VTS Dashboard entities in Cloud Datastore. */ -public interface DashboardEntity { +public interface DashboardEntity extends Serializable { /** - * Serialize the DashboardEntity to an Entity object. + * Save the Entity to the datastore. * - * @return Entity object representing the properties defined in the DashboardEntity. + * @return The saved entity's key value. */ - public Entity toEntity(); + <T> Key<T> save(); + + /** Save List of entity through objectify entities method. */ + static <T> Map<Key<T>, T> saveAll(List<T> entityList, int maxEntitySize) { + return ofy().transact( + () -> { + List<List<T>> partitionedList = + Lists.partition(entityList, maxEntitySize); + return partitionedList + .stream() + .map( + subEntityList -> + ofy().save().entities(subEntityList).now()) + .flatMap(m -> m.entrySet().stream()) + .collect( + Collectors.toMap( + entry -> entry.getKey(), + entry -> entry.getValue())); + }); + } } diff --git a/src/main/java/com/android/vts/entity/DeviceInfoEntity.java b/src/main/java/com/android/vts/entity/DeviceInfoEntity.java index d0c3d00..2b98355 100644 --- a/src/main/java/com/android/vts/entity/DeviceInfoEntity.java +++ b/src/main/java/com/android/vts/entity/DeviceInfoEntity.java @@ -27,7 +27,6 @@ import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Parent; -import java.io.Serializable; import java.util.List; import java.util.Objects; import java.util.logging.Level; @@ -44,7 +43,7 @@ import static com.googlecode.objectify.ObjectifyService.ofy; @Data @NoArgsConstructor /** Class describing a device used for a test run. */ -public class DeviceInfoEntity implements Serializable { +public class DeviceInfoEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(DeviceInfoEntity.class.getName()); /** This is the instance of App Engine memcache service java library */ @@ -64,8 +63,7 @@ public class DeviceInfoEntity implements Serializable { private Key parentKey; /** ID field using start timestamp */ - @Id - private long id; + @Id private Long id; /** parent field based on Test and TestRun key */ @Parent @@ -87,15 +85,6 @@ public class DeviceInfoEntity implements Serializable { private String abiName; - /* - public final String branch; - public final String product; - public final String buildFlavor; - public final String buildId; - public final String abiBitness; - public final String abiName; - */ - /** * Create a DeviceInfoEntity object. * @@ -215,8 +204,8 @@ public class DeviceInfoEntity implements Serializable { } /** Saving function for the instance of this class */ - public void save() { - ofy().save().entity(this).now(); + public com.googlecode.objectify.Key<DeviceInfoEntity> save() { + return ofy().save().entity(this).now(); } public Entity toEntity() { @@ -270,12 +259,12 @@ public class DeviceInfoEntity implements Serializable { /** * Convert a device info message to a DeviceInfoEntity. * - * @param parentKey The ancestor key for the device entity. + * @param parent The ancestor key for the device entity. * @param device The device info report describing the target Android device. * @return The DeviceInfoEntity for the target device, or null if incompatible */ public static DeviceInfoEntity fromDeviceInfoMessage( - Key parentKey, AndroidDeviceInfoMessage device) { + com.googlecode.objectify.Key parent, AndroidDeviceInfoMessage device) { if (!device.hasBuildAlias() || !device.hasBuildFlavor() || !device.hasProductVariant() || !device.hasBuildId()) { return null; @@ -287,7 +276,7 @@ public class DeviceInfoEntity implements Serializable { String abiBitness = device.getAbiBitness().toStringUtf8(); String abiName = device.getAbiName().toStringUtf8(); return new DeviceInfoEntity( - parentKey, branch, product, buildFlavor, buildId, abiBitness, abiName); + parent, branch, product, buildFlavor, buildId, abiBitness, abiName); } @Override @@ -312,10 +301,11 @@ public class DeviceInfoEntity implements Serializable { /** * Create a copy of the device info under a near parent. + * * @param parentKey The new parent key. * @return A copy of the DeviceInfoEntity with the specified parent. */ - public DeviceInfoEntity copyWithParent(Key parentKey) { + public DeviceInfoEntity copyWithParent(com.googlecode.objectify.Key parentKey) { return new DeviceInfoEntity(parentKey, this.branch, this.product, this.buildFlavor, this.buildId, this.abiBitness, this.abiName); } diff --git a/src/main/java/com/android/vts/entity/HalApiEntity.java b/src/main/java/com/android/vts/entity/HalApiEntity.java new file mode 100644 index 0000000..158a4ca --- /dev/null +++ b/src/main/java/com/android/vts/entity/HalApiEntity.java @@ -0,0 +1,105 @@ +/* + * 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.entity; + +import static com.googlecode.objectify.ObjectifyService.ofy; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.googlecode.objectify.Key; +import com.googlecode.objectify.annotation.Cache; +import com.googlecode.objectify.annotation.Entity; +import com.googlecode.objectify.annotation.Id; +import com.googlecode.objectify.annotation.Index; +import com.googlecode.objectify.annotation.Parent; +import java.util.Date; +import java.util.List; +import java.util.UUID; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** Entity Class for HalApiEntity */ +@Cache +@Entity(name = "HalApiEntity") +@EqualsAndHashCode(of = "id") +@NoArgsConstructor +@JsonAutoDetect(fieldVisibility = Visibility.ANY) +@JsonIgnoreProperties({"id", "parent"}) +public class HalApiEntity implements DashboardEntity { + + /** HalApiEntity id field */ + @Id @Getter @Setter String id; + + @Parent @Getter Key<?> parent; + + /** HAL Api Release Level. e.g. */ + @Index @Getter @Setter String halApiReleaseLevel; + + /** HAL package name. e.g. android.hardware.foo. */ + @Index @Getter @Setter String halPackageName; + + /** HAL (major) version. e.g. 1. */ + @Index @Getter @Setter int halVersionMajor; + + /** HAL (minor) version. e.g. 0. */ + @Index @Getter @Setter int halVersionMinor; + + /** HAL interface name. e.g. IFoo. */ + @Index @Getter @Setter String halInterfaceName; + + /** List of HAL API */ + @Getter @Setter List<String> halApi; + + /** List of HAL covered API */ + @Getter @Setter List<String> coveredHalApi; + + /** When this record was created or updated */ + @Index Date updated; + + /** Constructor function for HalApiEntity Class */ + public HalApiEntity( + com.googlecode.objectify.Key testRunKey, + String halApiReleaseLevel, + String halPackageName, + int halVersionMajor, + int halVersionMinor, + String halInterfaceName, + List<String> halApi, + List<String> coveredHalApi) { + + this.id = UUID.randomUUID().toString(); + this.parent = testRunKey; + + this.halApiReleaseLevel = halApiReleaseLevel; + this.halPackageName = halPackageName; + this.halVersionMajor = halVersionMajor; + this.halVersionMinor = halVersionMinor; + this.halInterfaceName = halInterfaceName; + this.halApi = halApi; + this.coveredHalApi = coveredHalApi; + this.updated = new Date(); + } + + /** Saving function for the instance of this class */ + @Override + public Key<HalApiEntity> save() { + return ofy().save().entity(this).now(); + } +} diff --git a/src/main/java/com/android/vts/entity/ProfilingPointEntity.java b/src/main/java/com/android/vts/entity/ProfilingPointEntity.java index 741a5a7..ac2994c 100644 --- a/src/main/java/com/android/vts/entity/ProfilingPointEntity.java +++ b/src/main/java/com/android/vts/entity/ProfilingPointEntity.java @@ -25,21 +25,20 @@ import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Index; -import java.io.Serializable; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import lombok.Data; -import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; + +import static com.googlecode.objectify.ObjectifyService.ofy; @com.googlecode.objectify.annotation.Entity(name = "ProfilingPoint") @Cache @Data @NoArgsConstructor /** Entity describing a profiling point. */ -public class ProfilingPointEntity implements Serializable { +public class ProfilingPointEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(ProfilingPointEntity.class.getName()); protected static final String DELIMITER = "#"; @@ -141,6 +140,12 @@ public class ProfilingPointEntity implements Serializable { return KeyFactory.createKey(KIND, testName + DELIMITER + profilingPointName); } + /** Saving function for the instance of this class */ + @Override + public com.googlecode.objectify.Key<ProfilingPointEntity> save() { + return ofy().save().entity(this).now(); + } + public Entity toEntity() { Entity profilingPoint = new Entity(key); profilingPoint.setIndexedProperty(TEST_NAME, this.testName); diff --git a/src/main/java/com/android/vts/entity/ProfilingPointRunEntity.java b/src/main/java/com/android/vts/entity/ProfilingPointRunEntity.java index 2010971..48df727 100644 --- a/src/main/java/com/android/vts/entity/ProfilingPointRunEntity.java +++ b/src/main/java/com/android/vts/entity/ProfilingPointRunEntity.java @@ -27,22 +27,21 @@ import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Parent; -import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import lombok.Data; -import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; + +import static com.googlecode.objectify.ObjectifyService.ofy; @com.googlecode.objectify.annotation.Entity(name = "ProfilingPointRun") @Cache @Data @NoArgsConstructor /** Entity describing a profiling point execution. */ -public class ProfilingPointRunEntity implements Serializable { +public class ProfilingPointRunEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(ProfilingPointRunEntity.class.getName()); @@ -94,7 +93,7 @@ public class ProfilingPointRunEntity implements Serializable { /** * Create a ProfilingPointRunEntity object. * - * @param parentKey The Key object for the parent TestRunEntity in the database. + * @param parentKey The Key object for the parent TestRunEntity in datastore. * @param name The name of the profiling point. * @param type The (number) type of the profiling point data. * @param regressionMode The (number) mode to use for detecting regression. @@ -125,6 +124,41 @@ public class ProfilingPointRunEntity implements Serializable { this.options = options; } + + /** + * Create a ProfilingPointRunEntity object. + * + * @param parent The objectify Key for the parent TestRunEntity in datastore. + * @param name The name of the profiling point. + * @param type The (number) type of the profiling point data. + * @param regressionMode The (number) mode to use for detecting regression. + * @param labels List of data labels, or null if the data is unlabeled. + * @param values List of data values. + * @param xLabel The x axis label. + * @param yLabel The y axis label. + * @param options The list of key=value options for the profiling point run. + */ + public ProfilingPointRunEntity( + com.googlecode.objectify.Key parent, + String name, + int type, + int regressionMode, + List<String> labels, + List<Long> values, + String xLabel, + String yLabel, + List<String> options) { + this.parent = parent; + this.name = name; + this.type = type; + this.regressionMode = regressionMode; + this.labels = labels == null ? null : new ArrayList<>(labels); + this.values = new ArrayList<>(values); + this.xLabel = xLabel; + this.yLabel = yLabel; + this.options = options; + } + /** * Get VtsProfilingType from int value. * @@ -143,6 +177,12 @@ public class ProfilingPointRunEntity implements Serializable { return VtsProfilingRegressionMode.forNumber(regressionMode); } + /** Saving function for the instance of this class */ + @Override + public com.googlecode.objectify.Key<ProfilingPointRunEntity> save() { + return ofy().save().entity(this).now(); + } + public Entity toEntity() { Entity profilingRun = new Entity(this.key); profilingRun.setUnindexedProperty(TYPE, this.type); @@ -207,12 +247,12 @@ public class ProfilingPointRunEntity implements Serializable { /** * Convert a coverage report to a CoverageEntity. * - * @param parentKey The ancestor key for the coverage entity. + * @param parent The ancestor objectify key for the coverage entity. * @param profilingReport The profiling report containing profiling data. * @return The ProfilingPointRunEntity for the profiling report message, or null if incompatible */ public static ProfilingPointRunEntity fromProfilingReport( - Key parentKey, ProfilingReportMessage profilingReport) { + com.googlecode.objectify.Key parent, ProfilingReportMessage profilingReport) { if (!profilingReport.hasName() || !profilingReport.hasType() || profilingReport.getType() == VtsProfilingType.UNKNOWN_VTS_PROFILING_TYPE @@ -265,7 +305,7 @@ public class ProfilingPointRunEntity implements Serializable { } } return new ProfilingPointRunEntity( - parentKey, + parent, name, type.getNumber(), regressionMode.getNumber(), diff --git a/src/main/java/com/android/vts/entity/ProfilingPointSummaryEntity.java b/src/main/java/com/android/vts/entity/ProfilingPointSummaryEntity.java index f426707..e4a1911 100644 --- a/src/main/java/com/android/vts/entity/ProfilingPointSummaryEntity.java +++ b/src/main/java/com/android/vts/entity/ProfilingPointSummaryEntity.java @@ -25,7 +25,6 @@ import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Index; -import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -33,16 +32,16 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import lombok.Data; -import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; + +import static com.googlecode.objectify.ObjectifyService.ofy; @com.googlecode.objectify.annotation.Entity(name = "ProfilingPointSummary") @Cache @Data @NoArgsConstructor /** Entity describing a profiling point summary. */ -public class ProfilingPointSummaryEntity implements Serializable { +public class ProfilingPointSummaryEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(ProfilingPointSummaryEntity.class.getName()); protected static final String DELIMITER = "#"; @@ -237,6 +236,12 @@ public class ProfilingPointSummaryEntity implements Serializable { } } + /** Saving function for the instance of this class */ + @Override + public com.googlecode.objectify.Key<ProfilingPointSummaryEntity> save() { + return ofy().save().entity(this).now(); + } + public Entity toEntity() { Entity profilingSummary; profilingSummary = new Entity(this.key); diff --git a/src/main/java/com/android/vts/entity/RoleEntity.java b/src/main/java/com/android/vts/entity/RoleEntity.java index d001cfa..508a9ca 100644 --- a/src/main/java/com/android/vts/entity/RoleEntity.java +++ b/src/main/java/com/android/vts/entity/RoleEntity.java @@ -6,49 +6,38 @@ import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Id; -import com.googlecode.objectify.annotation.Index; -import com.googlecode.objectify.annotation.Load; -import java.io.Serializable; -import java.util.ArrayList; import java.util.Date; -import java.util.List; -import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; - @Cache @Entity @EqualsAndHashCode(of = "role") @NoArgsConstructor -public class RoleEntity implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id - private String role; - - /** When this record was created or updated */ - @Getter - Date updated; - - /** Construction function for UserEntity Class */ - public RoleEntity(String roleName) { - this.role = roleName; - } - - /** Get role by email */ - public static RoleEntity getRole(String role) { - return ofy().load() - .type(RoleEntity.class) - .id(role) - .now(); - } - - /** Saving function for the instance of this class */ - public void save() { - this.updated = new Date(); - ofy().save().entity(this).now(); - } -}
\ No newline at end of file +public class RoleEntity implements DashboardEntity { + + private static final long serialVersionUID = 1L; + + @Id private String role; + + /** When this record was created or updated */ + @Getter Date updated; + + /** Construction function for UserEntity Class */ + public RoleEntity(String roleName) { + this.role = roleName; + } + + /** Get role by email */ + public static RoleEntity getRole(String role) { + return ofy().load().type(RoleEntity.class).id(role).now(); + } + + /** Saving function for the instance of this class */ + @Override + public Key<RoleEntity> save() { + this.updated = new Date(); + return ofy().save().entity(this).now(); + } +} diff --git a/src/main/java/com/android/vts/entity/TestAcknowledgmentEntity.java b/src/main/java/com/android/vts/entity/TestAcknowledgmentEntity.java index 66b56b8..a594f85 100644 --- a/src/main/java/com/android/vts/entity/TestAcknowledgmentEntity.java +++ b/src/main/java/com/android/vts/entity/TestAcknowledgmentEntity.java @@ -26,6 +26,12 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.google.gson.reflect.TypeToken; +import com.googlecode.objectify.annotation.Cache; +import com.googlecode.objectify.annotation.Id; +import com.googlecode.objectify.annotation.Ignore; +import com.googlecode.objectify.annotation.Index; +import lombok.Data; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashSet; @@ -35,6 +41,11 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import static com.googlecode.objectify.ObjectifyService.ofy; + +@com.googlecode.objectify.annotation.Entity(name = "TestAcknowledgment") +@Cache +@Data /** Entity describing a test failure acknowledgment. */ public class TestAcknowledgmentEntity implements DashboardEntity { protected static final Logger logger = @@ -44,21 +55,27 @@ public class TestAcknowledgmentEntity implements DashboardEntity { public static final String KEY = "key"; public static final String TEST_KEY = "testKey"; public static final String TEST_NAME = "testName"; - public static final String USER = "user"; + public static final String USER_OBJ = "userObj"; public static final String CREATED = "created"; public static final String BRANCHES = "branches"; public static final String DEVICES = "devices"; public static final String TEST_CASE_NAMES = "testCaseNames"; public static final String NOTE = "note"; - private final Key key; - private final long created; - public final Key test; - public final User user; - public final Set<String> branches; - public final Set<String> devices; - public final Set<String> testCaseNames; - public final String note; + @Ignore private final Key key; + @Ignore public final Key test; + @Ignore public final User userObj; + + @Id private Long id; + + private com.googlecode.objectify.Key testKey; + private Set<String> branches; + private Set<String> devices; + private Set<String> testCaseNames; + private String note; + private String user; + + @Index private final long created; /** * Create a AcknowledgmentEntity object. @@ -66,11 +83,11 @@ public class TestAcknowledgmentEntity implements DashboardEntity { * @param key The key of the AcknowledgmentEntity in the database. * @param created The timestamp when the entity was created (in microseconds). * @param test The key of the test. - * @param user The user who created or last modified the entity. + * @param userObj The user who created or last modified the entity. * @param branches The list of branch names for which the acknowledgment applies (or null if * all). - * @param devices The list of device build flavors for which the acknowledgment applies (or - * null if all). + * @param devices The list of device build flavors for which the acknowledgment applies (or null + * if all). * @param testCaseNames The list of test case names known to fail (or null if all). * @param note A text blob with details about the failure (or null if all). */ @@ -78,13 +95,13 @@ public class TestAcknowledgmentEntity implements DashboardEntity { Key key, long created, Key test, - User user, + User userObj, List<String> branches, List<String> devices, List<String> testCaseNames, Text note) { this.test = test; - this.user = user; + this.userObj = userObj; if (branches != null) this.branches = new HashSet(branches); else this.branches = new HashSet<>(); @@ -105,32 +122,37 @@ public class TestAcknowledgmentEntity implements DashboardEntity { * Create a AcknowledgmentEntity object. * * @param test The key of the test. - * @param user The user who created or last modified the entity. + * @param userObj The user who created or last modified the entity. * @param branches The list of branch names for which the acknowledgment applies (or null if * all). - * @param devices The list of device build flavors for which the acknowledgment applies (or - * null if all). + * @param devices The list of device build flavors for which the acknowledgment applies (or null + * if all). * @param testCaseNames The list of test case names known to fail (or null if all). * @param note A text blob with details about the failure (or null if all). */ public TestAcknowledgmentEntity( Key test, - User user, + User userObj, List<String> branches, List<String> devices, List<String> testCaseNames, Text note) { - this(null, -1, test, user, branches, devices, testCaseNames, note); + this(null, -1, test, userObj, branches, devices, testCaseNames, note); } + /** Saving function for the instance of this class */ @Override + public com.googlecode.objectify.Key<TestAcknowledgmentEntity> save() { + return ofy().save().entity(this).now(); + } + public Entity toEntity() { Entity ackEntity; if (this.key == null) ackEntity = new Entity(KIND); else ackEntity = new Entity(key); ackEntity.setProperty(TEST_KEY, this.test); - ackEntity.setProperty(USER, this.user); + ackEntity.setProperty(USER_OBJ, this.userObj); long created = this.created; if (created < 0) created = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); @@ -159,7 +181,7 @@ public class TestAcknowledgmentEntity implements DashboardEntity { public static TestAcknowledgmentEntity fromEntity(Entity e) { if (!e.getKind().equals(KIND) || !e.hasProperty(TEST_KEY) - || !e.hasProperty(USER) + || !e.hasProperty(USER_OBJ) || !e.hasProperty(CREATED)) { logger.log( Level.WARNING, "Missing attributes in acknowledgment entity: " + e.toString()); @@ -167,7 +189,7 @@ public class TestAcknowledgmentEntity implements DashboardEntity { } try { Key test = (Key) e.getProperty(TEST_KEY); - User user = (User) e.getProperty(USER); + User user = (User) e.getProperty(USER_OBJ); long created = (long) e.getProperty(CREATED); List<String> branches; @@ -247,7 +269,7 @@ public class TestAcknowledgmentEntity implements DashboardEntity { JsonObject json = new JsonObject(); json.add(KEY, new JsonPrimitive(KeyFactory.keyToString(this.key))); json.add(TEST_NAME, new JsonPrimitive(this.test.getName())); - json.add(USER, new JsonPrimitive(this.user.getEmail())); + json.add(USER_OBJ, new JsonPrimitive(this.userObj.getEmail())); json.add(CREATED, new JsonPrimitive(this.created)); List<JsonElement> branches = new ArrayList<>(); diff --git a/src/main/java/com/android/vts/entity/TestCaseRunEntity.java b/src/main/java/com/android/vts/entity/TestCaseRunEntity.java index 6d3ba83..30776d4 100644 --- a/src/main/java/com/android/vts/entity/TestCaseRunEntity.java +++ b/src/main/java/com/android/vts/entity/TestCaseRunEntity.java @@ -24,17 +24,17 @@ import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.OnLoad; import java.util.ArrayList; import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import static com.googlecode.objectify.ObjectifyService.ofy; @com.googlecode.objectify.annotation.Entity(name = "TestCaseRun") @Cache @Data +@Slf4j /** Entity describing the execution of a test case. */ public class TestCaseRunEntity implements DashboardEntity { - protected static final Logger logger = Logger.getLogger(TestCaseRunEntity.class.getName()); public static final String KIND = "TestCaseRun"; @@ -89,7 +89,8 @@ public class TestCaseRunEntity implements DashboardEntity { * Create a TestCaseRunEntity. */ public TestCaseRunEntity() { - this.id = -1L; + this.results = new ArrayList<>(); + this.testCaseNames = new ArrayList<>(); this.testCases = new ArrayList<>(); this.systraceUrl = null; } @@ -100,6 +101,8 @@ public class TestCaseRunEntity implements DashboardEntity { */ public TestCaseRunEntity(long id) { this.id = id; + this.results = new ArrayList<>(); + this.testCaseNames = new ArrayList<>(); this.testCases = new ArrayList<>(); this.systraceUrl = null; } @@ -147,13 +150,21 @@ public class TestCaseRunEntity implements DashboardEntity { * @return true if added, false otherwise. */ public boolean addTestCase(String name, int result) { - if (isFull()) + if (this.isFull()) { return false; - this.testCases.add(new TestCase(this.id, this.testCases.size(), name, result)); - return true; + } else { + this.testCaseNames.add(name); + this.results.add(result); + return true; + } } + /** Saving function for the instance of this class */ @Override + public com.googlecode.objectify.Key<TestCaseRunEntity> save() { + return ofy().save().entity(this).now(); + } + public Entity toEntity() { Entity testCaseRunEntity; if (this.id >= 0) { @@ -188,7 +199,7 @@ public class TestCaseRunEntity implements DashboardEntity { @SuppressWarnings("unchecked") public static TestCaseRunEntity fromEntity(Entity e) { if (!e.getKind().equals(KIND)) { - logger.log(Level.WARNING, "Wrong kind: " + e.getKey()); + log.warn("Wrong kind: " + e.getKey()); return null; } try { @@ -213,7 +224,7 @@ public class TestCaseRunEntity implements DashboardEntity { return testCaseRun; } catch (ClassCastException exception) { // Invalid cast - logger.log(Level.WARNING, "Error parsing test case run entity.", exception); + log.warn("Error parsing test case run entity.", exception); } return null; } diff --git a/src/main/java/com/android/vts/entity/TestCoverageStatusEntity.java b/src/main/java/com/android/vts/entity/TestCoverageStatusEntity.java index cc630c3..1156cc7 100644 --- a/src/main/java/com/android/vts/entity/TestCoverageStatusEntity.java +++ b/src/main/java/com/android/vts/entity/TestCoverageStatusEntity.java @@ -23,7 +23,6 @@ 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; @@ -42,7 +41,7 @@ import lombok.Setter; @Cache @NoArgsConstructor /** Entity describing test coverage status. */ -public class TestCoverageStatusEntity implements Serializable { +public class TestCoverageStatusEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(TestCoverageStatusEntity.class.getName()); @@ -190,9 +189,10 @@ public class TestCoverageStatusEntity implements Serializable { } /** Saving function for the instance of this class */ - public void save() { + @Override + public Key<TestCoverageStatusEntity> save() { this.updatedDate = new Date(); - ofy().save().entity(this).now(); + return ofy().save().entity(this).now(); } public Entity toEntity() { diff --git a/src/main/java/com/android/vts/entity/TestEntity.java b/src/main/java/com/android/vts/entity/TestEntity.java index e48d759..4df7fb0 100644 --- a/src/main/java/com/android/vts/entity/TestEntity.java +++ b/src/main/java/com/android/vts/entity/TestEntity.java @@ -24,7 +24,6 @@ import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Index; -import java.io.Serializable; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -34,12 +33,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -@Entity(name="Test") +@Entity(name = "Test") @Cache @Data @NoArgsConstructor /** Entity describing test metadata. */ -public class TestEntity implements Serializable { +public class TestEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(TestEntity.class.getName()); public static final String KIND = "Test"; @@ -75,6 +74,12 @@ public class TestEntity implements Serializable { this(testName, false); } + /** Saving function for the instance of this class */ + @Override + public com.googlecode.objectify.Key<TestEntity> save() { + return ofy().save().entity(this).now(); + } + public com.google.appengine.api.datastore.Entity toEntity() { com.google.appengine.api.datastore.Entity testEntity = new com.google.appengine.api.datastore.Entity(this.getOldKey()); testEntity.setProperty(HAS_PROFILING_DATA, this.hasProfilingData); @@ -82,6 +87,19 @@ public class TestEntity implements Serializable { } /** + * Get objectify TestRun Entity's key. + * + * @param startTimestamp test start timestamp + */ + public com.googlecode.objectify.Key getTestRunKey(long startTimestamp) { + com.googlecode.objectify.Key testKey = + com.googlecode.objectify.Key.create(TestEntity.class, this.getTestName()); + com.googlecode.objectify.Key testRunKey = + com.googlecode.objectify.Key.create(testKey, TestRunEntity.class, startTimestamp); + return testRunKey; + } + + /** * Get key info from appengine based library. */ public Key getOldKey() { @@ -130,10 +148,4 @@ public class TestEntity implements Serializable { } return new TestEntity(testName, hasProfilingData); } - - /** Saving function for the instance of this class */ - public void save() { - ofy().save().entity(this).now(); - } - } diff --git a/src/main/java/com/android/vts/entity/TestPlanEntity.java b/src/main/java/com/android/vts/entity/TestPlanEntity.java index 805c3cd..590db46 100644 --- a/src/main/java/com/android/vts/entity/TestPlanEntity.java +++ b/src/main/java/com/android/vts/entity/TestPlanEntity.java @@ -19,21 +19,20 @@ 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 java.io.Serializable; -import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import lombok.Data; import lombok.NoArgsConstructor; -@com.googlecode.objectify.annotation.Entity(name="TestPlan") +@com.googlecode.objectify.annotation.Entity(name = "TestPlan") @Cache @Data @NoArgsConstructor /** Entity describing test plan metadata. */ -public class TestPlanEntity implements Serializable { +public class TestPlanEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(TestPlanEntity.class.getName()); public static final String KIND = "TestPlan"; @@ -58,6 +57,11 @@ public class TestPlanEntity implements Serializable { return planEntity; } + public Key getKey() { + Key key = Key.create(TestPlanEntity.class, this.testPlanName); + return key; + } + /** * Convert an Entity object to a TestEntity. * @@ -76,7 +80,8 @@ public class TestPlanEntity implements Serializable { } /** Saving function for the instance of this class */ - public void save() { - ofy().save().entity(this).now(); + @Override + public com.googlecode.objectify.Key<TestPlanEntity> save() { + return ofy().save().entity(this).now(); } } diff --git a/src/main/java/com/android/vts/entity/TestPlanRunEntity.java b/src/main/java/com/android/vts/entity/TestPlanRunEntity.java index 7dbb0dc..de2e505 100644 --- a/src/main/java/com/android/vts/entity/TestPlanRunEntity.java +++ b/src/main/java/com/android/vts/entity/TestPlanRunEntity.java @@ -18,7 +18,6 @@ package com.android.vts.entity; import static com.googlecode.objectify.ObjectifyService.ofy; -import com.android.vts.entity.TestRunEntity.TestRunType; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; @@ -32,25 +31,21 @@ import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Parent; -import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.Objects; -import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import lombok.Data; -import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; @com.googlecode.objectify.annotation.Entity(name = "TestPlanRun") @Cache @Data @NoArgsConstructor /** Entity describing test plan run information. */ -public class TestPlanRunEntity implements Serializable { +public class TestPlanRunEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(TestPlanRunEntity.class.getName()); @@ -76,7 +71,7 @@ public class TestPlanRunEntity implements Serializable { @Id private Long id; - @Parent private com.googlecode.objectify.Key<TestPlanEntity> testParent; + @Parent private com.googlecode.objectify.Key<TestPlanEntity> parent; @Index private String testPlanName; @@ -98,7 +93,7 @@ public class TestPlanRunEntity implements Serializable { @Ignore private List<Key> oldTestRuns; - private List<com.googlecode.objectify.Key<?>> testRuns; + private List<com.googlecode.objectify.Key<TestRunEntity>> testRuns; /** When this record was created or updated */ @Index Date updated; @@ -106,7 +101,7 @@ public class TestPlanRunEntity implements Serializable { /** * Create a TestPlanRunEntity object describing a test plan run. * - * @param parentKey The key for the parent entity in the database. + * @param testPlanKey The key for the parent entity in the database. * @param type The test run type (e.g. presubmit, postsubmit, other) * @param startTimestamp The time in microseconds when the test plan run started. * @param endTimestamp The time in microseconds when the test plan run ended. @@ -116,7 +111,7 @@ public class TestPlanRunEntity implements Serializable { * @param testRuns A list of keys to the TestRunEntity objects for the plan run run. */ public TestPlanRunEntity( - Key parentKey, + Key testPlanKey, String testPlanName, long type, long startTimestamp, @@ -127,7 +122,8 @@ public class TestPlanRunEntity implements Serializable { long totalApiCount, long coveredApiCount, List<Key> testRuns) { - this.key = KeyFactory.createKey(parentKey, KIND, startTimestamp); + this.id = startTimestamp; + this.key = KeyFactory.createKey(testPlanKey, KIND, startTimestamp); this.testPlanName = testPlanName; this.type = type; this.startTimestamp = startTimestamp; @@ -152,6 +148,44 @@ public class TestPlanRunEntity implements Serializable { .collect(Collectors.toList()); } + /** + * Create a TestPlanRunEntity object describing a test plan run. + * + * @param testPlanKey The key for the parent entity in the database. + * @param type The test run type (e.g. presubmit, postsubmit, other) + * @param startTimestamp The time in microseconds when the test plan run started. + * @param endTimestamp The time in microseconds when the test plan run ended. + * @param testBuildId The build ID of the VTS test build. + * @param passCount The number of passing test cases in the run. + * @param failCount The number of failing test cases in the run. + * @param testRuns A list of keys to the TestRunEntity objects for the plan run run. + */ + public TestPlanRunEntity( + com.googlecode.objectify.Key<TestPlanEntity> testPlanKey, + String testPlanName, + long type, + long startTimestamp, + long endTimestamp, + String testBuildId, + long passCount, + long failCount, + long totalApiCount, + long coveredApiCount, + List<com.googlecode.objectify.Key<TestRunEntity>> testRuns) { + this.id = startTimestamp; + this.parent = testPlanKey; + this.testPlanName = testPlanName; + this.type = type; + this.startTimestamp = startTimestamp; + this.endTimestamp = endTimestamp; + this.testBuildId = testBuildId; + this.passCount = passCount; + this.failCount = failCount; + this.totalApiCount = totalApiCount; + this.coveredApiCount = coveredApiCount; + this.testRuns = testRuns; + } + public Entity toEntity() { Entity planRun = new Entity(this.key); planRun.setProperty(TEST_PLAN_NAME, this.testPlanName); @@ -168,6 +202,7 @@ public class TestPlanRunEntity implements Serializable { } /** Saving function for the instance of this class */ + @Override public com.googlecode.objectify.Key<TestPlanRunEntity> save() { this.updated = new Date(); return ofy().save().entity(this).now(); @@ -175,12 +210,7 @@ public class TestPlanRunEntity implements Serializable { /** Get UrlSafeKey from this class */ public String getUrlSafeKey() { - com.googlecode.objectify.Key testPlanKey = - com.googlecode.objectify.Key.create(TestPlanEntity.class, this.testPlanName); - com.googlecode.objectify.Key idKey = - com.googlecode.objectify.Key.create( - testPlanKey, TestPlanRunEntity.class, this.startTimestamp); - return idKey.toUrlSafe(); + return this.getOfyKey().toUrlSafe(); } /** Add a task to calculate the total number of coverage API */ @@ -191,7 +221,7 @@ public class TestPlanRunEntity implements Serializable { Queue queue = QueueFactory.getQueue(QUEUE_NAME); queue.add( TaskOptions.Builder.withUrl(COVERAGE_API_URL) - .param("urlSafeKey", String.valueOf(this.getUrlSafeKey())) + .param("urlSafeKey", this.getUrlSafeKey()) .method(TaskOptions.Method.POST)); } } @@ -205,6 +235,12 @@ public class TestPlanRunEntity implements Serializable { return KeyFactory.createKey(parentKey, KIND, startTimestamp); } + /** Get key info from objecitfy library. */ + public com.googlecode.objectify.Key getOfyKey() { + return com.googlecode.objectify.Key.create( + this.parent, TestPlanRunEntity.class, this.startTimestamp); + } + /** * Convert an Entity object to a TestPlanRunEntity. * diff --git a/src/main/java/com/android/vts/entity/TestRunEntity.java b/src/main/java/com/android/vts/entity/TestRunEntity.java index bdd0ac8..0d5f1ec 100644 --- a/src/main/java/com/android/vts/entity/TestRunEntity.java +++ b/src/main/java/com/android/vts/entity/TestRunEntity.java @@ -35,9 +35,7 @@ import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.OnLoad; import com.googlecode.objectify.annotation.Parent; -import java.io.Serializable; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.Objects; @@ -45,20 +43,17 @@ import java.util.Optional; import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; import java.util.stream.Stream; -import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.apache.commons.lang3.math.NumberUtils; -import org.json.JSONArray; @com.googlecode.objectify.annotation.Entity(name = "TestRun") @Cache @NoArgsConstructor /** Entity describing test run information. */ -public class TestRunEntity implements Serializable { +public class TestRunEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(TestRunEntity.class.getName()); /** Enum for classifying test run types. */ @@ -168,7 +163,7 @@ public class TestRunEntity implements Serializable { @Index @Getter @Setter private boolean hasCodeCoverage; - private com.googlecode.objectify.Key<CodeCoverageEntity> codeCoverageEntityKey; + @Ignore private com.googlecode.objectify.Key<CodeCoverageEntity> codeCoverageEntityKey; @Index @Getter @Setter private long coveredLineCount; @@ -202,6 +197,7 @@ public class TestRunEntity implements Serializable { boolean hasCodeCoverage, List<Long> testCaseIds, List<String> logLinks) { + this.id = startTimestamp; this.key = KeyFactory.createKey(parentKey, KIND, startTimestamp); this.type = type; this.startTimestamp = startTimestamp; @@ -212,9 +208,11 @@ public class TestRunEntity implements Serializable { this.failCount = failCount; this.hasCodeCoverage = hasCodeCoverage; this.testName = parentKey.getName(); - this.codeCoverageEntityKey = getCodeCoverageEntityKey(); this.testCaseIds = testCaseIds; this.logLinks = logLinks; + + this.testRunParent = com.googlecode.objectify.Key.create(TestEntity.class, testName); + this.codeCoverageEntityKey = getCodeCoverageEntityKey(); } /** @@ -247,6 +245,7 @@ public class TestRunEntity implements Serializable { } /** Saving function for the instance of this class */ + @Override public com.googlecode.objectify.Key<TestRunEntity> save() { return ofy().save().entity(this).now(); } @@ -289,7 +288,7 @@ public class TestRunEntity implements Serializable { } /** Get ApiCoverageEntity Key from the parent key */ - private com.googlecode.objectify.Key getOfyKey() { + public com.googlecode.objectify.Key getOfyKey() { com.googlecode.objectify.Key testKey = com.googlecode.objectify.Key.create( TestEntity.class, this.testName); diff --git a/src/main/java/com/android/vts/entity/TestStatusEntity.java b/src/main/java/com/android/vts/entity/TestStatusEntity.java index ec5ee36..b703c64 100644 --- a/src/main/java/com/android/vts/entity/TestStatusEntity.java +++ b/src/main/java/com/android/vts/entity/TestStatusEntity.java @@ -22,7 +22,6 @@ import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Index; -import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; @@ -30,12 +29,14 @@ import java.util.logging.Logger; import lombok.Data; import lombok.NoArgsConstructor; +import static com.googlecode.objectify.ObjectifyService.ofy; + @com.googlecode.objectify.annotation.Entity(name = "TestStatus") @Cache @Data @NoArgsConstructor /** Entity describing test status. */ -public class TestStatusEntity implements Serializable { +public class TestStatusEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(TestStatusEntity.class.getName()); public static final String KIND = "TestStatus"; @@ -125,6 +126,12 @@ public class TestStatusEntity implements Serializable { this(testName, 0, -1, -1, new ArrayList<TestCaseReference>()); } + /** Saving function for the instance of this class */ + @Override + public com.googlecode.objectify.Key<TestStatusEntity> save() { + return ofy().save().entity(this).now(); + } + public Entity toEntity() { Entity testEntity = new Entity(KIND, this.testName); if (this.updatedTimestamp >= 0 && this.passCount >= 0 && this.failCount >= 0) { diff --git a/src/main/java/com/android/vts/entity/TestSuiteFileEntity.java b/src/main/java/com/android/vts/entity/TestSuiteFileEntity.java index 2331e42..c68f2ec 100644 --- a/src/main/java/com/android/vts/entity/TestSuiteFileEntity.java +++ b/src/main/java/com/android/vts/entity/TestSuiteFileEntity.java @@ -16,6 +16,7 @@ package com.android.vts.entity; +import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Id; @@ -25,11 +26,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Date; -import java.util.List; import static com.googlecode.objectify.ObjectifyService.ofy; @@ -38,7 +37,7 @@ import static com.googlecode.objectify.ObjectifyService.ofy; @Entity @EqualsAndHashCode(of = "id") @NoArgsConstructor -public class TestSuiteFileEntity { +public class TestSuiteFileEntity implements DashboardEntity { /** Test Suite full file path field */ @Id @Getter @Setter String filePath; @@ -71,8 +70,9 @@ public class TestSuiteFileEntity { } /** Saving function for the instance of this class */ - public void save() { + @Override + public Key<TestSuiteFileEntity> save() { this.updated = new Date(); - ofy().save().entity(this).now(); + return ofy().save().entity(this).now(); } } diff --git a/src/main/java/com/android/vts/entity/TestSuiteResultEntity.java b/src/main/java/com/android/vts/entity/TestSuiteResultEntity.java index 2e12f54..95ba5c8 100644 --- a/src/main/java/com/android/vts/entity/TestSuiteResultEntity.java +++ b/src/main/java/com/android/vts/entity/TestSuiteResultEntity.java @@ -38,18 +38,13 @@ import org.apache.http.client.utils.URIUtils; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; -import javax.servlet.ServletContext; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.math.BigDecimal; -import java.math.RoundingMode; import java.net.URI; import java.net.URISyntaxException; -import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -126,7 +121,7 @@ class TestTypeIndex { @Entity @EqualsAndHashCode(of = "id") @NoArgsConstructor -public class TestSuiteResultEntity { +public class TestSuiteResultEntity implements DashboardEntity { private static final Logger logger = Logger.getLogger(TestSuiteResultEntity.class.getName()); @@ -316,6 +311,12 @@ public class TestSuiteResultEntity { } } + /** Saving function for the instance of this class */ + @Override + public Key<TestSuiteResultEntity> save() { + return ofy().save().entity(this).now(); + } + public static void setPropertyValues(Properties newSystemConfigProp) { systemConfigProp = newSystemConfigProp; bugTrackingSystemProp = getBugTrackingSystemProp(newSystemConfigProp); diff --git a/src/main/java/com/android/vts/entity/UserEntity.java b/src/main/java/com/android/vts/entity/UserEntity.java index a686efb..43b5edd 100644 --- a/src/main/java/com/android/vts/entity/UserEntity.java +++ b/src/main/java/com/android/vts/entity/UserEntity.java @@ -38,7 +38,7 @@ import static com.googlecode.objectify.ObjectifyService.ofy; @Entity @EqualsAndHashCode(of = "email") @NoArgsConstructor -public class UserEntity { +public class UserEntity implements DashboardEntity { /** User email field */ @Id @Getter @Setter String email; @@ -79,9 +79,10 @@ public class UserEntity { } /** Saving function for the instance of this class */ - public void save() { + @Override + public Key<UserEntity> save() { this.updated = new Date(); - ofy().save().entity(this).now(); + return ofy().save().entity(this).now(); } /** Get admin user list by admin email */ diff --git a/src/main/java/com/android/vts/entity/UserFavoriteEntity.java b/src/main/java/com/android/vts/entity/UserFavoriteEntity.java index 132d9b4..2548042 100644 --- a/src/main/java/com/android/vts/entity/UserFavoriteEntity.java +++ b/src/main/java/com/android/vts/entity/UserFavoriteEntity.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.users.User; +import com.googlecode.objectify.annotation.Cache; +import com.googlecode.objectify.annotation.Ignore; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + import java.util.logging.Level; import java.util.logging.Logger; +import static com.googlecode.objectify.ObjectifyService.ofy; + +@Cache +@com.googlecode.objectify.annotation.Entity(name = "UserFavorite") +@EqualsAndHashCode(of = "email") +@NoArgsConstructor /** Entity describing subscriptions between a user and a test. */ public class UserFavoriteEntity implements DashboardEntity { protected static final Logger logger = Logger.getLogger(UserFavoriteEntity.class.getName()); @@ -33,10 +46,17 @@ public class UserFavoriteEntity implements DashboardEntity { public static final String TEST_KEY = "testKey"; public static final String MUTE_NOTIFICATIONS = "muteNotifications"; - private final Key key; - public final User user; - public final Key testKey; - public boolean muteNotifications; + @Ignore private Key key = null; + + @Ignore public User user = null; + + @Ignore public Key testKey = null; + + @Getter @Setter private com.googlecode.objectify.Key<TestEntity> test; + + @Getter @Setter private String userEmail; + + @Getter @Setter public boolean muteNotifications; /** * Create a user favorite relationship. @@ -64,7 +84,12 @@ public class UserFavoriteEntity implements DashboardEntity { this(null, user, testKey, muteNotifications); } + /** Saving function for the instance of this class */ @Override + public com.googlecode.objectify.Key<UserFavoriteEntity> save() { + return ofy().save().entity(this).now(); + } + public Entity toEntity() { Entity favoriteEntity; if (this.key != null) { diff --git a/src/main/java/com/android/vts/job/BaseJobServlet.java b/src/main/java/com/android/vts/job/BaseJobServlet.java index 3d6232b..18a1d24 100644 --- a/src/main/java/com/android/vts/job/BaseJobServlet.java +++ b/src/main/java/com/android/vts/job/BaseJobServlet.java @@ -16,15 +16,10 @@ package com.android.vts.job; -import com.android.vts.servlet.BaseServlet; -import com.android.vts.util.EmailHelper; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; import java.util.Properties; /** @@ -37,20 +32,20 @@ public abstract class BaseJobServlet extends HttpServlet { */ protected static Properties systemConfigProp = new Properties(); + /** + * This variable is for maximum number of entities per transaction You can find the detail here + * (https://cloud.google.com/datastore/docs/concepts/limits) + */ + protected int MAX_ENTITY_SIZE_PER_TRANSACTION = 300; + @Override public void init(ServletConfig cfg) throws ServletException { super.init(cfg); - try { - InputStream defaultInputStream = - BaseServlet.class.getClassLoader().getResourceAsStream("config.properties"); - systemConfigProp.load(defaultInputStream); - - EmailHelper.setPropertyValues(systemConfigProp); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } + systemConfigProp = + Properties.class.cast(cfg.getServletContext().getAttribute("systemConfigProp")); + + this.MAX_ENTITY_SIZE_PER_TRANSACTION = + Integer.parseInt(systemConfigProp.getProperty("datastore.maxEntitySize")); } } diff --git a/src/main/java/com/android/vts/job/VtsAlertJobServlet.java b/src/main/java/com/android/vts/job/VtsAlertJobServlet.java index 3336c85..be23982 100644 --- a/src/main/java/com/android/vts/job/VtsAlertJobServlet.java +++ b/src/main/java/com/android/vts/job/VtsAlertJobServlet.java @@ -179,29 +179,30 @@ public class VtsAlertJobServlet extends BaseJobServlet { List<TestAcknowledgmentEntity> acks) { Set<String> acknowledged = new HashSet<>(); for (TestAcknowledgmentEntity ack : acks) { - boolean allDevices = ack.devices == null || ack.devices.size() == 0; - boolean allBranches = ack.branches == null || ack.branches.size() == 0; + boolean allDevices = ack.getDevices() == null || ack.getDevices().size() == 0; + boolean allBranches = ack.getBranches() == null || ack.getBranches().size() == 0; boolean isRelevant = allDevices && allBranches; // Determine if the acknowledgment is relevant to the devices. if (!isRelevant) { for (DeviceInfoEntity device : devices) { boolean deviceAcknowledged = - allDevices || ack.devices.contains(device.getBuildFlavor()); + allDevices || ack.getDevices().contains(device.getBuildFlavor()); boolean branchAcknowledged = - allBranches || ack.branches.contains(device.getBranch()); + allBranches || ack.getBranches().contains(device.getBranch()); if (deviceAcknowledged && branchAcknowledged) isRelevant = true; } } if (isRelevant) { // Separate the test cases - boolean allTestCases = ack.testCaseNames == null || ack.testCaseNames.size() == 0; + boolean allTestCases = + ack.getTestCaseNames() == null || ack.getTestCaseNames().size() == 0; if (allTestCases) { acknowledged.addAll(testCases); testCases.removeAll(acknowledged); } else { - for (String testCase : ack.testCaseNames) { + for (String testCase : ack.getTestCaseNames()) { if (testCases.contains(testCase)) { acknowledged.add(testCase); testCases.remove(testCase); diff --git a/src/main/java/com/android/vts/job/VtsSpreadSheetSyncServlet.java b/src/main/java/com/android/vts/job/VtsSpreadSheetSyncServlet.java index 0f1bfbc..45726d9 100644 --- a/src/main/java/com/android/vts/job/VtsSpreadSheetSyncServlet.java +++ b/src/main/java/com/android/vts/job/VtsSpreadSheetSyncServlet.java @@ -17,6 +17,7 @@ package com.android.vts.job; import com.android.vts.entity.ApiCoverageExcludedEntity; +import com.android.vts.entity.DashboardEntity; import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.extensions.appengine.datastore.AppEngineDataStoreFactory; import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; @@ -162,7 +163,7 @@ public class VtsSpreadSheetSyncServlet extends BaseJobServlet { } } - ApiCoverageExcludedEntity.saveAll(apiCoverageExcludedEntities); + DashboardEntity.saveAll(apiCoverageExcludedEntities, MAX_ENTITY_SIZE_PER_TRANSACTION); } catch (GeneralSecurityException gse) { logger.log(Level.SEVERE, gse.getMessage()); diff --git a/src/main/java/com/android/vts/servlet/BaseServlet.java b/src/main/java/com/android/vts/servlet/BaseServlet.java index 96ba561..5319ee1 100644 --- a/src/main/java/com/android/vts/servlet/BaseServlet.java +++ b/src/main/java/com/android/vts/servlet/BaseServlet.java @@ -26,9 +26,7 @@ import com.google.appengine.api.users.UserService; import com.google.appengine.api.users.UserServiceFactory; import com.google.gson.Gson; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -45,200 +43,191 @@ import javax.servlet.http.HttpSession; public abstract class BaseServlet extends HttpServlet { - protected final Logger logger = Logger.getLogger(getClass().getName()); - - protected String ERROR_MESSAGE_JSP = "WEB-INF/jsp/error_msg.jsp"; - - // Environment variables - protected static String GERRIT_URI; - protected static String GERRIT_SCOPE; - protected static String CLIENT_ID; - protected static String ANALYTICS_ID; - - protected static final String TREE_DEFAULT_PARAM = "treeDefault"; - - public enum PageType { - TOT("Test", "/"), - RELEASE("Release", "/show_release"), - COVERAGE_OVERVIEW("Coverage", "/show_coverage_overview"), - PROFILING_LIST("Profiling", "/show_profiling_list"), - TABLE("", "/show_table"), - TREE("", "/show_tree"), - GRAPH("Profiling", "/show_graph"), - COVERAGE("Coverage", "/show_coverage"), - PERFORMANCE_DIGEST("Performance Digest", "/show_performance_digest"), - PLAN_RELEASE("", "/show_plan_release"), - PLAN_RUN("Plan Run", "/show_plan_run"), - PROFILING_OVERVIEW("", "/show_profiling_overview"); - - public final String defaultName; - public final String defaultUrl; - - PageType(String defaultName, String defaultUrl) { - this.defaultName = defaultName; - this.defaultUrl = defaultUrl; + protected final Logger logger = Logger.getLogger(getClass().getName()); + + protected String ERROR_MESSAGE_JSP = "WEB-INF/jsp/error_msg.jsp"; + + // Environment variables + protected static String GERRIT_URI; + protected static String GERRIT_SCOPE; + protected static String CLIENT_ID; + protected static String ANALYTICS_ID; + + protected static final String TREE_DEFAULT_PARAM = "treeDefault"; + + public enum PageType { + TOT("Test", "/"), + RELEASE("Release", "/show_release"), + COVERAGE_OVERVIEW("Coverage", "/show_coverage_overview"), + PROFILING_LIST("Profiling", "/show_profiling_list"), + TABLE("", "/show_table"), + TREE("", "/show_tree"), + GRAPH("Profiling", "/show_graph"), + COVERAGE("Coverage", "/show_coverage"), + PERFORMANCE_DIGEST("Performance Digest", "/show_performance_digest"), + PLAN_RELEASE("", "/show_plan_release"), + PLAN_RUN("Plan Run", "/show_plan_run"), + PROFILING_OVERVIEW("", "/show_profiling_overview"); + + public final String defaultName; + public final String defaultUrl; + + PageType(String defaultName, String defaultUrl) { + this.defaultName = defaultName; + this.defaultUrl = defaultUrl; + } } - } - public static class Page { + public static class Page { - private final PageType type; - private final String name; - private final String url; + private final PageType type; + private final String name; + private final String url; - public Page(PageType type) { - this.type = type; - this.name = type.defaultName; - this.url = type.defaultUrl; - } + public Page(PageType type) { + this.type = type; + this.name = type.defaultName; + this.url = type.defaultUrl; + } - public Page(PageType type, String name, String url) { - this.type = type; - this.name = type.defaultName + name; - this.url = type.defaultUrl + url; - } + public Page(PageType type, String name, String url) { + this.type = type; + this.name = type.defaultName + name; + this.url = type.defaultUrl + url; + } - public Page(PageType type, String name, String url, Boolean withoutDefault) { - this.type = type; - this.name = name; - this.url = type.defaultUrl + url; - } + public Page(PageType type, String name, String url, Boolean withoutDefault) { + this.type = type; + this.name = name; + this.url = type.defaultUrl + url; + } - public Page(PageType type, String url) { - this.type = type; - this.name = type.defaultName; - this.url = type.defaultUrl + url; - } + public Page(PageType type, String url) { + this.type = type; + this.name = type.defaultName; + this.url = type.defaultUrl + url; + } + + public String getName() { + return name; + } - public String getName() { - return name; + public String getUrl() { + return url; + } } - public String getUrl() { - return url; + public static final List<Page> navbarLinks; + + static { + List<Page> links = new ArrayList<>(); + links.add(new Page(PageType.TOT)); + links.add(new Page(PageType.RELEASE)); + links.add(new Page(PageType.COVERAGE_OVERVIEW)); + links.add(new Page(PageType.PROFILING_LIST)); + navbarLinks = links; } - } - - public static final List<Page> navbarLinks; - - static { - List<Page> links = new ArrayList<>(); - links.add(new Page(PageType.TOT)); - links.add(new Page(PageType.RELEASE)); - links.add(new Page(PageType.COVERAGE_OVERVIEW)); - links.add(new Page(PageType.PROFILING_LIST)); - navbarLinks = links; - } - - public abstract PageType getNavParentType(); - - /** - * Get a list of URL/Display name pairs for the breadcrumb hierarchy. - * - * @param request The HttpServletRequest object for the page request. - * @return a list of Page entries. - */ - public abstract List<Page> getBreadcrumbLinks(HttpServletRequest request); - - /** - * System Configuration Property class - */ - protected static Properties systemConfigProp = new Properties(); - - @Override - public void init(ServletConfig cfg) throws ServletException { - super.init(cfg); - - try { - InputStream defaultInputStream = - BaseServlet.class.getClassLoader().getResourceAsStream("config.properties"); - systemConfigProp.load(defaultInputStream); - - GERRIT_URI = systemConfigProp.getProperty("gerrit.uri"); - GERRIT_SCOPE = systemConfigProp.getProperty("gerrit.scope"); - CLIENT_ID = systemConfigProp.getProperty("appengine.clientID"); - ANALYTICS_ID = systemConfigProp.getProperty("analytics.id"); - - CoverageEntity.setPropertyValues(systemConfigProp); - TestSuiteResultEntity.setPropertyValues(systemConfigProp); - EmailHelper.setPropertyValues(systemConfigProp); - GcsHelper.setGcsProjectId(systemConfigProp.getProperty("gcs.projectID")); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); + + public abstract PageType getNavParentType(); + + /** + * Get a list of URL/Display name pairs for the breadcrumb hierarchy. + * + * @param request The HttpServletRequest object for the page request. + * @return a list of Page entries. + */ + public abstract List<Page> getBreadcrumbLinks(HttpServletRequest request); + + /** System Configuration Property class */ + protected static Properties systemConfigProp = new Properties(); + + @Override + public void init(ServletConfig cfg) throws ServletException { + super.init(cfg); + + systemConfigProp = + Properties.class.cast(cfg.getServletContext().getAttribute("systemConfigProp")); + + GERRIT_URI = systemConfigProp.getProperty("gerrit.uri"); + GERRIT_SCOPE = systemConfigProp.getProperty("gerrit.scope"); + CLIENT_ID = systemConfigProp.getProperty("appengine.clientID"); + ANALYTICS_ID = systemConfigProp.getProperty("analytics.id"); + + CoverageEntity.setPropertyValues(systemConfigProp); + TestSuiteResultEntity.setPropertyValues(systemConfigProp); + EmailHelper.setPropertyValues(systemConfigProp); + GcsHelper.setGcsProjectId(systemConfigProp.getProperty("gcs.projectID")); } - } - - @Override - public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { - // If the user is logged out, allow them to log back in and return to the page. - // Set the logout URL to direct back to a login page that directs to the current request. - UserService userService = UserServiceFactory.getUserService(); - Optional<User> currentUser = Optional.ofNullable(userService.getCurrentUser()); - String currentUserEmail = - currentUser.isPresent() - ? currentUser.map(user -> user.getEmail().trim()).orElse("") - : ""; - String requestUri = request.getRequestURI(); - String requestArgs = request.getQueryString(); - String loginURI = userService.createLoginURL(requestUri + '?' + requestArgs); - String logoutURI = userService.createLogoutURL(loginURI); - if (currentUserEmail != "") { - - int activeIndex; - switch (getNavParentType()) { - case PROFILING_LIST: - activeIndex = 3; - break; - case COVERAGE_OVERVIEW: - activeIndex = 2; - break; - case RELEASE: - activeIndex = 1; - break; - default: - activeIndex = 0; - break; - } - if (request.getParameter(TREE_DEFAULT_PARAM) != null) { - HttpSession session = request.getSession(true); - boolean treeDefault = request.getParameter(TREE_DEFAULT_PARAM).equals("true"); - session.setAttribute(TREE_DEFAULT_PARAM, treeDefault); - } - - request.setAttribute("serverName", request.getServerName()); - request.setAttribute("logoutURL", logoutURI); - request.setAttribute("email", currentUserEmail); - request.setAttribute("analyticsID", new Gson().toJson(ANALYTICS_ID)); - request.setAttribute("breadcrumbLinks", getBreadcrumbLinks(request)); - request.setAttribute("navbarLinks", navbarLinks); - request.setAttribute("activeIndex", activeIndex); - response.setContentType("text/html"); - - if (currentUserEmail.endsWith("@google.com") || UserEntity.getUserList() - .contains(currentUserEmail)) { - doGetHandler(request, response); - } else { - RequestDispatcher dispatcher = - request.getRequestDispatcher("WEB-INF/jsp/auth_error.jsp"); - try { - dispatcher.forward(request, response); - } catch (ServletException e) { - logger.log(Level.SEVERE, "Servlet Exception caught : ", e); + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + // If the user is logged out, allow them to log back in and return to the page. + // Set the logout URL to direct back to a login page that directs to the current request. + UserService userService = UserServiceFactory.getUserService(); + Optional<User> currentUser = Optional.ofNullable(userService.getCurrentUser()); + String currentUserEmail = + currentUser.isPresent() + ? currentUser.map(user -> user.getEmail().trim()).orElse("") + : ""; + String requestUri = request.getRequestURI(); + String requestArgs = request.getQueryString(); + String loginURI = userService.createLoginURL(requestUri + '?' + requestArgs); + String logoutURI = userService.createLogoutURL(loginURI); + if (currentUserEmail != "") { + + int activeIndex; + switch (getNavParentType()) { + case PROFILING_LIST: + activeIndex = 3; + break; + case COVERAGE_OVERVIEW: + activeIndex = 2; + break; + case RELEASE: + activeIndex = 1; + break; + default: + activeIndex = 0; + break; + } + if (request.getParameter(TREE_DEFAULT_PARAM) != null) { + HttpSession session = request.getSession(true); + boolean treeDefault = request.getParameter(TREE_DEFAULT_PARAM).equals("true"); + session.setAttribute(TREE_DEFAULT_PARAM, treeDefault); + } + + request.setAttribute("serverName", request.getServerName()); + request.setAttribute("logoutURL", logoutURI); + request.setAttribute("email", currentUserEmail); + request.setAttribute("analyticsID", new Gson().toJson(ANALYTICS_ID)); + request.setAttribute("breadcrumbLinks", getBreadcrumbLinks(request)); + request.setAttribute("navbarLinks", navbarLinks); + request.setAttribute("activeIndex", activeIndex); + response.setContentType("text/html"); + + if (currentUserEmail.endsWith("@google.com") + || UserEntity.getUserList().contains(currentUserEmail)) { + doGetHandler(request, response); + } else { + RequestDispatcher dispatcher = + request.getRequestDispatcher("WEB-INF/jsp/auth_error.jsp"); + try { + dispatcher.forward(request, response); + } catch (ServletException e) { + logger.log(Level.SEVERE, "Servlet Exception caught : ", e); + } + } + } else { + response.sendRedirect(loginURI); } - } - } else { - response.sendRedirect(loginURI); } - } - - /** - * Implementation of the doGet method to be executed by servlet subclasses. - * - * @param request The HttpServletRequest object. - * @param response The HttpServletResponse object. - */ - public abstract void doGetHandler(HttpServletRequest request, HttpServletResponse response) - throws IOException; + + /** + * Implementation of the doGet method to be executed by servlet subclasses. + * + * @param request The HttpServletRequest object. + * @param response The HttpServletResponse object. + */ + public abstract void doGetHandler(HttpServletRequest request, HttpServletResponse response) + throws IOException; } diff --git a/src/main/java/com/android/vts/util/DatastoreHelper.java b/src/main/java/com/android/vts/util/DatastoreHelper.java index 2dbca37..3764c7a 100644 --- a/src/main/java/com/android/vts/util/DatastoreHelper.java +++ b/src/main/java/com/android/vts/util/DatastoreHelper.java @@ -112,8 +112,8 @@ public class DatastoreHelper { DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Key startKey = KeyFactory.createKey(parentKey, kind, lowerBound); Filter startFilter = - new FilterPredicate( - Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, startKey); + new FilterPredicate( + Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, startKey); Query q = new Query(kind).setAncestor(parentKey).setFilter(startFilter).setKeysOnly(); return datastore.prepare(q).countEntities(FetchOptions.Builder.withLimit(1)) > 0; } @@ -132,7 +132,7 @@ public class DatastoreHelper { } Key endKey = KeyFactory.createKey(parentKey, kind, upperBound); Filter endFilter = - new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.LESS_THAN, endKey); + new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.LESS_THAN, endKey); Query q = new Query(kind).setAncestor(parentKey).setFilter(endFilter).setKeysOnly(); return datastore.prepare(q).countEntities(FetchOptions.Builder.withLimit(1)) > 0; } @@ -179,10 +179,10 @@ public class DatastoreHelper { List<Entity> profilingPointRunEntityList = new ArrayList<>(); if (!report.hasStartTimestamp() - || !report.hasEndTimestamp() - || !report.hasTest() - || !report.hasHostInfo() - || !report.hasBuildInfo()) { + || !report.hasEndTimestamp() + || !report.hasTest() + || !report.hasHostInfo() + || !report.hasBuildInfo()) { // missing information return; } @@ -195,8 +195,8 @@ public class DatastoreHelper { TestEntity testEntity = new TestEntity(testName); Key testRunKey = - KeyFactory.createKey( - testEntity.getOldKey(), TestRunEntity.KIND, report.getStartTimestamp()); + KeyFactory.createKey( + testEntity.getOldKey(), TestRunEntity.KIND, report.getStartTimestamp()); long passCount = 0; long failCount = 0; @@ -220,7 +220,7 @@ public class DatastoreHelper { ++failCount; } if (testCase.getSystraceCount() > 0 - && testCase.getSystraceList().get(0).getUrlCount() > 0) { + && testCase.getSystraceList().get(0).getUrlCount() > 0) { String systraceLink = testCase.getSystraceList().get(0).getUrl(0).toStringUtf8(); links.add(systraceLink); } @@ -228,7 +228,7 @@ public class DatastoreHelper { // Process coverage data for test case for (CoverageReportMessage coverage : testCase.getCoverageList()) { CoverageEntity coverageEntity = - CoverageEntity.fromCoverageReport(testRunKey, testCaseName, coverage); + CoverageEntity.fromCoverageReport(testRunKey, testCaseName, coverage); if (coverageEntity == null) { logger.log(Level.WARNING, "Invalid coverage report in test run " + testRunKey); } else { @@ -241,7 +241,7 @@ public class DatastoreHelper { // Process profiling data for test case for (ProfilingReportMessage profiling : testCase.getProfilingList()) { ProfilingPointRunEntity profilingPointRunEntity = - ProfilingPointRunEntity.fromProfilingReport(testRunKey, profiling); + ProfilingPointRunEntity.fromProfilingReport(testRunKey, profiling); if (profilingPointRunEntity == null) { logger.log(Level.WARNING, "Invalid profiling report in test run " + testRunKey); } else { @@ -275,7 +275,7 @@ public class DatastoreHelper { long testRunType = 0; for (AndroidDeviceInfoMessage device : report.getDeviceInfoList()) { DeviceInfoEntity deviceInfoEntity = - DeviceInfoEntity.fromDeviceInfoMessage(testRunKey, device); + DeviceInfoEntity.fromDeviceInfoMessage(testRunKey, device); if (deviceInfoEntity == null) { logger.log(Level.WARNING, "Invalid device info in test run " + testRunKey); } else { @@ -308,7 +308,7 @@ public class DatastoreHelper { // Process global coverage data for (CoverageReportMessage coverage : report.getCoverageList()) { CoverageEntity coverageEntity = - CoverageEntity.fromCoverageReport(testRunKey, new String(), coverage); + CoverageEntity.fromCoverageReport(testRunKey, new String(), coverage); if (coverageEntity == null) { logger.log(Level.WARNING, "Invalid coverage report in test run " + testRunKey); } else { @@ -322,19 +322,19 @@ public class DatastoreHelper { for (ApiCoverageReportMessage apiCoverage : report.getApiCoverageList()) { HalInterfaceMessage halInterfaceMessage = apiCoverage.getHalInterface(); List<String> halApiList = apiCoverage.getHalApiList().stream().map(h -> h.toStringUtf8()) - .collect( - Collectors.toList()); + .collect( + Collectors.toList()); List<String> coveredHalApiList = apiCoverage.getCoveredHalApiList().stream() - .map(h -> h.toStringUtf8()).collect( - Collectors.toList()); + .map(h -> h.toStringUtf8()).collect( + Collectors.toList()); ApiCoverageEntity apiCoverageEntity = new ApiCoverageEntity( - testRunKey, - halInterfaceMessage.getHalPackageName().toStringUtf8(), - halInterfaceMessage.getHalVersionMajor(), - halInterfaceMessage.getHalVersionMinor(), - halInterfaceMessage.getHalInterfaceName().toStringUtf8(), - halApiList, - coveredHalApiList + testRunKey, + halInterfaceMessage.getHalPackageName().toStringUtf8(), + halInterfaceMessage.getHalVersionMajor(), + halInterfaceMessage.getHalVersionMinor(), + halInterfaceMessage.getHalInterfaceName().toStringUtf8(), + halApiList, + coveredHalApiList ); com.googlecode.objectify.Key apiCoverageEntityKey = apiCoverageEntity.save(); if (apiCoverageEntityKey == null) { @@ -345,7 +345,7 @@ public class DatastoreHelper { // Process global profiling data for (ProfilingReportMessage profiling : report.getProfilingList()) { ProfilingPointRunEntity profilingPointRunEntity = - ProfilingPointRunEntity.fromProfilingReport(testRunKey, profiling); + ProfilingPointRunEntity.fromProfilingReport(testRunKey, profiling); if (profilingPointRunEntity == null) { logger.log(Level.WARNING, "Invalid profiling report in test run " + testRunKey); } else { @@ -370,18 +370,18 @@ public class DatastoreHelper { boolean hasCodeCoverage = totalLineCount > 0 && coveredLineCount >= 0; TestRunEntity testRunEntity = - new TestRunEntity( - testEntity.getOldKey(), - testRunType, - startTimestamp, - endTimestamp, - testBuildId, - hostName, - passCount, - failCount, - hasCodeCoverage, - testCaseIds, - links); + new TestRunEntity( + testEntity.getOldKey(), + testRunType, + startTimestamp, + endTimestamp, + testBuildId, + hostName, + passCount, + failCount, + hasCodeCoverage, + testCaseIds, + links); testEntityList.add(testRunEntity.toEntity()); CodeCoverageEntity codeCoverageEntity = new CodeCoverageEntity( @@ -394,11 +394,11 @@ public class DatastoreHelper { if (datastoreTransactionalRetry(test, testEntityList)) { List<List<Entity>> auxiliaryEntityList = - Arrays.asList( - profilingPointRunEntityList, - coverageEntityList, - branchEntityList, - buildTargetEntityList); + Arrays.asList( + profilingPointRunEntityList, + coverageEntityList, + branchEntityList, + buildTargetEntityList); int indexCount = 0; for (List<Entity> entityList : auxiliaryEntityList) { switch (indexCount) { @@ -406,12 +406,12 @@ public class DatastoreHelper { case 1: if (entityList.size() > MAX_ENTITY_SIZE_PER_TRANSACTION) { List<List<Entity>> partitionedList = - Lists.partition(entityList, MAX_ENTITY_SIZE_PER_TRANSACTION); + Lists.partition(entityList, MAX_ENTITY_SIZE_PER_TRANSACTION); partitionedList.forEach( - subEntityList -> { - datastoreTransactionalRetry( - new Entity(NULL_ENTITY_KIND), subEntityList); - }); + subEntityList -> { + datastoreTransactionalRetry( + new Entity(NULL_ENTITY_KIND), subEntityList); + }); } else { datastoreTransactionalRetry(new Entity(NULL_ENTITY_KIND), entityList); } @@ -419,7 +419,7 @@ public class DatastoreHelper { case 2: case 3: datastoreTransactionalRetryWithXG( - new Entity(NULL_ENTITY_KIND), entityList, true); + new Entity(NULL_ENTITY_KIND), entityList, true); break; default: break; @@ -429,7 +429,7 @@ public class DatastoreHelper { if (testRunEntity.getType() == TestRunType.POSTSUBMIT.getNumber()) { VtsAlertJobServlet.addTask(testRunKey); - if (testRunEntity.getHasCodeCoverage()) { + if (testRunEntity.getHasCodeCoverage()) { VtsCoverageAlertJobServlet.addTask(testRunKey); } if (profilingPointKeys.size() > 0) { @@ -437,9 +437,9 @@ public class DatastoreHelper { } } else { logger.log( - Level.WARNING, - "The alert email was not sent as testRunEntity type is not POSTSUBMIT!" + - " \n " + " testRunEntity type => " + testRunEntity.getType()); + Level.WARNING, + "The alert email was not sent as testRunEntity type is not POSTSUBMIT!" + + " \n " + " testRunEntity type => " + testRunEntity.getType()); } } } @@ -506,18 +506,18 @@ public class DatastoreHelper { return; } TestPlanRunEntity testPlanRun = - new TestPlanRunEntity( - testPlanEntity.getKey(), - testPlanName, - type, - startTimestamp, - endTimestamp, - testBuildId, - passCount, - failCount, - 0L, - 0L, - testRunKeys); + new TestPlanRunEntity( + testPlanEntity.getKey(), + testPlanName, + type, + startTimestamp, + endTimestamp, + testBuildId, + passCount, + failCount, + 0L, + 0L, + testRunKeys); // Create the device infos. for (DeviceInfoEntity device : deviceInfoEntitySet) { @@ -549,7 +549,7 @@ public class DatastoreHelper { * @param entityList The list of entity for using datastore put method. */ private static boolean datastoreTransactionalRetryWithXG( - Entity entity, List<Entity> entityList, boolean withXG) { + Entity entity, List<Entity> entityList, boolean withXG) { int retries = 0; while (true) { Transaction txn; @@ -568,7 +568,7 @@ public class DatastoreHelper { Entity datastoreEntity = datastore.get(entity.getKey()); TestEntity datastoreTestEntity = TestEntity.fromEntity(datastoreEntity); if (datastoreTestEntity == null - || !datastoreTestEntity.equals(entity)) { + || !datastoreTestEntity.equals(entity)) { entityList.add(entity); } } else if (entity.getKind().equalsIgnoreCase("TestPlan")) { @@ -584,25 +584,25 @@ public class DatastoreHelper { txn.commit(); break; } catch (ConcurrentModificationException - | DatastoreFailureException - | DatastoreTimeoutException e) { + | DatastoreFailureException + | DatastoreTimeoutException e) { entityList.remove(entity); logger.log( - Level.WARNING, - "Retrying insert kind: " + entity.getKind() + " key: " + entity.getKey()); + Level.WARNING, + "Retrying insert kind: " + entity.getKind() + " key: " + entity.getKey()); if (retries++ >= MAX_WRITE_RETRIES) { logger.log( - Level.SEVERE, - "Exceeded maximum retries kind: " - + entity.getKind() - + " key: " - + entity.getKey()); + Level.SEVERE, + "Exceeded maximum retries kind: " + + entity.getKind() + + " key: " + + entity.getKey()); return false; } } finally { if (txn.isActive()) { logger.log( - Level.WARNING, "Transaction rollback forced for : " + entity.getKind()); + Level.WARNING, "Transaction rollback forced for : " + entity.getKind()); txn.rollback(); } } |