diff options
author | Young Gyu Park <younggyu@google.com> | 2018-01-09 11:53:12 +0900 |
---|---|---|
committer | Young Gyu Park <younggyu@google.com> | 2018-01-10 13:50:16 +0900 |
commit | 834fd6cbc59f87c2eb5890c0815af397bc48887a (patch) | |
tree | e00acb63d320819e54594d014dca3e36fb7a6084 /src | |
parent | 1df2445b9bf6624556ca7a9dfd212901260e534c (diff) | |
download | dashboard-834fd6cbc59f87c2eb5890c0815af397bc48887a.tar.gz |
Mock data generating API for dashboard in local dev environment.
Test: Tested with curl command( curl -d @test-report-data.json -m 30 -X
POST http://127.0.0.1:8080/api/test_data/report -H "Content-Type:
application/json" --verbose )
Bug: 71725296
Change-Id: Iaeda9b68294e41415921ef07bd4aa960d7762883
Diffstat (limited to 'src')
-rw-r--r-- | src/main/java/com/android/vts/api/TestDataForDevServlet.java | 304 | ||||
-rw-r--r-- | src/main/webapp/WEB-INF/web.xml | 10 |
2 files changed, 314 insertions, 0 deletions
diff --git a/src/main/java/com/android/vts/api/TestDataForDevServlet.java b/src/main/java/com/android/vts/api/TestDataForDevServlet.java new file mode 100644 index 0000000..8faf262 --- /dev/null +++ b/src/main/java/com/android/vts/api/TestDataForDevServlet.java @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2017 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you + * may not use this file except in compliance with the License. You may + * obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.vts.api; + +import com.android.vts.entity.BranchEntity; +import com.android.vts.entity.BuildTargetEntity; +import com.android.vts.entity.CoverageEntity; +import com.android.vts.entity.DeviceInfoEntity; +import com.android.vts.entity.ProfilingPointRunEntity; +import com.android.vts.entity.TestCaseRunEntity; +import com.android.vts.entity.TestEntity; +import com.android.vts.entity.TestRunEntity; +import com.android.vts.entity.TestRunEntity.TestRunType; +import com.android.vts.entity.TestStatusEntity; +import com.android.vts.entity.TestStatusEntity.TestCaseReference; +import com.google.appengine.api.datastore.DatastoreFailureException; +import com.google.appengine.api.datastore.DatastoreService; +import com.google.appengine.api.datastore.DatastoreServiceFactory; +import com.google.appengine.api.datastore.DatastoreTimeoutException; +import com.google.appengine.api.datastore.Entity; +import com.google.appengine.api.datastore.EntityNotFoundException; +import com.google.appengine.api.datastore.Key; +import com.google.appengine.api.datastore.KeyFactory; +import com.google.appengine.api.datastore.Transaction; +import com.google.appengine.api.users.User; +import com.google.appengine.api.users.UserService; +import com.google.appengine.api.users.UserServiceFactory; +import com.google.appengine.api.utils.SystemProperty; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.ConcurrentModificationException; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** Servlet for handling requests to add mock data in datastore. */ +public class TestDataForDevServlet extends HttpServlet { + protected static final Logger logger = + Logger.getLogger(TestDataForDevServlet.class.getName()); + + /** datastore instance to save the test data into datastore through datastore library. */ + private DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); + /** Gson is a Java library that can be used to convert Java Objects into their JSON representation. + * It can also be used to convert a JSON string to an equivalent Java object. + */ + private Gson gson = new GsonBuilder().create(); + + /** TestReportData class for mapping test-report-data.json. + * This internal class's each fields will be automatically mapped + * to test-report-data.json file through Gson + */ + private class TestReportDataObject { + private List<Test> testList; + + private class Test { + private List<TestRun> testRunList; + + private class TestRun { + private String testName; + private int type; + private long startTimestamp; + private long endTimestamp; + private String testBuildId; + private String hostName; + private long passCount; + private long failCount; + private boolean hasCoverage; + private long coveredLineCount; + private long totalLineCount; + private List<Long> testCaseIds; + private List<Long> failingTestcaseIds; + private List<Integer> failingTestcaseOffsets; + private List<String> links; + + private List<Coverage> coverageList; + private List<Profiling> profilingList; + private List<TestCaseRun> testCaseRunList; + private List<DeviceInfo> deviceInfoList; + private List<BuildTarget> buildTargetList; + private List<Branch> branchList; + + + private class Coverage { + private String group; + private long coveredLineCount; + private long totalLineCount; + private String filePath; + private String projectName; + private String projectVersion; + private List<Long> lineCoverage; + } + + private class Profiling { + private String name; + private int type; + private int regressionMode; + private List<String> labels; + private List<Long> values; + private String xLabel; + private String yLabel; + private List<String> options; + } + + private class TestCaseRun { + private List<String> testCaseNames; + private List<Integer> results; + } + + private class DeviceInfo { + private String branch; + private String product; + private String buildFlavor; + private String buildId; + private String abiBitness; + private String abiName; + } + + private class BuildTarget { + private String targetName; + } + + private class Branch { + private String branchName; + } + + } + } + + @Override + public String toString() { + return "(" + testList + ")"; + } + } + + private class TestPlanReportDataObject { + private List<BranchEntity> branchEntities; + private List<BuildTargetEntity> buildTargetEntities; + private List<CoverageEntity> coverageEntities; + + @Override + public String toString() { + return "(" + branchEntities + ")"; + } + } + + /** Add mock data to local dev datastore. */ + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws IOException { + if (SystemProperty.environment.value() == SystemProperty.Environment.Value.Production) { + response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); + return; + } + + UserService userService = UserServiceFactory.getUserService(); + User currentUser = userService.getCurrentUser(); + + String pathInfo = request.getPathInfo(); + String[] pathParts = pathInfo.split("/"); + if (pathParts.length > 1) { + // Read the json output + Reader postJsonReader = new InputStreamReader(request.getInputStream()); + Gson gson = new GsonBuilder().create(); + + String testType = pathParts[1]; + if (testType.equalsIgnoreCase("report")) { + TestReportDataObject trdObj = + gson.fromJson(postJsonReader, TestReportDataObject.class); + logger.log(Level.INFO, "trdObj => " + trdObj); + trdObj.testList.forEach(test -> { + test.testRunList.forEach(testRun -> { + TestEntity testEntity = new TestEntity(testRun.testName); + + Key testRunKey = KeyFactory.createKey(testEntity.key, TestRunEntity.KIND, + testRun.startTimestamp); + + List<TestCaseReference> failingTestCases = new ArrayList<>(); + for (int idx = 0; idx < testRun.failingTestcaseIds.size(); idx++){ + failingTestCases.add( + new TestCaseReference(testRun.failingTestcaseIds.get(idx), + testRun.failingTestcaseOffsets.get(idx)) + ); + } + + TestStatusEntity testStatusEntity = new TestStatusEntity(testRun.testName, + testRun.startTimestamp, (int) testRun.passCount, failingTestCases.size(), + failingTestCases); + datastore.put(testStatusEntity.toEntity()); + + testRun.coverageList.forEach(testRunCoverage -> { + CoverageEntity coverageEntity = new CoverageEntity(testRunKey, + testRunCoverage.group, testRunCoverage.coveredLineCount, + testRunCoverage.totalLineCount, testRunCoverage.filePath, + testRunCoverage.projectName, testRunCoverage.projectVersion, + testRunCoverage.lineCoverage); + datastore.put(coverageEntity.toEntity()); + }); + + testRun.profilingList.forEach(testRunProfile -> { + ProfilingPointRunEntity profilingEntity = new ProfilingPointRunEntity( + testRunKey, testRunProfile.name, testRunProfile.type, + testRunProfile.regressionMode, testRunProfile.labels, + testRunProfile.values, testRunProfile.xLabel, testRunProfile.yLabel, + testRunProfile.options); + datastore.put(profilingEntity.toEntity()); + }); + + TestCaseRunEntity testCaseEntity = new TestCaseRunEntity(); + testRun.testCaseRunList.forEach(testCaseRun -> { + for (int idx = 0; idx < testCaseRun.testCaseNames.size(); idx++){ + testCaseEntity.addTestCase(testCaseRun.testCaseNames.get(idx), + testCaseRun.results.get(idx)); + } + }); + datastore.put(testCaseEntity.toEntity()); + + testRun.deviceInfoList.forEach(deviceInfo -> { + DeviceInfoEntity deviceInfoEntity = new DeviceInfoEntity( + testRunKey, deviceInfo.branch, deviceInfo.product, + deviceInfo.buildFlavor, deviceInfo.buildId, deviceInfo.abiBitness, + deviceInfo.abiName);; + datastore.put(deviceInfoEntity.toEntity()); + }); + + testRun.buildTargetList.forEach(buildTarget -> { + BuildTargetEntity buildTargetEntity = + new BuildTargetEntity(buildTarget.targetName); + datastore.put(buildTargetEntity.toEntity()); + }); + + testRun.branchList.forEach(branch -> { + BranchEntity branchEntity = new BranchEntity(branch.branchName); + datastore.put(branchEntity.toEntity()); + }); + + TestRunEntity testRunEntity = new TestRunEntity(testEntity.key, + TestRunType.fromNumber(testRun.type), testRun.startTimestamp, + testRun.endTimestamp, testRun.testBuildId, testRun.hostName, + testRun.passCount, testRun.failCount, testRun.testCaseIds, + testRun.links, testRun.coveredLineCount, testRun.totalLineCount); + datastore.put(testRunEntity.toEntity()); + + Entity newTestEntity = testEntity.toEntity(); + + Transaction txn = datastore.beginTransaction(); + try { + // Check if test already exists in the datastore + try { + Entity oldTest = datastore.get(testEntity.key); + TestEntity oldTestEntity = TestEntity.fromEntity(oldTest); + if (oldTestEntity == null || !oldTestEntity.equals(testEntity)) { + datastore.put(newTestEntity); + } + } catch (EntityNotFoundException e) { + datastore.put(newTestEntity); + } + txn.commit(); + + } catch (ConcurrentModificationException | DatastoreFailureException + | DatastoreTimeoutException e) { + logger.log(Level.WARNING, + "Retrying test run insert: " + newTestEntity.getKey()); + } finally { + if (txn.isActive()) { + logger.log( + Level.WARNING, + "Transaction rollback forced for run: " + testRunEntity.key); + txn.rollback(); + } + } + }); + }); + } else { + TestPlanReportDataObject tprdObj = + gson.fromJson(postJsonReader, TestPlanReportDataObject.class); + } + } else { + logger.log(Level.WARNING, "URL path parameter is omitted!"); + } + + response.setStatus(HttpServletResponse.SC_OK); + } +} diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 6b2ea9c..bf8c2ea 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -81,6 +81,11 @@ Copyright 2016 Google Inc. All Rights Reserved. </servlet> <servlet> + <servlet-name>test_data</servlet-name> + <servlet-class>com.android.vts.api.TestDataForDevServlet</servlet-class> +</servlet> + +<servlet> <servlet-name>datastore</servlet-name> <servlet-class>com.android.vts.api.DatastoreRestServlet</servlet-class> </servlet> @@ -201,6 +206,11 @@ Copyright 2016 Google Inc. All Rights Reserved. </servlet-mapping> <servlet-mapping> + <servlet-name>test_data</servlet-name> + <url-pattern>/api/test_data/*</url-pattern> +</servlet-mapping> + +<servlet-mapping> <servlet-name>datastore</servlet-name> <url-pattern>/api/datastore/*</url-pattern> </servlet-mapping> |