ManagerService.java revision 23a0ee4758da5e2d24ec6c9e8a63c127463a096f
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_TEST_CRASHED = 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
76    /**
77     * This handler is purely for IPC. It is used to create mMessenger
78     * that generates a binder returned in onBind method.
79     */
80    private Handler mIncomingHandler = new Handler() {
81        @Override
82        public void handleMessage(Message msg) {
83            switch (msg.what) {
84                case MSG_FIRST_TEST:
85                    Bundle bundle = msg.getData();
86                    ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index"));
87                    break;
88
89                case MSG_PROCESS_ACTUAL_RESULTS:
90                    Log.d(LOG_TAG + ".mIncomingHandler", msg.getData().getString("relativePath"));
91                    onActualResultsObtained(msg.getData());
92                    break;
93
94                case MSG_ALL_TESTS_FINISHED:
95                    mSummarizer.summarize();
96                    break;
97            }
98        }
99    };
100
101    private Messenger mMessenger = new Messenger(mIncomingHandler);
102
103    private Handler mCrashMessagesHandler = new Handler() {
104        @Override
105        public void handleMessage(Message msg) {
106            if (msg.what == MSG_TEST_CRASHED) {
107                onTestCrashed();
108            }
109        }
110    };
111
112    private FileFilter mFileFilter;
113    private Summarizer mSummarizer;
114
115    private String mCurrentlyRunningTest;
116    private int mCurrentlyRunningTestIndex;
117
118    @Override
119    public void onCreate() {
120        super.onCreate();
121
122        mFileFilter = new FileFilter(TESTS_ROOT_DIR_PATH);
123        mSummarizer = new Summarizer(mFileFilter, RESULTS_ROOT_DIR_PATH);
124    }
125
126    @Override
127    public int onStartCommand(Intent intent, int flags, int startId) {
128        return START_STICKY;
129    }
130
131    @Override
132    public IBinder onBind(Intent intent) {
133        return mMessenger.getBinder();
134    }
135
136    private void onActualResultsObtained(Bundle bundle) {
137        mCrashMessagesHandler.removeMessages(MSG_TEST_CRASHED);
138        ensureNextTestSetup(bundle.getString("nextTest"), bundle.getInt("testIndex") + 1);
139
140        AbstractResult results =
141                AbstractResult.TestType.valueOf(bundle.getString("type")).createResult(bundle);
142
143        handleResults(results);
144    }
145
146    private void ensureNextTestSetup(String nextTest, int index) {
147        if (nextTest == null) {
148            return;
149        }
150
151        mCurrentlyRunningTest = nextTest;
152        mCurrentlyRunningTestIndex = index;
153        mCrashMessagesHandler.sendEmptyMessageDelayed(MSG_TEST_CRASHED, CRASH_TIMEOUT_MS);
154    }
155
156    /**
157     * This sends an intent to TestsListActivity to restart LayoutTestsExecutor.
158     * The more detailed description of the flow is in the comment of onNewIntent
159     * method in TestsListActivity.
160     */
161    private void onTestCrashed() {
162        handleResults(new CrashedDummyResult(mCurrentlyRunningTest));
163
164        Log.w(LOG_TAG + "::onTestCrashed", mCurrentlyRunningTest +
165                "(" + mCurrentlyRunningTestIndex + ")");
166
167        Intent intent = new Intent(this, TestsListActivity.class);
168        intent.setAction(Intent.ACTION_REBOOT);
169        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
170        intent.putExtra("crashedTestIndex", mCurrentlyRunningTestIndex);
171        startActivity(intent);
172    }
173
174    private void handleResults(AbstractResult results) {
175        String relativePath = results.getRelativePath();
176        results.setExpectedTextResult(getExpectedTextResult(relativePath));
177        results.setExpectedImageResult(getExpectedImageResult(relativePath));
178
179        dumpActualTextResult(results);
180        dumpActualImageResult(results);
181
182        mSummarizer.appendTest(results);
183    }
184
185    private void dumpActualTextResult(AbstractResult result) {
186        String testPath = result.getRelativePath();
187        String actualTextResult = result.getActualTextResult();
188        if (actualTextResult == null) {
189            return;
190        }
191
192        String resultPath = FileFilter.setPathEnding(testPath, "-actual." + TEXT_RESULT_EXTENSION);
193        FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
194                actualTextResult.getBytes(), false);
195    }
196
197    private void dumpActualImageResult(AbstractResult result) {
198        String testPath = result.getRelativePath();
199        byte[] actualImageResult = result.getActualImageResult();
200        if (actualImageResult == null) {
201            return;
202        }
203
204        String resultPath = FileFilter.setPathEnding(testPath,
205                "-actual." + IMAGE_RESULT_EXTENSION);
206        FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
207                actualImageResult, false);
208    }
209
210    public static String getExpectedTextResult(String relativePath) {
211        byte[] result = getExpectedResult(relativePath, TEXT_RESULT_EXTENSION);
212        if (result != null) {
213            return new String(result);
214        }
215        return null;
216    }
217
218    public static byte[] getExpectedImageResult(String relativePath) {
219        return getExpectedResult(relativePath, IMAGE_RESULT_EXTENSION);
220    }
221
222    private static byte[] getExpectedResult(String relativePath, String extension) {
223        String originalRelativePath =
224                FileFilter.setPathEnding(relativePath, "-expected." + extension);
225
226        byte[] bytes = null;
227        List<String> locations = EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES;
228
229        int size = EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.size();
230        for (int i = 0; bytes == null && i < size; i++) {
231            relativePath = locations.get(i) + originalRelativePath;
232            bytes = FsUtils.readDataFromStorage(new File(TESTS_ROOT_DIR_PATH, relativePath));
233        }
234
235        return bytes;
236    }
237}