blob: 36637a324896a41afada45aa923b64a848cdd86b [file] [log] [blame]
/*
* Copyright (C) 2017 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.job;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.android.vts.entity.DeviceInfoEntity;
import com.android.vts.entity.ProfilingPointEntity;
import com.android.vts.entity.ProfilingPointRunEntity;
import com.android.vts.entity.ProfilingPointSummaryEntity;
import com.android.vts.entity.TestEntity;
import com.android.vts.entity.TestRunEntity;
import com.android.vts.proto.VtsReportMessage;
import com.android.vts.util.ObjectifyTestBase;
import com.android.vts.util.StatSummary;
import com.android.vts.util.TimeUtil;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.taskqueue.dev.LocalTaskQueue;
import com.google.appengine.api.taskqueue.dev.QueueStateInfo;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.appengine.tools.development.testing.LocalTaskQueueTestConfig;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
public class VtsProfilingStatsJobServletTest extends ObjectifyTestBase {
private final LocalServiceTestHelper helper =
new LocalServiceTestHelper(
new LocalDatastoreServiceTestConfig(),
new LocalTaskQueueTestConfig()
.setQueueXmlPath("src/main/webapp/WEB-INF/queue.xml"));
private static final double THRESHOLD = 1e-10;
@BeforeEach
public void setUp() {
helper.setUp();
}
@AfterEach
public void tearDown() {
helper.tearDown();
}
private static void createProfilingRun() {
Date d = new Date();
long time = TimeUnit.MILLISECONDS.toMicros(d.getTime());
long canonicalTime = VtsProfilingStatsJobServlet.getCanonicalTime(time);
String test = "test";
String profilingPointName = "profilingPoint";
String xLabel = "xLabel";
String yLabel = "yLabel";
VtsReportMessage.VtsProfilingType type =
VtsReportMessage.VtsProfilingType.VTS_PROFILING_TYPE_UNLABELED_VECTOR;
VtsReportMessage.VtsProfilingRegressionMode mode =
VtsReportMessage.VtsProfilingRegressionMode.VTS_REGRESSION_MODE_INCREASING;
Key testKey = KeyFactory.createKey(TestEntity.KIND, test);
Key testRunKey = KeyFactory.createKey(testKey, TestRunEntity.KIND, time);
Long[] valueArray = new Long[] {1l, 2l, 3l, 4l, 5l};
StatSummary stats =
new StatSummary(
"expected",
VtsReportMessage.VtsProfilingRegressionMode.UNKNOWN_REGRESSION_MODE);
for (long value : valueArray) {
stats.updateStats(value);
}
Mean mean = new Mean();
List<Long> values = Arrays.asList(valueArray);
ProfilingPointRunEntity profilingPointRunEntity =
new ProfilingPointRunEntity(
testRunKey,
profilingPointName,
type.getNumber(),
mode.getNumber(),
null,
values,
xLabel,
yLabel,
null);
String branch = "master";
String product = "product";
String flavor = "flavor";
String id = "12345";
String bitness = "64";
String abiName = "abi";
DeviceInfoEntity device =
new DeviceInfoEntity(testRunKey, branch, product, flavor, id, bitness, abiName);
}
/**
* Test that tasks are correctly scheduled on the queue.
*
* @throws InterruptedException
*/
@Test
public void testTasksScheduled() throws InterruptedException {
String[] testNames = new String[] {"test1", "test2", "test3"};
List<Key> testKeys = new ArrayList();
Set<Key> testKeySet = new HashSet<>();
String kind = "TEST";
for (String testName : testNames) {
Key key = KeyFactory.createKey(kind, testName);
testKeys.add(key);
testKeySet.add(key);
}
VtsProfilingStatsJobServlet.addTasks(testKeys);
Thread.sleep(1000); // wait one second (tasks are scheduled asychronously), must wait.
LocalTaskQueue taskQueue = LocalTaskQueueTestConfig.getLocalTaskQueue();
QueueStateInfo qsi = taskQueue.getQueueStateInfo().get(VtsProfilingStatsJobServlet.QUEUE);
assertNotNull(qsi);
assertEquals(testNames.length, qsi.getTaskInfo().size());
int i = 0;
for (QueueStateInfo.TaskStateInfo taskStateInfo : qsi.getTaskInfo()) {
assertEquals(
VtsProfilingStatsJobServlet.PROFILING_STATS_JOB_URL, taskStateInfo.getUrl());
assertEquals("POST", taskStateInfo.getMethod());
String body = taskStateInfo.getBody();
String[] parts = body.split("=");
assertEquals(2, parts.length);
assertEquals(VtsProfilingStatsJobServlet.PROFILING_POINT_KEY, parts[0]);
String keyString = parts[1];
Key profilingPointRunKey;
try {
profilingPointRunKey = KeyFactory.stringToKey(keyString);
} catch (IllegalArgumentException e) {
fail();
return;
}
assertTrue(testKeys.contains(profilingPointRunKey));
}
}
/** Test that canonical time is correctly derived from a timestamp in the middle of the day. */
@Test
public void testCanonicalTimeMidday() {
int year = 2017;
Month month = Month.MAY;
int day = 28;
int hour = 14;
int minute = 30;
LocalDateTime now = LocalDateTime.of(year, month.getValue(), day, hour, minute);
ZonedDateTime zdt = ZonedDateTime.of(now, TimeUtil.PT_ZONE);
long time = TimeUnit.SECONDS.toMicros(zdt.toEpochSecond());
long canonicalTime = VtsProfilingStatsJobServlet.getCanonicalTime(time);
long canonicalTimeSec = TimeUnit.MICROSECONDS.toSeconds(canonicalTime);
ZonedDateTime canonical =
ZonedDateTime.ofInstant(Instant.ofEpochSecond(canonicalTimeSec), TimeUtil.PT_ZONE);
assertEquals(month, canonical.getMonth());
assertEquals(day, canonical.getDayOfMonth());
assertEquals(0, canonical.getHour());
assertEquals(0, canonical.getMinute());
}
/** Test that canonical time is correctly derived at the boundary of two days (midnight). */
@Test
public void testCanonicalTimeMidnight() {
int year = 2017;
Month month = Month.MAY;
int day = 28;
int hour = 0;
int minute = 0;
LocalDateTime now = LocalDateTime.of(year, month.getValue(), day, hour, minute);
ZonedDateTime zdt = ZonedDateTime.of(now, TimeUtil.PT_ZONE);
long time = TimeUnit.SECONDS.toMicros(zdt.toEpochSecond());
long canonicalTime = VtsProfilingStatsJobServlet.getCanonicalTime(time);
long canonicalTimeSec = TimeUnit.MICROSECONDS.toSeconds(canonicalTime);
ZonedDateTime canonical =
ZonedDateTime.ofInstant(Instant.ofEpochSecond(canonicalTimeSec), TimeUtil.PT_ZONE);
assertEquals(zdt, canonical);
}
/** Test that new summaries are created with a clean database. */
@Test
public void testNewSummary() {
Date d = new Date();
long time = TimeUnit.MILLISECONDS.toMicros(d.getTime());
String test = "test";
Key testKey = KeyFactory.createKey(TestEntity.KIND, test);
Key testRunKey = KeyFactory.createKey(testKey, TestRunEntity.KIND, time);
Long[] valueArray = new Long[] {1l, 2l, 3l, 4l, 5l};
StatSummary expected =
new StatSummary(
"expected",
VtsReportMessage.VtsProfilingRegressionMode.UNKNOWN_REGRESSION_MODE);
for (long value : valueArray) {
expected.updateStats(value);
}
Mean mean = new Mean();
List<Long> values = Arrays.asList(valueArray);
ProfilingPointRunEntity profilingPointRunEntity =
new ProfilingPointRunEntity(
testRunKey,
"profilingPoint",
VtsReportMessage.VtsProfilingType.VTS_PROFILING_TYPE_UNLABELED_VECTOR_VALUE,
VtsReportMessage.VtsProfilingRegressionMode
.VTS_REGRESSION_MODE_INCREASING_VALUE,
null,
values,
"xLabel",
"yLabel",
null);
DeviceInfoEntity device =
new DeviceInfoEntity(
testRunKey, "master", "product", "flavor", "12345", "64", "abi");
List<DeviceInfoEntity> devices = new ArrayList<>();
devices.add(device);
boolean result =
VtsProfilingStatsJobServlet.updateSummaries(
testKey, profilingPointRunEntity, devices, time);
assertTrue(result);
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
// Check profiling point entity
Key profilingPointKey = ProfilingPointEntity.createKey(test, profilingPointRunEntity.getName());
ProfilingPointEntity profilingPointEntity = null;
try {
Entity profilingPoint = datastore.get(profilingPointKey);
profilingPointEntity = ProfilingPointEntity.fromEntity(profilingPoint);
} catch (EntityNotFoundException exception) {
fail();
}
assertNotNull(profilingPointEntity);
assertEquals(profilingPointRunEntity.getName(), profilingPointEntity.getProfilingPointName());
assertEquals(profilingPointRunEntity.getXLabel(), profilingPointEntity.getXLabel());
assertEquals(profilingPointRunEntity.getYLabel(), profilingPointEntity.getYLabel());
assertEquals(profilingPointRunEntity.getType(), profilingPointEntity.getType());
assertEquals(profilingPointRunEntity.getRegressionMode(), profilingPointEntity.getRegressionMode());
// Check all summary entities
Query q = new Query(ProfilingPointSummaryEntity.KIND).setAncestor(profilingPointKey);
for (Entity e : datastore.prepare(q).asIterable()) {
ProfilingPointSummaryEntity pps = ProfilingPointSummaryEntity.fromEntity(e);
assertNotNull(pps);
assertTrue(
pps.getBranch().equals(device.getBranch())
|| pps.getBranch().equals(ProfilingPointSummaryEntity.ALL));
assertTrue(
pps.getBuildFlavor().equals(ProfilingPointSummaryEntity.ALL)
|| pps.getBuildFlavor().equals(device.getBuildFlavor()));
assertEquals(expected.getCount(), pps.getGlobalStats().getCount());
assertEquals(expected.getMax(), pps.getGlobalStats().getMax(), THRESHOLD);
assertEquals(expected.getMin(), pps.getGlobalStats().getMin(), THRESHOLD);
assertEquals(expected.getMean(), pps.getGlobalStats().getMean(), THRESHOLD);
assertEquals(expected.getSumSq(), pps.getGlobalStats().getSumSq(), THRESHOLD);
}
}
/** Test that existing summaries are updated correctly when a job pushes new profiling data. */
@Test
public void testUpdateSummary() {
Date d = new Date();
long time = TimeUnit.MILLISECONDS.toMicros(d.getTime());
String test = "test2";
Key testKey = KeyFactory.createKey(TestEntity.KIND, test);
Key testRunKey = KeyFactory.createKey(testKey, TestRunEntity.KIND, time);
Long[] valueArray = new Long[] {0l};
List<Long> values = Arrays.asList(valueArray);
// Create a new profiling point run
ProfilingPointRunEntity profilingPointRunEntity =
new ProfilingPointRunEntity(
testRunKey,
"profilingPoint2",
VtsReportMessage.VtsProfilingType.VTS_PROFILING_TYPE_UNLABELED_VECTOR_VALUE,
VtsReportMessage.VtsProfilingRegressionMode
.VTS_REGRESSION_MODE_INCREASING_VALUE,
null,
values,
"xLabel",
"yLabel",
null);
// Create a device for the run
String series = "";
DeviceInfoEntity device =
new DeviceInfoEntity(
testRunKey, "master", "product", "flavor", "12345", "64", "abi");
List<DeviceInfoEntity> devices = new ArrayList<>();
devices.add(device);
// Create the existing stats
Key profilingPointKey = ProfilingPointEntity.createKey(test, profilingPointRunEntity.getName());
StatSummary expected =
new StatSummary(
"label",
0,
10,
5,
100,
10,
VtsReportMessage.VtsProfilingRegressionMode.UNKNOWN_REGRESSION_MODE);
ProfilingPointSummaryEntity summary =
new ProfilingPointSummaryEntity(
profilingPointKey,
expected,
new ArrayList<>(),
new HashMap<>(),
device.getBranch(),
device.getBuildFlavor(),
series,
time);
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
datastore.put(summary.toEntity());
// Update the summaries in the database
boolean result =
VtsProfilingStatsJobServlet.updateSummaries(
testKey, profilingPointRunEntity, devices, time);
assertTrue(result);
// Calculate the expected stats with the values from the new run
for (long value : values) expected.updateStats(value);
// Get the summary and check the values match what is expected
Key summaryKey =
ProfilingPointSummaryEntity.createKey(
profilingPointKey, device.getBranch(), device.getBuildFlavor(), series, time);
ProfilingPointSummaryEntity pps = null;
try {
Entity e = datastore.get(summaryKey);
pps = ProfilingPointSummaryEntity.fromEntity(e);
} catch (EntityNotFoundException e) {
fail();
}
assertNotNull(pps);
assertTrue(pps.getBranch().equals(device.getBranch()));
assertTrue(pps.getBuildFlavor().equals(device.getBuildFlavor()));
assertEquals(expected.getCount(), pps.getGlobalStats().getCount());
assertEquals(expected.getMax(), pps.getGlobalStats().getMax(), THRESHOLD);
assertEquals(expected.getMin(), pps.getGlobalStats().getMin(), THRESHOLD);
assertEquals(expected.getMean(), pps.getGlobalStats().getMean(), THRESHOLD);
assertEquals(expected.getSumSq(), pps.getGlobalStats().getSumSq(), THRESHOLD);
}
}