ManagerService.java revision 01c1f9d9e9e7ab3a40a9bf27195a434eb0c9bb9b
1/*
2 * Copyright (C) 2010 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.dumprendertree2;
18
19import android.app.Service;
20import android.content.Intent;
21import android.os.Bundle;
22import android.os.Environment;
23import android.os.Handler;
24import android.os.IBinder;
25import android.os.Message;
26import android.os.Messenger;
27import android.util.Log;
28
29import java.io.File;
30import java.util.ArrayList;
31import java.util.List;
32
33/**
34 * A service that handles managing the results of tests, informing of crashes, generating
35 * summaries, etc.
36 */
37public class ManagerService extends Service {
38
39    private static final String LOG_TAG = "ManagerService";
40
41    private static final int MSG_CRASH_TIMEOUT_EXPIRED = 0;
42
43    private static final int CRASH_TIMEOUT_MS = 20 * 1000;
44
45    /** TODO: make it a setting */
46    static final String TESTS_ROOT_DIR_PATH =
47            Environment.getExternalStorageDirectory() +
48            File.separator + "android" +
49            File.separator + "LayoutTests";
50
51    /** TODO: make it a setting */
52    static final String RESULTS_ROOT_DIR_PATH =
53            Environment.getExternalStorageDirectory() +
54            File.separator + "android" +
55            File.separator + "LayoutTests-results";
56
57    /** TODO: Make it a setting */
58    private static final List<String> EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES =
59            new ArrayList<String>(3);
60    {
61        EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.add("platform" + File.separator +
62                "android-v8" + File.separator);
63        EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.add("platform" + File.separator +
64                "android" + File.separator);
65        EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.add("");
66    }
67
68    /** TODO: Make these settings */
69    private static final String TEXT_RESULT_EXTENSION = "txt";
70    private static final String IMAGE_RESULT_EXTENSION = "png";
71
72    static final int MSG_PROCESS_ACTUAL_RESULTS = 0;
73    static final int MSG_ALL_TESTS_FINISHED = 1;
74    static final int MSG_FIRST_TEST = 2;
75    static final int MSG_CURRENT_TEST_CRASHED = 3;
76
77    /**
78     * This handler is purely for IPC. It is used to create mMessenger
79     * that generates a binder returned in onBind method.
80     */
81    private Handler mIncomingHandler = new Handler() {
82        @Override
83        public void handleMessage(Message msg) {
84            switch (msg.what) {
85                case MSG_FIRST_TEST:
86                    mSummarizer.reset();
87                    Bundle bundle = msg.getData();
88                    ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index"));
89                    break;
90
91                case MSG_PROCESS_ACTUAL_RESULTS:
92                    Log.d(LOG_TAG,"mIncomingHandler: " + msg.getData().getString("relativePath"));
93                    onActualResultsObtained(msg.getData());
94                    break;
95
96                case MSG_CURRENT_TEST_CRASHED:
97                    mCrashMessagesHandler.removeMessages(MSG_CRASH_TIMEOUT_EXPIRED);
98                    onTestCrashed();
99                    break;
100
101                case MSG_ALL_TESTS_FINISHED:
102                    mSummarizer.setTestsRelativePath(mAllTestsRelativePath);
103                    mSummarizer.summarize();
104                    Intent intent = new Intent(ManagerService.this, TestsListActivity.class);
105                    intent.setAction(Intent.ACTION_SHUTDOWN);
106                    /** This flag is needed because we send the intent from the service */
107                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
108                    startActivity(intent);
109                    break;
110            }
111        }
112    };
113
114    private Messenger mMessenger = new Messenger(mIncomingHandler);
115
116    private Handler mCrashMessagesHandler = new Handler() {
117        @Override
118        public void handleMessage(Message msg) {
119            if (msg.what == MSG_CRASH_TIMEOUT_EXPIRED) {
120                onTestCrashed();
121            }
122        }
123    };
124
125    private FileFilter mFileFilter;
126    private Summarizer mSummarizer;
127
128    private String mCurrentlyRunningTest;
129    private int mCurrentlyRunningTestIndex;
130
131    /**
132     * These are implementation details of getExpectedResultPath() used to reduce the number
133     * of requests required to the host server.
134     */
135    private String mLastExpectedResultPathRequested;
136    private String mLastExpectedResultPathFetched;
137
138    private String mAllTestsRelativePath;
139
140    @Override
141    public void onCreate() {
142        super.onCreate();
143
144        mFileFilter = new FileFilter(TESTS_ROOT_DIR_PATH);
145        mSummarizer = new Summarizer(mFileFilter, RESULTS_ROOT_DIR_PATH);
146    }
147
148    @Override
149    public int onStartCommand(Intent intent, int flags, int startId) {
150        mAllTestsRelativePath = intent.getStringExtra("path");
151        assert mAllTestsRelativePath != null;
152        return START_STICKY;
153    }
154
155    @Override
156    public IBinder onBind(Intent intent) {
157        return mMessenger.getBinder();
158    }
159
160    private void onActualResultsObtained(Bundle bundle) {
161        mCrashMessagesHandler.removeMessages(MSG_CRASH_TIMEOUT_EXPIRED);
162        ensureNextTestSetup(bundle.getString("nextTest"), bundle.getInt("testIndex") + 1);
163
164        AbstractResult results =
165                AbstractResult.TestType.valueOf(bundle.getString("type")).createResult(bundle);
166
167        Log.i(LOG_TAG, "onActualResultObtained: " + results.getRelativePath());
168        handleResults(results);
169    }
170
171    private void ensureNextTestSetup(String nextTest, int index) {
172        if (nextTest == null) {
173            Log.w(LOG_TAG, "ensureNextTestSetup(): nextTest=null");
174            return;
175        }
176
177        mCurrentlyRunningTest = nextTest;
178        mCurrentlyRunningTestIndex = index;
179        mCrashMessagesHandler.sendEmptyMessageDelayed(MSG_CRASH_TIMEOUT_EXPIRED, CRASH_TIMEOUT_MS);
180    }
181
182    /**
183     * This sends an intent to TestsListActivity to restart LayoutTestsExecutor.
184     * The more detailed description of the flow is in the comment of onNewIntent
185     * method in TestsListActivity.
186     */
187    private void onTestCrashed() {
188        handleResults(new CrashedDummyResult(mCurrentlyRunningTest));
189
190        Log.w(LOG_TAG, "onTestCrashed(): " + mCurrentlyRunningTest +
191                " (" + mCurrentlyRunningTestIndex + ")");
192
193        Intent intent = new Intent(this, TestsListActivity.class);
194        intent.setAction(Intent.ACTION_REBOOT);
195        /** This flag is needed because we send the intent from the service */
196        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
197        intent.putExtra("crashedTestIndex", mCurrentlyRunningTestIndex);
198        startActivity(intent);
199    }
200
201    private void handleResults(AbstractResult results) {
202        String relativePath = results.getRelativePath();
203        results.setExpectedTextResult(getExpectedTextResult(relativePath));
204        results.setExpectedTextResultPath(getExpectedTextResultPath(relativePath));
205        results.setExpectedImageResult(getExpectedImageResult(relativePath));
206        results.setExpectedImageResultPath(getExpectedImageResultPath(relativePath));
207
208        dumpActualTextResult(results);
209        dumpActualImageResult(results);
210
211        mSummarizer.appendTest(results);
212    }
213
214    private void dumpActualTextResult(AbstractResult result) {
215        String testPath = result.getRelativePath();
216        String actualTextResult = result.getActualTextResult();
217        if (actualTextResult == null) {
218            return;
219        }
220
221        String resultPath = FileFilter.setPathEnding(testPath, "-actual." + TEXT_RESULT_EXTENSION);
222        FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
223                actualTextResult.getBytes(), false);
224    }
225
226    private void dumpActualImageResult(AbstractResult result) {
227        String testPath = result.getRelativePath();
228        byte[] actualImageResult = result.getActualImageResult();
229        if (actualImageResult == null) {
230            return;
231        }
232
233        String resultPath = FileFilter.setPathEnding(testPath,
234                "-actual." + IMAGE_RESULT_EXTENSION);
235        FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
236                actualImageResult, false);
237    }
238
239    public String getExpectedTextResult(String relativePath) {
240        byte[] result = getExpectedResult(relativePath, TEXT_RESULT_EXTENSION);
241        if (result != null) {
242            return new String(result);
243        }
244        return null;
245    }
246
247    public byte[] getExpectedImageResult(String relativePath) {
248        return getExpectedResult(relativePath, IMAGE_RESULT_EXTENSION);
249    }
250
251    private byte[] getExpectedResult(String relativePath, String extension) {
252        String originalRelativePath =
253                FileFilter.setPathEnding(relativePath, "-expected." + extension);
254        mLastExpectedResultPathRequested = originalRelativePath;
255
256        byte[] bytes = null;
257        List<String> locations = EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES;
258
259        int size = EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.size();
260        for (int i = 0; bytes == null && i < size; i++) {
261            relativePath = locations.get(i) + originalRelativePath;
262            bytes = FsUtils.readDataFromUrl(FileFilter.getUrl(relativePath));
263        }
264
265        mLastExpectedResultPathFetched = relativePath;
266        return bytes;
267    }
268
269    private String getExpectedTextResultPath(String relativePath) {
270        return getExpectedResultPath(relativePath, TEXT_RESULT_EXTENSION);
271    }
272
273    private String getExpectedImageResultPath(String relativePath) {
274        return getExpectedResultPath(relativePath, IMAGE_RESULT_EXTENSION);
275    }
276
277    private String getExpectedResultPath(String relativePath, String extension) {
278        String originalRelativePath =
279            FileFilter.setPathEnding(relativePath, "-expected." + extension);
280        if (!originalRelativePath.equals(mLastExpectedResultPathRequested)) {
281            getExpectedResult(relativePath, extension);
282        }
283
284        return mLastExpectedResultPathFetched;
285    }
286}