1/*
2 * Copyright (C) 2011 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 */
16
17package com.android.cts.tradefed.testtype;
18
19import com.android.compatibility.common.util.AbiUtils;
20import com.android.cts.tradefed.build.CtsBuildHelper;
21import com.android.ddmlib.testrunner.ITestRunListener;
22import com.android.tradefed.build.IBuildInfo;
23import com.android.tradefed.device.DeviceNotAvailableException;
24import com.android.tradefed.device.ITestDevice;
25import com.android.tradefed.log.LogUtil.CLog;
26import com.android.tradefed.result.ITestInvocationListener;
27import com.android.tradefed.testtype.IAbi;
28import com.android.tradefed.testtype.IBuildReceiver;
29import com.android.tradefed.testtype.IDeviceTest;
30import com.android.tradefed.testtype.IRemoteTest;
31
32import java.io.File;
33import java.util.ArrayList;
34import java.util.List;
35
36/**
37 * Test runner for native gTests.
38 *
39 * TODO: This is similar to Tradefed's existing GTest, but it doesn't confirm
40 *       each directory segment exists using ddmlib's file service. This was
41 *       a problem since /data is not visible on a user build, but it is
42 *       executable. It's also a lot more verbose when it comes to errors.
43 */
44public class GeeTest implements IBuildReceiver, IDeviceTest, IRemoteTest {
45
46    private static final String NATIVE_TESTS_DIRECTORY = "/data/local/tmp/cts-native-tests";
47    private static final String NATIVE_TESTS_DIRECTORY_TMP = "/data/local/tmp";
48    private static final String ANDROID_PATH_SEPARATOR = "/";
49    private static final String GTEST_FLAG_FILTER = "--gtest_filter=";
50
51    private int mMaxTestTimeMs = 1 * 90 * 1000;
52
53    private CtsBuildHelper mCtsBuild;
54    private ITestDevice mDevice;
55    private IAbi mAbi;
56    private String mExeName;
57
58    private final String mPackageName;
59
60    private String mPositiveFilters = "";
61    private String mNegativeFilters = "";
62
63    public GeeTest(String packageName, String exeName) {
64        mPackageName = packageName;
65        mExeName = exeName;
66    }
67
68    public void setPositiveFilters(String positiveFilters) {
69        mPositiveFilters = positiveFilters;
70    }
71
72    public void setNegativeFilters(String negativeFilters) {
73        mNegativeFilters = negativeFilters;
74    }
75
76    protected String getGTestFilters() {
77        // If both filters are empty or null return empty string.
78        if (mPositiveFilters == null && mNegativeFilters == null) {
79            return "";
80        }
81        if (mPositiveFilters.isEmpty() && mNegativeFilters.isEmpty()) {
82            return "";
83        }
84        // Build filter string.
85        StringBuilder sb = new StringBuilder();
86        sb.append(GTEST_FLAG_FILTER);
87        boolean hasPositiveFilters = false;
88        if (mPositiveFilters != null && !mPositiveFilters.isEmpty()) {
89            sb.append(mPositiveFilters);
90            hasPositiveFilters = true;
91        }
92        if (mNegativeFilters != null && ! mNegativeFilters.isEmpty()) {
93            if (hasPositiveFilters) {
94                sb.append(":");
95            }
96            sb.append("-");
97            sb.append(mNegativeFilters);
98        }
99        return sb.toString();
100    }
101
102    /**
103     * @param abi The ABI to run the test on
104     */
105    public void setAbi(IAbi abi) {
106        mAbi = abi;
107        mExeName += mAbi.getBitness();
108    }
109
110    @Override
111    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
112        if (installTest()) {
113            runTest(listener);
114        } else {
115            CLog.e("Failed to install native tests");
116        }
117    }
118
119    private boolean installTest() throws DeviceNotAvailableException {
120        if (!createRemoteDir(NATIVE_TESTS_DIRECTORY)) {
121            CLog.e("Could not create directory for native tests: " + NATIVE_TESTS_DIRECTORY);
122            return false;
123        }
124
125        File nativeExe = new File(mCtsBuild.getTestCasesDir(), mExeName);
126        if (!nativeExe.exists()) {
127            CLog.e("Native test not found: " + nativeExe);
128            return false;
129        }
130
131        String devicePath = NATIVE_TESTS_DIRECTORY + ANDROID_PATH_SEPARATOR + mExeName;
132        if (!mDevice.pushFile(nativeExe, devicePath)) {
133            CLog.e("Failed to push native test to device");
134            return false;
135        }
136        return true;
137    }
138
139    private boolean createRemoteDir(String remoteFilePath) throws DeviceNotAvailableException {
140        if (mDevice.doesFileExist(remoteFilePath)) {
141            return true;
142        }
143        if (!(mDevice.doesFileExist(NATIVE_TESTS_DIRECTORY_TMP))) {
144            CLog.e("Could not find the /data/local/tmp directory");
145            return false;
146        }
147
148        mDevice.executeShellCommand(String.format("mkdir %s", remoteFilePath));
149        return mDevice.doesFileExist(remoteFilePath);
150    }
151
152    void runTest(ITestRunListener listener) throws DeviceNotAvailableException {
153        String id = AbiUtils.createId(mAbi.getName(), mPackageName);
154        GeeTestResultParser resultParser = new GeeTestResultParser(id, listener);
155        resultParser.setFakePackagePrefix(mPackageName + ".");
156
157        String fullPath = NATIVE_TESTS_DIRECTORY + ANDROID_PATH_SEPARATOR + mExeName;
158        String flags = getGTestFilters();
159        CLog.v("Running gtest %s %s on %s", fullPath, flags, mDevice.getSerialNumber());
160        // force file to be executable
161        CLog.v("%s", mDevice.executeShellCommand(String.format("chmod 755 %s", fullPath)));
162
163        try {
164            mDevice.executeShellCommand(String.format("%s %s", fullPath, flags), resultParser,
165                    mMaxTestTimeMs /* maxTimeToShellOutputResponse */,
166                    0 /* retryAttempts */);
167        } catch (DeviceNotAvailableException e) {
168            resultParser.flush();
169            throw e;
170        } catch (RuntimeException e) {
171            resultParser.flush();
172            throw e;
173        }
174    }
175
176
177    @Override
178    public void setBuild(IBuildInfo buildInfo) {
179        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
180    }
181
182    @Override
183    public void setDevice(ITestDevice device) {
184        mDevice = device;
185    }
186
187    @Override
188    public ITestDevice getDevice() {
189        return mDevice;
190    }
191}
192