diff options
7 files changed, 789 insertions, 143 deletions
diff --git a/cmds/statsd/tools/localtools/Android.bp b/cmds/statsd/tools/localtools/Android.bp index 75a57a3f3068..69a43a8f4712 100644 --- a/cmds/statsd/tools/localtools/Android.bp +++ b/cmds/statsd/tools/localtools/Android.bp @@ -11,9 +11,8 @@ java_binary_host { ], } -java_binary_host { - name: "statsd_testdrive", - manifest: "testdrive_manifest.txt", +java_library_host { + name: "statsd_testdrive_lib", srcs: [ "src/com/android/statsd/shelltools/testdrive/*.java", "src/com/android/statsd/shelltools/Utils.java", @@ -22,4 +21,26 @@ java_binary_host { "platformprotos", "guava", ], -}
\ No newline at end of file +} + + +java_binary_host { + name: "statsd_testdrive", + manifest: "testdrive_manifest.txt", + static_libs: [ + "statsd_testdrive_lib", + ], +} + +java_test_host { + name: "statsd_testdrive_test", + test_suites: ["general-tests"], + srcs: ["test/com/android/statsd/shelltools/testdrive/*.java"], + static_libs: [ + "statsd_testdrive_lib", + "junit", + "platformprotos", + "guava", + ], +} + diff --git a/cmds/statsd/tools/localtools/TEST_MAPPING b/cmds/statsd/tools/localtools/TEST_MAPPING new file mode 100644 index 000000000000..7c8a3db5c610 --- /dev/null +++ b/cmds/statsd/tools/localtools/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + "presubmit": [ + { + "name": "statsd_testdrive_test", + "host": true + } + ] +} diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java index 2909048da7ea..6a74480b505e 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java @@ -229,4 +229,56 @@ public class Utils { } return null; } + + /** + * Returns ANDROID_SERIAL environment variable, or null if that is undefined or unavailable. + * @param logger Destination of error messages. + * @return String value of ANDROID_SERIAL environment variable, or null. + */ + public static String getDefaultDevice(Logger logger) { + try { + return System.getenv("ANDROID_SERIAL"); + } catch (Exception ex) { + logger.log(Level.SEVERE, "Failed to check ANDROID_SERIAL environment variable.", + ex); + } + return null; + } + + /** + * Returns the device to use if one can be deduced, or null. + * @param device Command-line specified device, or null. + * @param connectedDevices List of all connected devices. + * @param defaultDevice Environment-variable specified device, or null. + * @param logger Destination of error messages. + * @return Device to use, or null. + */ + public static String chooseDevice(String device, List<String> connectedDevices, + String defaultDevice, Logger logger) { + if (connectedDevices == null || connectedDevices.isEmpty()) { + logger.severe("No connected device."); + return null; + } + if (device != null) { + if (connectedDevices.contains(device)) { + return device; + } + logger.severe("Device not connected: " + device); + return null; + } + if (connectedDevices.size() == 1) { + return connectedDevices.get(0); + } + if (defaultDevice != null) { + if (connectedDevices.contains(defaultDevice)) { + return defaultDevice; + } else { + logger.severe("ANDROID_SERIAL device is not connected: " + defaultDevice); + return null; + } + } + logger.severe("More than one device is connected. Choose one" + + " with -s DEVICE_SERIAL or environment variable ANDROID_SERIAL."); + return null; + } } diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java index 7db514180b9a..ec3c7df7bfba 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java @@ -27,7 +27,6 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.List; -import java.util.logging.Level; import java.util.logging.Logger; /** @@ -112,17 +111,9 @@ public class LocalDrive { } List<String> connectedDevices = Utils.getDeviceSerials(sLogger); - if (connectedDevices == null || connectedDevices.size() == 0) { - sLogger.log(Level.SEVERE, "No device connected."); - return; - } - if (connectedDevices.size() == 1 && deviceSerial == null) { - deviceSerial = connectedDevices.get(0); - } - + deviceSerial = Utils.chooseDevice(deviceSerial, connectedDevices, + Utils.getDefaultDevice(sLogger), sLogger); if (deviceSerial == null) { - sLogger.log(Level.SEVERE, "More than one devices connected. Please specify" - + " with -s DEVICE_SERIAL"); return; } diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java index 54a744b654cb..8ac251e39a18 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java @@ -15,6 +15,7 @@ */ package com.android.statsd.shelltools.testdrive; +import com.android.internal.os.StatsdConfigProto; import com.android.internal.os.StatsdConfigProto.AtomMatcher; import com.android.internal.os.StatsdConfigProto.EventMetric; import com.android.internal.os.StatsdConfigProto.FieldFilter; @@ -29,6 +30,7 @@ import com.android.os.StatsLog.ConfigMetricsReportList; import com.android.os.StatsLog.StatsLogReport; import com.android.statsd.shelltools.Utils; +import com.google.common.annotations.VisibleForTesting; import com.google.common.io.Files; import java.io.File; @@ -39,6 +41,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; @@ -73,179 +76,269 @@ public class TestDrive { }; private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName()); - private String mAdditionalAllowedPackage; - private String mDeviceSerial; - private final Set<Long> mTrackedMetrics = new HashSet<>(); + @VisibleForTesting + String mDeviceSerial = null; public static void main(String[] args) { + final Configuration configuration = new Configuration(); + TestDrive testDrive = new TestDrive(); - Set<Integer> trackedAtoms = new HashSet<>(); Utils.setUpLogger(LOGGER, false); - String remoteConfigPath = null; - if (args.length < 1) { - LOGGER.log(Level.SEVERE, "Usage: ./test_drive [-p additional_allowed_package] " - + "[-s DEVICE_SERIAL_NUMBER]" - + "<atomId1> <atomId2> ... <atomIdN>"); + if (!testDrive.processArgs(configuration, args, + Utils.getDeviceSerials(LOGGER), Utils.getDefaultDevice(LOGGER))) { return; } - List<String> connectedDevices = Utils.getDeviceSerials(LOGGER); - if (connectedDevices == null || connectedDevices.size() == 0) { - LOGGER.log(Level.SEVERE, "No device connected."); - return; + final ConfigMetricsReportList reports = testDrive.testDriveAndGetReports( + configuration.createConfig(), configuration.hasPulledAtoms(), + configuration.hasPushedAtoms()); + if (reports != null) { + configuration.dumpMetrics(reports); } + } - int arg_index = 0; - while (arg_index < args.length) { - String arg = args[arg_index]; - if (arg.equals("-p")) { - testDrive.mAdditionalAllowedPackage = args[++arg_index]; - } else if (arg.equals("-s")) { - testDrive.mDeviceSerial = args[++arg_index]; - } else { - break; - } - arg_index++; + boolean processArgs(Configuration configuration, String[] args, List<String> connectedDevices, + String defaultDevice) { + if (args.length < 1) { + LOGGER.severe("Usage: ./test_drive [-one] " + + "[-p additional_allowed_package] " + + "[-s DEVICE_SERIAL_NUMBER] " + + "<atomId1> <atomId2> ... <atomIdN>"); + return false; } - if (connectedDevices.size() == 1 && testDrive.mDeviceSerial == null) { - testDrive.mDeviceSerial = connectedDevices.get(0); + int first_arg = 0; + // Consume all flags, which must precede all atoms + for (; first_arg < args.length; ++first_arg) { + String arg = args[first_arg]; + int remaining_args = args.length - first_arg; + if (remaining_args >= 2 && arg.equals("-one")) { + LOGGER.info("Creating one event metric to catch all pushed atoms."); + configuration.mOnePushedAtomEvent = true; + } else if (remaining_args >= 3 && arg.equals("-p")) { + configuration.mAdditionalAllowedPackage = args[++first_arg]; + } else if (remaining_args >= 3 && arg.equals("-s")) { + mDeviceSerial = args[++first_arg]; + } else { + break; // Found the atom list + } } - if (testDrive.mDeviceSerial == null) { - LOGGER.log(Level.SEVERE, "More than one devices connected. Please specify" - + " with -s DEVICE_SERIAL"); - return; + mDeviceSerial = Utils.chooseDevice(mDeviceSerial, connectedDevices, defaultDevice, LOGGER); + if (mDeviceSerial == null) { + return false; } - for (int i = arg_index; i < args.length; i++) { + for ( ; first_arg < args.length; ++first_arg) { + String atom = args[first_arg]; try { - int atomId = Integer.valueOf(args[i]); - if (Atom.getDescriptor().findFieldByNumber(atomId) == null) { - LOGGER.log(Level.SEVERE, "No such atom found: " + args[i]); - continue; - } - trackedAtoms.add(atomId); + configuration.addAtom(Integer.valueOf(atom)); } catch (NumberFormatException e) { - LOGGER.log(Level.SEVERE, "Bad atom id provided: " + args[i]); - continue; + LOGGER.severe("Bad atom id provided: " + atom); } } + return configuration.hasPulledAtoms() || configuration.hasPushedAtoms(); + } + + private ConfigMetricsReportList testDriveAndGetReports(StatsdConfig config, + boolean hasPulledAtoms, boolean hasPushedAtoms) { + if (config == null) { + LOGGER.severe("Failed to create valid config."); + return null; + } + + String remoteConfigPath = null; try { - StatsdConfig config = testDrive.createConfig(trackedAtoms); - if (config == null) { - LOGGER.log(Level.SEVERE, "Failed to create valid config."); - return; - } - remoteConfigPath = testDrive.pushConfig(config, testDrive.mDeviceSerial); - LOGGER.info("Pushed the following config to statsd:"); + remoteConfigPath = pushConfig(config, mDeviceSerial); + LOGGER.info("Pushed the following config to statsd on device '" + mDeviceSerial + + "':"); LOGGER.info(config.toString()); - if (!hasPulledAtom(trackedAtoms)) { + if (hasPushedAtoms) { + LOGGER.info("Now please play with the device to trigger the event."); + } + if (!hasPulledAtoms) { LOGGER.info( - "Now please play with the device to trigger the event. All events should " - + "be dumped after 1 min ..."); + "All events should be dumped after 1 min ..."); Thread.sleep(60_000); } else { - LOGGER.info("Now wait for 1.5 minutes ..."); + LOGGER.info("All events should be dumped after 1.5 minutes ..."); Thread.sleep(15_000); - Utils.logAppBreadcrumb(0, 0, LOGGER, testDrive.mDeviceSerial); + Utils.logAppBreadcrumb(0, 0, LOGGER, mDeviceSerial); Thread.sleep(75_000); } - testDrive.dumpMetrics(); + return Utils.getReportList(CONFIG_ID, true, false, LOGGER, + mDeviceSerial); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e); } finally { - testDrive.removeConfig(testDrive.mDeviceSerial); + removeConfig(mDeviceSerial); if (remoteConfigPath != null) { try { Utils.runCommand(null, LOGGER, - "adb", "-s", testDrive.mDeviceSerial, "shell", "rm", remoteConfigPath); + "adb", "-s", mDeviceSerial, "shell", "rm", + remoteConfigPath); } catch (Exception e) { LOGGER.log(Level.WARNING, "Unable to remove remote config file: " + remoteConfigPath, e); } } } + return null; } - private void dumpMetrics() throws Exception { - ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER, - mDeviceSerial); - // We may get multiple reports. Take the last one. - ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1); - for (StatsLogReport statsLog : report.getMetricsList()) { - if (mTrackedMetrics.contains(statsLog.getMetricId())) { - LOGGER.info(statsLog.toString()); + static class Configuration { + boolean mOnePushedAtomEvent = false; + @VisibleForTesting + Set<Integer> mPushedAtoms = new TreeSet<>(); + @VisibleForTesting + Set<Integer> mPulledAtoms = new TreeSet<>(); + @VisibleForTesting + String mAdditionalAllowedPackage = null; + private final Set<Long> mTrackedMetrics = new HashSet<>(); + + private void dumpMetrics(ConfigMetricsReportList reportList) { + // We may get multiple reports. Take the last one. + ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1); + for (StatsLogReport statsLog : report.getMetricsList()) { + if (isTrackedMetric(statsLog.getMetricId())) { + LOGGER.info(statsLog.toString()); + } } } - } - private StatsdConfig createConfig(Set<Integer> atomIds) { - long metricId = METRIC_ID_BASE; - long atomMatcherId = ATOM_MATCHER_ID_BASE; + boolean isTrackedMetric(long metricId) { + return mTrackedMetrics.contains(metricId); + } - ArrayList<String> allowedSources = new ArrayList<>(); - Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES); - if (mAdditionalAllowedPackage != null) { - allowedSources.add(mAdditionalAllowedPackage); + static boolean isPulledAtom(int atomId) { + return atomId >= PULL_ATOM_START && atomId <= MAX_PLATFORM_ATOM_TAG + || atomId >= VENDOR_PULLED_ATOM_START_TAG; } - StatsdConfig.Builder builder = StatsdConfig.newBuilder(); - builder - .addAllAllowedLogSource(allowedSources) - .addAllDefaultPullPackages(Arrays.asList(DEFAULT_PULL_SOURCES)) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.GPU_STATS_GLOBAL_INFO_FIELD_NUMBER) - .addPackages("AID_GPU_SERVICE")) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.GPU_STATS_APP_INFO_FIELD_NUMBER) - .addPackages("AID_GPU_SERVICE")) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER) - .addPackages("AID_STATSD")) - .setHashStringsInMetricReport(false); - - if (hasPulledAtom(atomIds)) { - builder.addAtomMatcher( - createAtomMatcher( - Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, APP_BREADCRUMB_MATCHER_ID)); + void addAtom(Integer atom) { + if (Atom.getDescriptor().findFieldByNumber(atom) == null) { + LOGGER.severe("No such atom found: " + atom); + return; + } + if (isPulledAtom(atom)) { + mPulledAtoms.add(atom); + } else { + mPushedAtoms.add(atom); + } } - for (int atomId : atomIds) { - if (isPulledAtom(atomId)) { + private boolean hasPulledAtoms() { + return !mPulledAtoms.isEmpty(); + } + + private boolean hasPushedAtoms() { + return !mPushedAtoms.isEmpty(); + } + + StatsdConfig createConfig() { + long metricId = METRIC_ID_BASE; + long atomMatcherId = ATOM_MATCHER_ID_BASE; + + StatsdConfig.Builder builder = baseBuilder(); + + if (hasPulledAtoms()) { + builder.addAtomMatcher( + createAtomMatcher( + Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, + APP_BREADCRUMB_MATCHER_ID)); + } + + for (int atomId : mPulledAtoms) { builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId)); GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder(); gaugeMetricBuilder - .setId(metricId) - .setWhat(atomMatcherId) - .setTriggerEvent(APP_BREADCRUMB_MATCHER_ID) - .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build()) - .setBucket(TimeUnit.ONE_MINUTE) - .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES) - .setMaxNumGaugeAtomsPerBucket(100); + .setId(metricId) + .setWhat(atomMatcherId) + .setTriggerEvent(APP_BREADCRUMB_MATCHER_ID) + .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build()) + .setBucket(TimeUnit.ONE_MINUTE) + .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES) + .setMaxNumGaugeAtomsPerBucket(100); builder.addGaugeMetric(gaugeMetricBuilder.build()); - } else { + atomMatcherId++; + mTrackedMetrics.add(metricId++); + } + + // A simple atom matcher for each pushed atom. + List<AtomMatcher> simpleAtomMatchers = new ArrayList<>(); + for (int atomId : mPushedAtoms) { + final AtomMatcher atomMatcher = createAtomMatcher(atomId, atomMatcherId++); + simpleAtomMatchers.add(atomMatcher); + builder.addAtomMatcher(atomMatcher); + } + + if (mOnePushedAtomEvent) { + // Create a union event metric, using an matcher that matches all pulled atoms. + AtomMatcher unionAtomMatcher = createUnionMatcher(simpleAtomMatchers, + atomMatcherId); + builder.addAtomMatcher(unionAtomMatcher); EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder(); - eventMetricBuilder - .setId(metricId) - .setWhat(atomMatcherId); + eventMetricBuilder.setId(metricId).setWhat(unionAtomMatcher.getId()); builder.addEventMetric(eventMetricBuilder.build()); - builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId)); + mTrackedMetrics.add(metricId++); + } else { + // Create multiple event metrics, one per pulled atom. + for (AtomMatcher atomMatcher : simpleAtomMatchers) { + EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder(); + eventMetricBuilder + .setId(metricId) + .setWhat(atomMatcher.getId()); + builder.addEventMetric(eventMetricBuilder.build()); + mTrackedMetrics.add(metricId++); + } } - atomMatcherId++; - mTrackedMetrics.add(metricId++); + + return builder.build(); + } + + private static AtomMatcher createAtomMatcher(int atomId, long matcherId) { + AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder(); + atomMatcherBuilder + .setId(matcherId) + .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId)); + return atomMatcherBuilder.build(); } - return builder.build(); - } - private static AtomMatcher createAtomMatcher(int atomId, long matcherId) { - AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder(); - atomMatcherBuilder - .setId(matcherId) - .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId)); - return atomMatcherBuilder.build(); + private AtomMatcher createUnionMatcher(List<AtomMatcher> simpleAtomMatchers, + long atomMatcherId) { + AtomMatcher.Combination.Builder combinationBuilder = + AtomMatcher.Combination.newBuilder(); + combinationBuilder.setOperation(StatsdConfigProto.LogicalOperation.OR); + for (AtomMatcher matcher : simpleAtomMatchers) { + combinationBuilder.addMatcher(matcher.getId()); + } + AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder(); + atomMatcherBuilder.setId(atomMatcherId).setCombination(combinationBuilder.build()); + return atomMatcherBuilder.build(); + } + + private StatsdConfig.Builder baseBuilder() { + ArrayList<String> allowedSources = new ArrayList<>(); + Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES); + if (mAdditionalAllowedPackage != null) { + allowedSources.add(mAdditionalAllowedPackage); + } + return StatsdConfig.newBuilder() + .addAllAllowedLogSource(allowedSources) + .addAllDefaultPullPackages(Arrays.asList(DEFAULT_PULL_SOURCES)) + .addPullAtomPackages(PullAtomPackages.newBuilder() + .setAtomId(Atom.GPU_STATS_GLOBAL_INFO_FIELD_NUMBER) + .addPackages("AID_GPU_SERVICE")) + .addPullAtomPackages(PullAtomPackages.newBuilder() + .setAtomId(Atom.GPU_STATS_APP_INFO_FIELD_NUMBER) + .addPackages("AID_GPU_SERVICE")) + .addPullAtomPackages(PullAtomPackages.newBuilder() + .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER) + .addPackages("AID_STATSD")) + .setHashStringsInMetricReport(false); + } } private static String pushConfig(StatsdConfig config, String deviceSerial) @@ -267,21 +360,7 @@ public class TestDrive { Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial, "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID)); } catch (Exception e) { - LOGGER.log(Level.SEVERE, "Failed to remove config: " + e.getMessage()); - } - } - - private static boolean isPulledAtom(int atomId) { - return atomId >= PULL_ATOM_START && atomId <= MAX_PLATFORM_ATOM_TAG - || atomId >= VENDOR_PULLED_ATOM_START_TAG; - } - - private static boolean hasPulledAtom(Set<Integer> atoms) { - for (Integer i : atoms) { - if (isPulledAtom(i)) { - return true; - } + LOGGER.severe("Failed to remove config: " + e.getMessage()); } - return false; } } diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java new file mode 100644 index 000000000000..b1cc60f74993 --- /dev/null +++ b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2020 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.statsd.shelltools.testdrive; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import com.android.internal.os.StatsdConfigProto; +import com.android.internal.os.StatsdConfigProto.StatsdConfig; +import com.android.os.AtomsProto; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Tests for {@link TestDrive} + */ +public class ConfigurationTest { + + private StatsdConfigProto.AtomMatcher findAndRemoveAtomMatcherById( + List<StatsdConfigProto.AtomMatcher> atomMatchers, long id) { + int numMatches = 0; + StatsdConfigProto.AtomMatcher match = null; + for (StatsdConfigProto.AtomMatcher atomMatcher : atomMatchers) { + if (id == atomMatcher.getId()) { + ++numMatches; + match = atomMatcher; + } + } + if (numMatches == 1) { + atomMatchers.remove(match); + return match; + } + return null; // Too many, or not found + } + + private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration(); + + @Test + public void testOnePushed() { + final int atom = 90; + assertFalse(TestDrive.Configuration.isPulledAtom(atom)); + mConfiguration.addAtom(atom); + StatsdConfig config = mConfiguration.createConfig(); + + //event_metric { + // id: 1111 + // what: 1234567 + //} + //atom_matcher { + // id: 1234567 + // simple_atom_matcher { + // atom_id: 90 + // } + //} + + assertEquals(1, config.getEventMetricCount()); + assertEquals(0, config.getGaugeMetricCount()); + + assertTrue(mConfiguration.isTrackedMetric(config.getEventMetric(0).getId())); + + final List<StatsdConfigProto.AtomMatcher> atomMatchers = + new ArrayList<>(config.getAtomMatcherList()); + assertEquals(atom, + findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat()) + .getSimpleAtomMatcher().getAtomId()); + assertEquals(0, atomMatchers.size()); + } + + @Test + public void testOnePulled() { + final int atom = 10022; + assertTrue(TestDrive.Configuration.isPulledAtom(atom)); + mConfiguration.addAtom(atom); + StatsdConfig config = mConfiguration.createConfig(); + + //gauge_metric { + // id: 1111 + // what: 1234567 + // gauge_fields_filter { + // include_all: true + // } + // bucket: ONE_MINUTE + // sampling_type: FIRST_N_SAMPLES + // max_num_gauge_atoms_per_bucket: 100 + // trigger_event: 1111111 + //} + //atom_matcher { + // id: 1111111 + // simple_atom_matcher { + // atom_id: 47 + // } + //} + //atom_matcher { + // id: 1234567 + // simple_atom_matcher { + // atom_id: 10022 + // } + //} + + assertEquals(0, config.getEventMetricCount()); + assertEquals(1, config.getGaugeMetricCount()); + + assertTrue(mConfiguration.isTrackedMetric(config.getGaugeMetric(0).getId())); + + final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0); + assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll()); + + final List<StatsdConfigProto.AtomMatcher> atomMatchers = + new ArrayList<>(config.getAtomMatcherList()); + assertEquals(atom, + findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat()) + .getSimpleAtomMatcher().getAtomId()); + assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, + findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent()) + .getSimpleAtomMatcher().getAtomId()); + assertEquals(0, atomMatchers.size()); + } + + @Test + public void testOnePulledTwoPushed() { + final int pulledAtom = 10022; + assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom)); + mConfiguration.addAtom(pulledAtom); + + Integer[] pushedAtoms = new Integer[]{244, 245}; + for (int atom : pushedAtoms) { + assertFalse(TestDrive.Configuration.isPulledAtom(atom)); + mConfiguration.addAtom(atom); + } + StatsdConfig config = mConfiguration.createConfig(); + + // event_metric { + // id: 1111 + // what: 1234567 + // } + // event_metric { + // id: 1112 + // what: 1234568 + // } + // gauge_metric { + // id: 1114 + // what: 1234570 + // gauge_fields_filter { + // include_all: true + // } + // bucket: ONE_MINUTE + // sampling_type: FIRST_N_SAMPLES + // max_num_gauge_atoms_per_bucket: 100 + // trigger_event: 1111111 + // } + // atom_matcher { + // id: 1111111 + // simple_atom_matcher { + // atom_id: 47 + // } + // } + // atom_matcher { + // id: 1234567 + // simple_atom_matcher { + // atom_id: 244 + // } + // } + // atom_matcher { + // id: 1234568 + // simple_atom_matcher { + // atom_id: 245 + // } + // } + // atom_matcher { + // id: 1234570 + // simple_atom_matcher { + // atom_id: 10022 + // } + // } + + assertEquals(2, config.getEventMetricCount()); + assertEquals(1, config.getGaugeMetricCount()); + + final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0); + assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId())); + assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll()); + for (StatsdConfigProto.EventMetric eventMetric : config.getEventMetricList()) { + assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId())); + } + + final List<StatsdConfigProto.AtomMatcher> atomMatchers = + new ArrayList<>(config.getAtomMatcherList()); + + assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat()) + .getSimpleAtomMatcher().getAtomId()); + assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, + findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent()) + .getSimpleAtomMatcher().getAtomId()); + + Integer[] actualAtoms = new Integer[]{ + findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat()) + .getSimpleAtomMatcher().getAtomId(), + findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(1).getWhat()) + .getSimpleAtomMatcher().getAtomId()}; + Arrays.sort(actualAtoms); + assertArrayEquals(pushedAtoms, actualAtoms); + + assertEquals(0, atomMatchers.size()); + } + + @Test + public void testOnePulledTwoPushedTogether() { + mConfiguration.mOnePushedAtomEvent = true; // Use one event grabbing all pushed atoms + + final int pulledAtom = 10022; + assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom)); + mConfiguration.addAtom(pulledAtom); + + Integer[] pushedAtoms = new Integer[]{244, 245}; + for (int atom : pushedAtoms) { + assertFalse(TestDrive.Configuration.isPulledAtom(atom)); + mConfiguration.addAtom(atom); + } + StatsdConfig config = mConfiguration.createConfig(); + + // event_metric { + // id: 1112 + // what: 1234570 + // } + // gauge_metric { + // id: 1111 + // what: 1234567 + // gauge_fields_filter { + // include_all: true + // } + // bucket: ONE_MINUTE + // sampling_type: FIRST_N_SAMPLES + // max_num_gauge_atoms_per_bucket: 100 + // trigger_event: 1111111 + // } + // atom_matcher { + // id: 1111111 + // simple_atom_matcher { + // atom_id: 47 + // } + // } + // atom_matcher { + // id: 1234567 + // simple_atom_matcher { + // atom_id: 10022 + // } + // } + // atom_matcher { + // id: 1234568 + // simple_atom_matcher { + // atom_id: 244 + // } + // } + // atom_matcher { + // id: 1234569 + // simple_atom_matcher { + // atom_id: 245 + // } + // } + // atom_matcher { + // id: 1234570 + // combination { + // operation: OR + // matcher: 1234568 + // matcher: 1234569 + // } + // } + + assertEquals(1, config.getEventMetricCount()); + assertEquals(1, config.getGaugeMetricCount()); + + final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0); + assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId())); + assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll()); + + StatsdConfigProto.EventMetric eventMetric = config.getEventMetric(0); + assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId())); + + final List<StatsdConfigProto.AtomMatcher> atomMatchers = + new ArrayList<>(config.getAtomMatcherList()); + + assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat()) + .getSimpleAtomMatcher().getAtomId()); + assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, + findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent()) + .getSimpleAtomMatcher().getAtomId()); + + StatsdConfigProto.AtomMatcher unionMatcher = findAndRemoveAtomMatcherById(atomMatchers, + eventMetric.getWhat()); + assertNotNull(unionMatcher.getCombination()); + assertEquals(2, unionMatcher.getCombination().getMatcherCount()); + + Integer[] actualAtoms = new Integer[]{ + findAndRemoveAtomMatcherById(atomMatchers, + unionMatcher.getCombination().getMatcher(0)) + .getSimpleAtomMatcher().getAtomId(), + findAndRemoveAtomMatcherById(atomMatchers, + unionMatcher.getCombination().getMatcher(1)) + .getSimpleAtomMatcher().getAtomId()}; + Arrays.sort(actualAtoms); + assertArrayEquals(pushedAtoms, actualAtoms); + + assertEquals(0, atomMatchers.size()); + } +} diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java new file mode 100644 index 000000000000..9d986e7c09cf --- /dev/null +++ b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2020 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.statsd.shelltools.testdrive; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Tests for {@link TestDrive} + */ +@RunWith(Parameterized.class) +public class TestDriveTest { + /** + * Expected results of a single iteration of the paramerized test. + */ + static class Expect { + public boolean success; + public Integer[] atoms; + public boolean onePushedAtomEvent; + public String extraPackage; + public String target; + + static Expect success(Integer... atoms) { + return new Expect(true, atoms, false, null, + TARGET); + } + Expect(boolean success, Integer[] atoms, boolean onePushedAtomEvent, String extraPackage, + String target) { + this.success = success; + this.atoms = atoms; + this.onePushedAtomEvent = onePushedAtomEvent; + this.extraPackage = extraPackage; + this.target = target; + } + static final Expect FAILURE = new Expect(false, null, + false, null, null); + Expect onePushedAtomEvent() { + this.onePushedAtomEvent = true; + return this; + } + Expect extraPackage() { + this.extraPackage = TestDriveTest.PACKAGE; + return this; + } + } + + @Parameterized.Parameter(0) + public String[] mArgs; + + @Parameterized.Parameter(1) + public List<String> mConnectedDevices; + + @Parameterized.Parameter(2) + public String mDefaultDevice; + + @Parameterized.Parameter(3) + public Expect mExpect; + + private static final String TARGET = "target"; + private static final List<String> TARGET_AND_OTHER = Arrays.asList("otherDevice", + TARGET); + private static final List<String> TWO_OTHER_DEVICES = Arrays.asList( + "other1", "other2"); + private static final List<String> TARGET_ONLY = Collections.singletonList(TARGET); + private static final List<String> NOT_TARGET = Collections.singletonList("other"); + private static final List<String> NO_DEVICES = Collections.emptyList(); + private static final String PACKAGE = "extraPackage"; + + @Parameterized.Parameters + public static Collection<Object[]> data() { + return Arrays.asList( + new Object[]{new String[]{}, null, null, + Expect.FAILURE}, // Usage explanation + new Object[]{new String[]{"244", "245"}, null, null, + Expect.FAILURE}, // Failure looking up connected devices + new Object[]{new String[]{"244", "245"}, NO_DEVICES, null, + Expect.FAILURE}, // No connected devices + new Object[]{new String[]{"-s", TARGET, "244", "245"}, NOT_TARGET, null, + Expect.FAILURE}, // Wrong device connected + new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, null, + Expect.FAILURE}, // Wrong devices connected + new Object[]{new String[]{"244", "245"}, TARGET_ONLY, null, + Expect.success(244, 245)}, // If only one device connected, guess that one + new Object[]{new String[]{"244", "not_an_atom"}, TARGET_ONLY, null, + Expect.success(244)}, // Ignore non-atoms + new Object[]{new String[]{"not_an_atom"}, TARGET_ONLY, null, + Expect.FAILURE}, // Require at least one atom + new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, TARGET, + Expect.FAILURE}, // ANDROID_SERIAL specifies non-connected target + new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, TARGET, + Expect.success(244, 245)}, // ANDROID_SERIAL specifies a valid target + new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, null, + Expect.FAILURE}, // Two connected devices, no indication of which to use + new Object[]{new String[]{"-one", "244", "245"}, TARGET_ONLY, null, + Expect.success(244, 245).onePushedAtomEvent()}, + new Object[]{new String[]{"-p", PACKAGE, "244", "245"}, TARGET_ONLY, null, + Expect.success(244, 245).extraPackage()}, + new Object[]{new String[]{"-p", PACKAGE, "-one", "244", "245"}, TARGET_ONLY, null, + Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, + new Object[]{new String[]{"-one", "-p", PACKAGE, "244", "245"}, TARGET_ONLY, null, + Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, + new Object[]{new String[]{"-s", TARGET, "-one", "-p", PACKAGE, "244", "245"}, + TARGET_AND_OTHER, null, + Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, + new Object[]{new String[]{"-one", "-s", TARGET, "-p", PACKAGE, "244", "245"}, + TARGET_AND_OTHER, null, + Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, + new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "244", "245"}, + TARGET_AND_OTHER, null, + Expect.success(244, 245).extraPackage().onePushedAtomEvent()} + ); + } + + private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration(); + private final TestDrive mTestDrive = new TestDrive(); + + private static Integer[] collectAtoms(TestDrive.Configuration configuration) { + Integer[] result = new Integer[configuration.mPulledAtoms.size() + + configuration.mPushedAtoms.size()]; + int result_index = 0; + for (Integer atom : configuration.mPushedAtoms) { + result[result_index++] = atom; + } + for (Integer atom : configuration.mPulledAtoms) { + result[result_index++] = atom; + } + Arrays.sort(result); + return result; + } + + @Test + public void testProcessArgs() { + boolean result = mTestDrive.processArgs(mConfiguration, mArgs, mConnectedDevices, + mDefaultDevice); + if (mExpect.success) { + assertTrue(result); + assertArrayEquals(mExpect.atoms, collectAtoms(mConfiguration)); + assertEquals(mExpect.onePushedAtomEvent, mConfiguration.mOnePushedAtomEvent); + assertEquals(mExpect.target, mTestDrive.mDeviceSerial); + } else { + assertFalse(result); + } + } +} |