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 vogar;
18
19import com.google.common.collect.Lists;
20import java.io.PrintWriter;
21import java.io.StringWriter;
22import java.util.Arrays;
23import java.util.Date;
24import java.util.List;
25import vogar.util.Strings;
26
27/**
28 * An outcome of an action. Some actions may have multiple outcomes. For
29 * example, JUnit tests have one outcome for each test method.
30 */
31public final class Outcome {
32
33    private final String outcomeName;
34    private final Result result;
35    private final String output;
36    private final Date date;
37
38    public Outcome(String outcomeName, Result result, List<String> outputLines) {
39        this.outcomeName = outcomeName;
40        this.result = result;
41        this.output = sanitizeOutputLines(outputLines);
42        this.date = new Date();
43    }
44
45    public Outcome(String outcomeName, Result result, String outputLine, Date date) {
46        this.outcomeName = outcomeName;
47        this.result = result;
48        this.output = sanitizeOutputLine(outputLine);
49        this.date = date;
50    }
51
52    public Outcome(String outcomeName, Result result, String outputLine) {
53        this.outcomeName = outcomeName;
54        this.result = result;
55        this.output = sanitizeOutputLine(outputLine);
56        this.date = new Date();
57    }
58
59    public Outcome(String outcomeName, Result result, Throwable throwable) {
60        this.outcomeName = outcomeName;
61        this.result = result;
62        this.output = sanitizeOutputLines(throwableToLines(throwable));
63        this.date = new Date();
64    }
65
66    private String sanitizeOutputLines(List<String> outputLines) {
67        List<String> sanitizedStrings = Lists.newArrayList();
68        for (String line : outputLines) {
69            sanitizedStrings.add(sanitizeOutputLine(line));
70        }
71        return Strings.join(sanitizedStrings, "\n");
72    }
73
74    private String sanitizeOutputLine(String outputLine) {
75        return Strings.xmlSanitize(outputLine.replaceAll("\r\n?", "\n"));
76    }
77
78    public Date getDate() {
79        return date;
80    }
81
82    public String getName() {
83        return outcomeName;
84    }
85
86    public Result getResult() {
87        return result;
88    }
89
90    public String getOutput() {
91        return output;
92    }
93
94    public List<String> getOutputLines() {
95        return Arrays.asList(output.split("\n"));
96    }
97
98    private static List<String> throwableToLines(Throwable t) {
99        StringWriter writer = new StringWriter();
100        PrintWriter out = new PrintWriter(writer);
101        t.printStackTrace(out);
102        return Arrays.asList(writer.toString().split("\\n"));
103    }
104
105    /**
106     * Returns the action's suite name, such as java.lang.Integer or
107     * java.lang.IntegerTest.
108     */
109    public String getSuiteName() {
110        int split = split(outcomeName);
111        return split == -1 ? "defaultpackage" : outcomeName.substring(0, split);
112    }
113
114    /**
115     * Returns the specific action name, such as BitTwiddle or testBitTwiddle.
116     */
117    public String getTestName() {
118        int split = split(outcomeName);
119        return split == -1 ? outcomeName : outcomeName.substring(split + 1);
120    }
121
122    private static int split(String name) {
123        int lastHash = name.indexOf('#');
124        return lastHash == -1 ? name.lastIndexOf('.') : lastHash;
125    }
126
127    /**
128     * Returns whether the result indicates that the contents of the Outcome are important.
129     *
130     * For example, for a test skipped because it is unsupported, we don't care about the result.
131     */
132    private boolean matters() {
133        return result != Result.UNSUPPORTED;
134    }
135
136    public ResultValue getResultValue(Expectation expectation) {
137        if (matters()) {
138            if (expectation.matches(this)) {
139                return ResultValue.OK;
140            } else if (expectation.getIsFromExpectationFile()) {
141                // If the expectation is created from a file, make this a warning, to
142                // mimic CTS's behavior.
143                return ResultValue.WARNING;
144            }
145            return ResultValue.FAIL;
146        }
147        return ResultValue.IGNORE;
148    }
149
150    /**
151     * Returns a filesystem db path for this outcome. For example, a path for an outcome with name
152     * "foo.bar.baz#testName" would be "foo/bar/baz/testName".
153     */
154    public String getPath() {
155        return outcomeName.replaceAll("[\\.#]", "/");
156    }
157
158    @Override public boolean equals(Object o) {
159        if (o instanceof Outcome) {
160            Outcome outcome = (Outcome) o;
161            return outcomeName.equals(outcome.outcomeName)
162                    && result == outcome.result
163                    && output.equals(outcome.output);
164        }
165        return false;
166    }
167
168    @Override public int hashCode() {
169        int hashCode = 17;
170        hashCode = 37 * hashCode + outcomeName.hashCode();
171        hashCode  = 37 * hashCode + result.hashCode();
172        hashCode = 37 * hashCode + output.hashCode();
173        return hashCode;
174    }
175
176    @Override public String toString() {
177        return "Outcome[name=" + outcomeName + " output=" + output + "]";
178    }
179
180}
181