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 
17 package vogar;
18 
19 import com.google.gson.stream.JsonReader;
20 import com.google.gson.stream.JsonWriter;
21 import java.io.File;
22 import java.io.FileReader;
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.text.SimpleDateFormat;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.Date;
30 import java.util.LinkedHashMap;
31 import java.util.Map;
32 import java.util.TimeZone;
33 import vogar.commands.Mkdir;
34 import vogar.commands.Rm;
35 
36 public final class OutcomeStore {
37     private static final String FILE_NAME_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssz";
38     private static final int PRESERVE_TOTAL = 10;
39 
40     private static final Comparator<File> ORDER_BY_LAST_MODIFIED = new Comparator<File>() {
41         @Override public int compare(File a, File b) {
42             if (a.lastModified() != b.lastModified()) {
43                 return a.lastModified() < b.lastModified() ? -1 : 1;
44             }
45             return 0;
46         }
47     };
48 
49     private final Log log;
50     private final Mkdir mkdir;
51     private final Rm rm;
52     private final File resultsDir;
53     private final boolean recordResults;
54     private final ExpectationStore expectationStore;
55     private final Date date;
56 
OutcomeStore(Log log, Mkdir mkdir, Rm rm, File resultsDir, boolean recordResults, ExpectationStore expectationStore, Date date)57     public OutcomeStore(Log log, Mkdir mkdir, Rm rm, File resultsDir, boolean recordResults,
58             ExpectationStore expectationStore, Date date) {
59         this.log = log;
60         this.mkdir = mkdir;
61         this.rm = rm;
62         this.resultsDir = resultsDir;
63         this.recordResults = recordResults;
64         this.expectationStore = expectationStore;
65         this.date = date;
66     }
67 
read(Map<String, Outcome> outcomes)68     public Map<String, AnnotatedOutcome> read(Map<String, Outcome> outcomes) {
69         Map<String, AnnotatedOutcome> result = new LinkedHashMap<String, AnnotatedOutcome>();
70         for (Map.Entry<String, Outcome> entry : outcomes.entrySet()) {
71             Outcome outcome = entry.getValue();
72             Expectation expectation = expectationStore.get(outcome);
73             result.put(entry.getKey(), new AnnotatedOutcome(outcome, expectation));
74         }
75 
76         try {
77             File[] oldOutcomes = getOutcomeFiles();
78             log.verbose("parsing outcomes from " + oldOutcomes.length + " files");
79             for (File file : oldOutcomes) {
80                 if (!file.getName().endsWith(".json")) {
81                     continue;
82                 }
83 
84                 loadOutcomes(result, file, file.lastModified());
85             }
86         } catch (IOException e) {
87             log.info("Failed to read outcomes from " + resultsDir, e);
88         }
89 
90         return result;
91     }
92 
loadOutcomes(Map<String, AnnotatedOutcome> map, File file, long fileDate)93     private void loadOutcomes(Map<String, AnnotatedOutcome> map, File file, long fileDate)
94             throws IOException {
95         JsonReader in = new JsonReader(new FileReader(file));
96         in.beginObject();
97         while (in.hasNext()) {
98             String outcomeName = in.nextName();
99             AnnotatedOutcome annotatedOutcome = map.get(outcomeName);
100             if (annotatedOutcome == null) {
101                 in.skipValue();
102                 continue;
103             }
104 
105             Result result = null;
106             in.beginObject();
107             while (in.hasNext()) {
108                 String fieldName = in.nextName();
109                 if (fieldName.equals("result")) {
110                     result = Result.valueOf(in.nextString());
111                 } else {
112                     in.skipValue();
113                 }
114             }
115             in.endObject();
116 
117             annotatedOutcome.add(fileDate, new Outcome(outcomeName, result,
118                     Collections.<String>emptyList()));
119         }
120         in.endObject();
121         in.close();
122     }
123 
write(Map<String, Outcome> outcomes)124     public void write(Map<String, Outcome> outcomes) {
125         if (!recordResults) {
126             return;
127         }
128 
129         makeRoom();
130         File outputFile = getOutputFile();
131         try {
132             mkdir.mkdirs(outputFile.getParentFile());
133             JsonWriter out = new JsonWriter(new FileWriter(outputFile));
134             out.setIndent("  ");
135             out.beginObject();
136             for (Map.Entry<String, Outcome> entry : outcomes.entrySet()) {
137                 out.name(entry.getKey());
138                 out.beginObject();
139                 out.name("result");
140                 out.value(entry.getValue().getResult().toString());
141                 out.endObject();
142             }
143             out.endObject();
144             out.close();
145         } catch (IOException e) {
146             log.info("Failed to write outcomes to " + outputFile, e);
147         }
148     }
149 
getOutcomeFiles()150     private File[] getOutcomeFiles() {
151         File[] result = resultsDir.listFiles();
152         if (result == null) {
153             return new File[0];
154         }
155         Arrays.sort(result, ORDER_BY_LAST_MODIFIED);
156         return result;
157     }
158 
getOutputFile()159     private File getOutputFile() {
160         SimpleDateFormat dateFormat = new SimpleDateFormat(FILE_NAME_DATE_FORMAT);
161         dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
162         dateFormat.setLenient(true);
163         String timestamp = dateFormat.format(date);
164         return new File(resultsDir, timestamp + ".json");
165     }
166 
167     /**
168      * Removes the oldest result files until only (PRESERVE_TOTAL - 1) remain.
169      */
makeRoom()170     private void makeRoom() {
171         File[] outcomeFiles = getOutcomeFiles();
172         for (int i = 0; i <= (outcomeFiles.length - PRESERVE_TOTAL); i++) {
173             rm.file(outcomeFiles[i]);
174             log.verbose("garbage collected results file: " + outcomeFiles[i]);
175         }
176     }
177 }
178