1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.test;
17
18import android.net.NetworkStats;
19import android.net.TrafficStats;
20import android.os.Bundle;
21import android.util.Log;
22
23import java.lang.reflect.InvocationTargetException;
24import java.lang.reflect.Method;
25import java.lang.reflect.Modifier;
26
27/**
28 * A bandwidth test case that collects bandwidth statistics for tests that are
29 * annotated with {@link BandwidthTest} otherwise the test is executed
30 * as an {@link InstrumentationTestCase}
31 */
32public class BandwidthTestCase extends InstrumentationTestCase {
33    private static final String TAG = "BandwidthTestCase";
34    private static final String REPORT_KEY_PACKETS_SENT = "txPackets";
35    private static final String REPORT_KEY_PACKETS_RECEIVED = "rxPackets";
36    private static final String REPORT_KEY_BYTES_SENT = "txBytes";
37    private static final String REPORT_KEY_BYTES_RECEIVED = "rxBytes";
38    private static final String REPORT_KEY_OPERATIONS = "operations";
39
40    @Override
41    protected void runTest() throws Throwable {
42        //This is a copy of {@link InstrumentationTestCase#runTest} with
43        //added logic to handle bandwidth measurements
44        String fName = getName();
45        assertNotNull(fName);
46        Method method = null;
47        Class testClass = null;
48        try {
49            // use getMethod to get all public inherited
50            // methods. getDeclaredMethods returns all
51            // methods of this class but excludes the
52            // inherited ones.
53            testClass = getClass();
54            method = testClass.getMethod(fName, (Class[]) null);
55        } catch (NoSuchMethodException e) {
56            fail("Method \""+fName+"\" not found");
57        }
58
59        if (!Modifier.isPublic(method.getModifiers())) {
60            fail("Method \""+fName+"\" should be public");
61        }
62
63        int runCount = 1;
64        boolean isRepetitive = false;
65        if (method.isAnnotationPresent(FlakyTest.class)) {
66            runCount = method.getAnnotation(FlakyTest.class).tolerance();
67        } else if (method.isAnnotationPresent(RepetitiveTest.class)) {
68            runCount = method.getAnnotation(RepetitiveTest.class).numIterations();
69            isRepetitive = true;
70        }
71
72        if (method.isAnnotationPresent(UiThreadTest.class)) {
73            final int tolerance = runCount;
74            final boolean repetitive = isRepetitive;
75            final Method testMethod = method;
76            final Throwable[] exceptions = new Throwable[1];
77            getInstrumentation().runOnMainSync(new Runnable() {
78                public void run() {
79                    try {
80                        runMethod(testMethod, tolerance, repetitive);
81                    } catch (Throwable throwable) {
82                        exceptions[0] = throwable;
83                    }
84                }
85            });
86            if (exceptions[0] != null) {
87                throw exceptions[0];
88            }
89        } else if (method.isAnnotationPresent(BandwidthTest.class) ||
90                testClass.isAnnotationPresent(BandwidthTest.class)) {
91            /**
92             * If bandwidth profiling fails for whatever reason the test
93             * should be allow to execute to its completion.
94             * Typically bandwidth profiling would fail when a lower level
95             * component is missing, such as the kernel module, for a newly
96             * introduced hardware.
97             */
98            try{
99                TrafficStats.startDataProfiling(null);
100            } catch(IllegalStateException isx){
101                Log.w(TAG, "Failed to start bandwidth profiling");
102            }
103            runMethod(method, 1, false);
104            try{
105                NetworkStats stats = TrafficStats.stopDataProfiling(null);
106                NetworkStats.Entry entry = stats.getTotal(null);
107                getInstrumentation().sendStatus(2, getBandwidthStats(entry));
108            } catch (IllegalStateException isx){
109                Log.w(TAG, "Failed to collect bandwidth stats");
110            }
111        } else {
112            runMethod(method, runCount, isRepetitive);
113        }
114    }
115
116    private void runMethod(Method runMethod, int tolerance, boolean isRepetitive) throws Throwable {
117        //This is a copy of {@link InstrumentationTestCase#runMethod}
118        Throwable exception = null;
119
120        int runCount = 0;
121        do {
122            try {
123                runMethod.invoke(this, (Object[]) null);
124                exception = null;
125            } catch (InvocationTargetException e) {
126                e.fillInStackTrace();
127                exception = e.getTargetException();
128            } catch (IllegalAccessException e) {
129                e.fillInStackTrace();
130                exception = e;
131            } finally {
132                runCount++;
133                // Report current iteration number, if test is repetitive
134                if (isRepetitive) {
135                    Bundle iterations = new Bundle();
136                    iterations.putInt("currentiterations", runCount);
137                    getInstrumentation().sendStatus(2, iterations);
138                }
139            }
140        } while ((runCount < tolerance) && (isRepetitive || exception != null));
141
142        if (exception != null) {
143            throw exception;
144        }
145    }
146
147    private Bundle getBandwidthStats(NetworkStats.Entry entry){
148        Bundle bundle = new Bundle();
149        bundle.putLong(REPORT_KEY_BYTES_RECEIVED, entry.rxBytes);
150        bundle.putLong(REPORT_KEY_BYTES_SENT, entry.txBytes);
151        bundle.putLong(REPORT_KEY_PACKETS_RECEIVED, entry.rxPackets);
152        bundle.putLong(REPORT_KEY_PACKETS_SENT, entry.txPackets);
153        bundle.putLong(REPORT_KEY_OPERATIONS, entry.operations);
154        return bundle;
155    }
156}
157
158