| /* |
| * 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 android.cts.statsd.metric; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth.assertWithMessage; |
| |
| import android.cts.statsd.atom.DeviceAtomTestCase; |
| |
| import com.android.internal.os.StatsdConfigProto; |
| import com.android.internal.os.StatsdConfigProto.FieldMatcher; |
| import com.android.internal.os.StatsdConfigProto.Position; |
| import com.android.os.AtomsProto.Atom; |
| import com.android.os.AtomsProto.AppBreadcrumbReported; |
| import com.android.os.AtomsProto.AttributionNode; |
| import com.android.os.AtomsProto.BleScanStateChanged; |
| import com.android.os.StatsLog; |
| import com.android.os.StatsLog.ConfigMetricsReport; |
| import com.android.os.StatsLog.ConfigMetricsReportList; |
| import com.android.os.StatsLog.CountBucketInfo; |
| import com.android.os.StatsLog.CountMetricData; |
| import com.android.os.StatsLog.StatsLogReport; |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.log.LogUtil; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| |
| public class CountMetricsTests extends DeviceAtomTestCase { |
| |
| public void testSimpleEventCountMetric() throws Exception { |
| if (statsdDisabled()) { |
| return; |
| } |
| int matcherId = 1; |
| StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder(); |
| builder.addCountMetric(StatsdConfigProto.CountMetric.newBuilder() |
| .setId(MetricsUtils.COUNT_METRIC_ID) |
| .setBucket(StatsdConfigProto.TimeUnit.CTS) |
| .setWhat(matcherId)) |
| .addAtomMatcher(MetricsUtils.simpleAtomMatcher(matcherId)); |
| uploadConfig(builder); |
| |
| doAppBreadcrumbReportedStart(0); |
| doAppBreadcrumbReportedStop(0); |
| Thread.sleep(2000); // Wait for the metrics to propagate to statsd. |
| |
| StatsLogReport metricReport = getStatsLogReport(); |
| LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString()); |
| assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID); |
| assertThat(metricReport.hasCountMetrics()).isTrue(); |
| |
| StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics(); |
| |
| assertThat(countData.getDataCount()).isGreaterThan(0); |
| assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2); |
| } |
| public void testEventCountWithCondition() throws Exception { |
| if (statsdDisabled()) { |
| return; |
| } |
| int startMatcherId = 1; |
| int endMatcherId = 2; |
| int whatMatcherId = 3; |
| int conditionId = 4; |
| |
| StatsdConfigProto.AtomMatcher whatMatcher = |
| MetricsUtils.unspecifiedAtomMatcher(whatMatcherId); |
| |
| StatsdConfigProto.AtomMatcher predicateStartMatcher = |
| MetricsUtils.startAtomMatcher(startMatcherId); |
| |
| StatsdConfigProto.AtomMatcher predicateEndMatcher = |
| MetricsUtils.stopAtomMatcher(endMatcherId); |
| |
| StatsdConfigProto.Predicate p = StatsdConfigProto.Predicate.newBuilder() |
| .setSimplePredicate(StatsdConfigProto.SimplePredicate.newBuilder() |
| .setStart(startMatcherId) |
| .setStop(endMatcherId) |
| .setCountNesting(false)) |
| .setId(conditionId) |
| .build(); |
| |
| StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder() |
| .addCountMetric(StatsdConfigProto.CountMetric.newBuilder() |
| .setId(MetricsUtils.COUNT_METRIC_ID) |
| .setBucket(StatsdConfigProto.TimeUnit.CTS) |
| .setWhat(whatMatcherId) |
| .setCondition(conditionId)) |
| .addAtomMatcher(whatMatcher) |
| .addAtomMatcher(predicateStartMatcher) |
| .addAtomMatcher(predicateEndMatcher) |
| .addPredicate(p); |
| |
| uploadConfig(builder); |
| |
| doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal()); |
| Thread.sleep(10); |
| doAppBreadcrumbReportedStart(0); |
| Thread.sleep(10); |
| doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal()); |
| Thread.sleep(10); |
| doAppBreadcrumbReportedStop(0); |
| Thread.sleep(10); |
| doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal()); |
| Thread.sleep(2000); // Wait for the metrics to propagate to statsd. |
| |
| StatsLogReport metricReport = getStatsLogReport(); |
| assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID); |
| assertThat(metricReport.hasCountMetrics()).isTrue(); |
| |
| StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics(); |
| |
| assertThat(countData.getDataCount()).isGreaterThan(0); |
| assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(1); |
| } |
| |
| public void testEventCountWithConditionAndActivation() throws Exception { |
| if (statsdDisabled()) { |
| return; |
| } |
| int startMatcherId = 1; |
| int startMatcherLabel = 1; |
| int endMatcherId = 2; |
| int endMatcherLabel = 2; |
| int whatMatcherId = 3; |
| int whatMatcherLabel = 3; |
| int conditionId = 4; |
| int activationMatcherId = 5; |
| int activationMatcherLabel = 5; |
| int ttlSec = 5; |
| |
| StatsdConfigProto.AtomMatcher whatMatcher = |
| MetricsUtils.appBreadcrumbMatcherWithLabel(whatMatcherId, whatMatcherLabel); |
| |
| StatsdConfigProto.AtomMatcher predicateStartMatcher = |
| MetricsUtils.startAtomMatcherWithLabel(startMatcherId, startMatcherLabel); |
| |
| StatsdConfigProto.AtomMatcher predicateEndMatcher = |
| MetricsUtils.stopAtomMatcherWithLabel(endMatcherId, endMatcherLabel); |
| |
| StatsdConfigProto.AtomMatcher activationMatcher = |
| MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId, |
| activationMatcherLabel); |
| |
| StatsdConfigProto.Predicate p = StatsdConfigProto.Predicate.newBuilder() |
| .setSimplePredicate(StatsdConfigProto.SimplePredicate.newBuilder() |
| .setStart(startMatcherId) |
| .setStop(endMatcherId) |
| .setCountNesting(false)) |
| .setId(conditionId) |
| .build(); |
| |
| StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder() |
| .addCountMetric(StatsdConfigProto.CountMetric.newBuilder() |
| .setId(MetricsUtils.COUNT_METRIC_ID) |
| .setBucket(StatsdConfigProto.TimeUnit.CTS) |
| .setWhat(whatMatcherId) |
| .setCondition(conditionId) |
| ) |
| .addAtomMatcher(whatMatcher) |
| .addAtomMatcher(predicateStartMatcher) |
| .addAtomMatcher(predicateEndMatcher) |
| .addAtomMatcher(activationMatcher) |
| .addPredicate(p) |
| .addMetricActivation(StatsdConfigProto.MetricActivation.newBuilder() |
| .setMetricId(MetricsUtils.COUNT_METRIC_ID) |
| .setActivationType(StatsdConfigProto.ActivationType.ACTIVATE_IMMEDIATELY) |
| .addEventActivation(StatsdConfigProto.EventActivation.newBuilder() |
| .setAtomMatcherId(activationMatcherId) |
| .setTtlSeconds(ttlSec))); |
| |
| uploadConfig(builder); |
| |
| // Activate the metric. |
| doAppBreadcrumbReported(activationMatcherLabel); |
| Thread.sleep(10); |
| |
| // Set the condition to true. |
| doAppBreadcrumbReportedStart(startMatcherLabel); |
| Thread.sleep(10); |
| |
| // Log an event that should be counted. Bucket 1 Count 1. |
| doAppBreadcrumbReported(whatMatcherLabel); |
| Thread.sleep(10); |
| |
| // Log an event that should be counted. Bucket 1 Count 2. |
| doAppBreadcrumbReported(whatMatcherLabel); |
| Thread.sleep(10); |
| |
| // Set the condition to false. |
| doAppBreadcrumbReportedStop(endMatcherLabel); |
| Thread.sleep(10); |
| |
| // Log an event that should not be counted because condition is false. |
| doAppBreadcrumbReported(whatMatcherLabel); |
| Thread.sleep(10); |
| |
| // Let the metric deactivate. |
| Thread.sleep(ttlSec * 1000); |
| |
| // Log an event that should not be counted. |
| doAppBreadcrumbReported(whatMatcherLabel); |
| Thread.sleep(10); |
| |
| // Condition to true again. |
| doAppBreadcrumbReportedStart(startMatcherLabel); |
| Thread.sleep(10); |
| |
| // Event should not be counted, metric is still not active. |
| doAppBreadcrumbReported(whatMatcherLabel); |
| Thread.sleep(10); |
| |
| // Activate the metric. |
| doAppBreadcrumbReported(activationMatcherLabel); |
| Thread.sleep(10); |
| |
| // Log an event that should be counted. |
| doAppBreadcrumbReported(whatMatcherLabel); |
| Thread.sleep(10); |
| |
| // Let the metric deactivate. |
| Thread.sleep(ttlSec * 1000); |
| |
| // Log an event that should not be counted. |
| doAppBreadcrumbReported(whatMatcherLabel); |
| Thread.sleep(10); |
| |
| // Wait for the metrics to propagate to statsd. |
| Thread.sleep(2000); |
| |
| StatsLogReport metricReport = getStatsLogReport(); |
| assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID); |
| LogUtil.CLog.d("Received the following data: " + metricReport.toString()); |
| assertThat(metricReport.hasCountMetrics()).isTrue(); |
| assertThat(metricReport.getIsActive()).isFalse(); |
| |
| StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics(); |
| assertThat(countData.getDataCount()).isEqualTo(1); |
| assertThat(countData.getData(0).getBucketInfoCount()).isEqualTo(2); |
| assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2); |
| assertThat(countData.getData(0).getBucketInfo(1).getCount()).isEqualTo(1); |
| } |
| |
| public void testPartialBucketCountMetric() throws Exception { |
| if (statsdDisabled()) { |
| return; |
| } |
| int matcherId = 1; |
| StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder(); |
| builder.addCountMetric(StatsdConfigProto.CountMetric.newBuilder() |
| .setId(MetricsUtils.COUNT_METRIC_ID) |
| .setBucket(StatsdConfigProto.TimeUnit.ONE_DAY) // Should ensure partial bucket. |
| .setWhat(matcherId)) |
| .addAtomMatcher(MetricsUtils.simpleAtomMatcher(matcherId)); |
| uploadConfig(builder); |
| |
| doAppBreadcrumbReportedStart(0); |
| |
| builder.getCountMetricBuilder(0).setBucket(StatsdConfigProto.TimeUnit.CTS); |
| uploadConfig(builder); // The count metric had a partial bucket. |
| doAppBreadcrumbReportedStart(0); |
| Thread.sleep(10); |
| doAppBreadcrumbReportedStart(0); |
| Thread.sleep(WAIT_TIME_LONG); // Finish the current bucket. |
| |
| ConfigMetricsReportList reports = getReportList(); |
| LogUtil.CLog.d("Got following report list: " + reports.toString()); |
| |
| assertThat(reports.getReportsCount()).isEqualTo(2); |
| boolean inOrder = reports.getReports(0).getCurrentReportWallClockNanos() < |
| reports.getReports(1).getCurrentReportWallClockNanos(); |
| |
| // Only 1 metric, so there should only be 1 StatsLogReport. |
| for (ConfigMetricsReport report : reports.getReportsList()) { |
| assertThat(report.getMetricsCount()).isEqualTo(1); |
| assertThat(report.getMetrics(0).getCountMetrics().getDataCount()).isEqualTo(1); |
| } |
| CountMetricData data1 = |
| reports.getReports(inOrder? 0 : 1).getMetrics(0).getCountMetrics().getData(0); |
| CountMetricData data2 = |
| reports.getReports(inOrder? 1 : 0).getMetrics(0).getCountMetrics().getData(0); |
| // Data1 should have only 1 bucket, and it should be a partial bucket. |
| // The count should be 1. |
| assertThat(data1.getBucketInfoCount()).isEqualTo(1); |
| CountBucketInfo bucketInfo = data1.getBucketInfo(0); |
| assertThat(bucketInfo.getCount()).isEqualTo(1); |
| assertWithMessage("First report's bucket should be less than 1 day") |
| .that(bucketInfo.getEndBucketElapsedNanos()) |
| .isLessThan(bucketInfo.getStartBucketElapsedNanos() + |
| 1_000_000_000L * 60L * 60L * 24L); |
| |
| //Second report should have a count of 2. |
| assertThat(data2.getBucketInfoCount()).isAtMost(2); |
| int totalCount = 0; |
| for (CountBucketInfo bucket : data2.getBucketInfoList()) { |
| totalCount += bucket.getCount(); |
| } |
| assertThat(totalCount).isEqualTo(2); |
| } |
| |
| public void testSlicedStateCountMetric() throws Exception { |
| if (statsdDisabled()) { |
| return; |
| } |
| if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return; |
| |
| int whatMatcherId = 3; |
| int stateId = 4; |
| |
| // Atom 9999 { |
| // repeated AttributionNode attribution_node = 1; |
| // optional bool is_filtered = 2; |
| // optional bool is_first_match = 3; |
| // optional bool is_opportunistic = 4; |
| // } |
| int whatAtomId = 9_999; |
| |
| StatsdConfigProto.AtomMatcher whatMatcher = |
| MetricsUtils.getAtomMatcher(whatAtomId) |
| .setId(whatMatcherId) |
| .build(); |
| |
| StatsdConfigProto.State state = StatsdConfigProto.State.newBuilder() |
| .setId(stateId) |
| .setAtomId(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER) |
| .build(); |
| |
| StatsdConfigProto.MetricStateLink stateLink = StatsdConfigProto.MetricStateLink.newBuilder() |
| .setStateAtomId(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER) |
| .setFieldsInWhat(FieldMatcher.newBuilder() |
| .setField(whatAtomId) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(1) |
| .setPosition(Position.FIRST) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(AttributionNode.UID_FIELD_NUMBER) |
| ) |
| ) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(2) |
| ) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(3) |
| ) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(4) |
| ) |
| ) |
| .setFieldsInState(FieldMatcher.newBuilder() |
| .setField(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(BleScanStateChanged.ATTRIBUTION_NODE_FIELD_NUMBER) |
| .setPosition(Position.FIRST) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(AttributionNode.UID_FIELD_NUMBER) |
| ) |
| ) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(BleScanStateChanged.IS_FILTERED_FIELD_NUMBER) |
| ) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(BleScanStateChanged.IS_FIRST_MATCH_FIELD_NUMBER) |
| ) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(BleScanStateChanged.IS_OPPORTUNISTIC_FIELD_NUMBER) |
| ) |
| ) |
| .build(); |
| |
| StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder() |
| .addCountMetric(StatsdConfigProto.CountMetric.newBuilder() |
| .setId(MetricsUtils.COUNT_METRIC_ID) |
| .setBucket(StatsdConfigProto.TimeUnit.CTS) |
| .setWhat(whatMatcherId) |
| .addSliceByState(stateId) |
| .addStateLink(stateLink) |
| ) |
| .addAtomMatcher(whatMatcher) |
| .addState(state); |
| uploadConfig(builder); |
| |
| runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testBleScanInterrupted"); |
| |
| StatsLogReport metricReport = getStatsLogReport(); |
| LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString()); |
| assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID); |
| assertThat(metricReport.hasCountMetrics()).isTrue(); |
| |
| StatsLogReport.CountMetricDataWrapper dataWrapper = metricReport.getCountMetrics(); |
| assertThat(dataWrapper.getDataCount()).isEqualTo(2); |
| |
| CountMetricData data = dataWrapper.getData(0); |
| assertThat(data.getSliceByStateCount()).isEqualTo(1); |
| assertThat(data.getSliceByState(0).getAtomId()) |
| .isEqualTo(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER); |
| assertThat(data.getSliceByState(0).getValue()) |
| .isEqualTo(BleScanStateChanged.State.OFF.ordinal()); |
| long totalCount = data.getBucketInfoList().stream() |
| .mapToLong(CountBucketInfo::getCount) |
| .sum(); |
| assertThat(totalCount).isEqualTo(3); |
| |
| data = dataWrapper.getData(1); |
| assertThat(data.getSliceByStateCount()).isEqualTo(1); |
| assertThat(data.getSliceByState(0).getAtomId()) |
| .isEqualTo(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER); |
| assertThat(data.getSliceByState(0).getValue()) |
| .isEqualTo(BleScanStateChanged.State.ON.ordinal()); |
| totalCount = data.getBucketInfoList().stream() |
| .mapToLong(CountBucketInfo::getCount) |
| .sum(); |
| assertThat(totalCount).isEqualTo(2); |
| } |
| } |