1/*
2 * Copyright (C) 2009 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 vogar;
18
19import java.io.File;
20import java.util.Collection;
21import java.util.Collections;
22import java.util.HashSet;
23import java.util.LinkedHashMap;
24import java.util.List;
25import java.util.Map;
26import java.util.Set;
27import vogar.tasks.BuildActionTask;
28import vogar.tasks.PrepareTarget;
29import vogar.tasks.PrepareUserDirTask;
30import vogar.tasks.RetrieveFilesTask;
31import vogar.tasks.RmTask;
32import vogar.tasks.Task;
33import vogar.util.TimeUtilities;
34
35/**
36 * Compiles, installs, runs and reports on actions.
37 */
38public final class Driver {
39    private final Run run;
40
41    public Driver(Run run) {
42        this.run = run;
43    }
44
45    private int successes = 0;
46    private int failures = 0;
47    private int skipped = 0;
48    private int warnings = 0;
49
50    private Task prepareTargetTask;
51    private Set<Task> installVogarTasks;
52
53    private final Map<String, Action> actions = Collections.synchronizedMap(
54            new LinkedHashMap<String, Action>());
55    private final Map<String, Outcome> outcomes = Collections.synchronizedMap(
56            new LinkedHashMap<String, Outcome>());
57    public boolean recordResults = true;
58
59    /**
60     * Builds and executes the actions in the given files.
61     */
62    public boolean buildAndRun(Collection<File> files, Collection<String> classes) {
63        if (!actions.isEmpty()) {
64            throw new IllegalStateException("Drivers are not reusable");
65        }
66
67        run.mkdir.mkdirs(run.localTemp);
68
69        filesToActions(files);
70        classesToActions(classes);
71
72        if (actions.isEmpty()) {
73            run.console.info("Nothing to do.");
74            return false;
75        }
76
77        run.console.info("Actions: " + actions.size());
78        final long t0 = System.currentTimeMillis();
79
80        prepareTargetTask = new PrepareTarget(run, run.target);
81        run.taskQueue.enqueue(prepareTargetTask);
82
83        installVogarTasks = run.mode.installTasks();
84        run.taskQueue.enqueueAll(installVogarTasks);
85        registerPrerequisites(Collections.singleton(prepareTargetTask), installVogarTasks);
86
87        for (Action action : actions.values()) {
88            action.setUserDir(new File(run.runnerDir, action.getName()));
89            Outcome outcome = outcomes.get(action.getName());
90            if (outcome != null) {
91                addEarlyResult(outcome);
92            } else if (run.expectationStore.get(action.getName()).getResult() == Result.UNSUPPORTED) {
93                addEarlyResult(new Outcome(action.getName(), Result.UNSUPPORTED,
94                    "Unsupported according to expectations file"));
95            } else {
96                enqueueActionTasks(action);
97            }
98        }
99
100        if (run.cleanAfter) {
101            Set<Task> shutdownTasks = new HashSet<Task>();
102            shutdownTasks.add(new RmTask(run.rm, run.localTemp));
103            shutdownTasks.add(run.target.rmTask(run.runnerDir));
104            for (Task task : shutdownTasks) {
105                task.after(run.taskQueue.getTasks());
106            }
107            run.taskQueue.enqueueAll(shutdownTasks);
108        }
109
110        run.taskQueue.printTasks();
111        run.taskQueue.runTasks();
112        if (run.taskQueue.hasFailedTasks()) {
113            run.taskQueue.printProblemTasks();
114            return false;
115        }
116
117        if (run.reportPrinter.isReady()) {
118            run.console.info("Printing XML Reports... ");
119            int numFiles = run.reportPrinter.generateReports(outcomes.values());
120            run.console.info(numFiles + " XML files written.");
121        }
122
123        long t1 = System.currentTimeMillis();
124
125        Map<String, AnnotatedOutcome> annotatedOutcomes = run.outcomeStore.read(this.outcomes);
126        if (recordResults) {
127            run.outcomeStore.write(outcomes);
128        }
129
130        run.console.summarizeOutcomes(annotatedOutcomes.values());
131
132        List<String> jarStringList = run.jarSuggestions.getStringList();
133        if (!jarStringList.isEmpty()) {
134            run.console.warn(
135                    "consider adding the following to the classpath:",
136                    jarStringList);
137        }
138
139        if (failures > 0 || skipped > 0 || warnings > 0) {
140            run.console.info(String.format(
141                    "Outcomes: %s. Passed: %d, Failed: %d, Skipped: %d, Warnings: %d. Took %s.",
142                    (successes + failures + warnings + skipped), successes, failures, skipped, warnings,
143                    TimeUtilities.msToString(t1 - t0)));
144        } else {
145            run.console.info(String.format("Outcomes: %s. All successful. Took %s.",
146                    successes, TimeUtilities.msToString(t1 - t0)));
147        }
148        return failures == 0;
149    }
150
151    private void enqueueActionTasks(Action action) {
152        Expectation expectation = run.expectationStore.get(action.getName());
153        boolean useLargeTimeout = expectation.getTags().contains("large");
154        File jar;
155        if (run.useJack) {
156            jar = run.hostJack(action);
157        } else {
158            jar = run.hostJar(action);
159        }
160        Task build = new BuildActionTask(run, action, this, jar);
161        run.taskQueue.enqueue(build);
162
163        Task prepareUserDir = new PrepareUserDirTask(run.target, action);
164        prepareUserDir.after(installVogarTasks);
165        run.taskQueue.enqueue(prepareUserDir);
166
167        Set<Task> install = run.mode.installActionTasks(action, jar);
168        registerPrerequisites(Collections.singleton(build), install);
169        registerPrerequisites(installVogarTasks, install);
170        registerPrerequisites(Collections.singleton(prepareTargetTask), install);
171        run.taskQueue.enqueueAll(install);
172
173        Task execute = run.mode.executeActionTask(action, useLargeTimeout)
174                .afterSuccess(installVogarTasks)
175                .afterSuccess(build)
176                .afterSuccess(prepareUserDir)
177                .afterSuccess(install);
178        run.taskQueue.enqueue(execute);
179
180        Task retrieveFiles = new RetrieveFilesTask(run, action.getUserDir()).after(execute);
181        run.taskQueue.enqueue(retrieveFiles);
182
183        if (run.cleanAfter) {
184            run.taskQueue.enqueue(new RmTask(run.rm, run.localFile(action))
185                    .after(execute).after(retrieveFiles));
186            Set<Task> cleanupTasks = run.mode.cleanupTasks(action);
187            for (Task task : cleanupTasks) {
188                task.after(execute).after(retrieveFiles);
189            }
190            run.taskQueue.enqueueAll(cleanupTasks);
191        }
192    }
193
194    private void registerPrerequisites(Set<Task> allBefore, Set<Task> allAfter) {
195        for (Task task : allAfter) {
196            task.afterSuccess(allBefore);
197        }
198    }
199
200    private void classesToActions(Collection<String> classNames) {
201        for (String className : classNames) {
202            Action action = new Action(className, className, null, null, null);
203            actions.put(action.getName(), action);
204        }
205    }
206
207    private void filesToActions(Collection<File> files) {
208        for (File file : files) {
209            new ActionFinder(run.console, actions, outcomes).findActions(file);
210        }
211    }
212
213    public synchronized void addEarlyResult(Outcome earlyFailure) {
214        if (earlyFailure.getResult() == Result.UNSUPPORTED) {
215            run.console.verbose("skipped " + earlyFailure.getName());
216            skipped++;
217
218        } else {
219            for (String line : earlyFailure.getOutputLines()) {
220                run.console.streamOutput(earlyFailure.getName(), line + "\n");
221            }
222            recordOutcome(earlyFailure);
223        }
224    }
225
226    public synchronized void recordOutcome(Outcome outcome) {
227        outcomes.put(outcome.getName(), outcome);
228        Expectation expectation = run.expectationStore.get(outcome);
229        ResultValue resultValue = outcome.getResultValue(expectation);
230
231        if (resultValue == ResultValue.OK) {
232            successes++;
233        } else if (resultValue == ResultValue.FAIL) {
234            failures++;
235        } else if (resultValue == ResultValue.WARNING) {
236            warnings++;
237        } else { // ResultValue.IGNORE
238            skipped++;
239        }
240
241        Result result = outcome.getResult();
242        run.console.outcome(outcome.getName());
243        run.console.printResult(outcome.getName(), result, resultValue, expectation);
244
245        JarSuggestions singleOutcomeJarSuggestions = new JarSuggestions();
246        singleOutcomeJarSuggestions.addSuggestionsFromOutcome(outcome, run.classFileIndex,
247                run.classpath);
248        List<String> jarStringList = singleOutcomeJarSuggestions.getStringList();
249        if (!jarStringList.isEmpty()) {
250            run.console.warn(
251                    "may have failed because some of these jars are missing from the classpath:",
252                    jarStringList);
253        }
254        run.jarSuggestions.addSuggestions(singleOutcomeJarSuggestions);
255    }
256}
257