View Javadoc
1   package net.avcompris.guixer.core;
2   
3   import static com.google.common.base.Preconditions.checkNotNull;
4   import static com.google.common.base.Preconditions.checkState;
5   import static com.google.common.collect.Maps.newHashMap;
6   import static com.google.common.io.Resources.getResource;
7   import static com.google.common.io.Resources.toByteArray;
8   import static org.apache.commons.lang3.CharEncoding.UTF_8;
9   import static org.apache.commons.lang3.StringUtils.isBlank;
10  import static org.joda.time.DateTimeZone.UTC;
11  
12  import java.io.ByteArrayInputStream;
13  import java.io.File;
14  import java.io.FileNotFoundException;
15  import java.io.IOException;
16  import java.lang.reflect.Method;
17  import java.util.Map;
18  import java.util.Properties;
19  
20  import javax.annotation.Nullable;
21  
22  import org.joda.time.DateTime;
23  
24  import net.avcompris.domdumper.Dumper;
25  import net.avcompris.domdumper.XMLDumpers;
26  
27  final class DumperLogger implements Logger {
28  
29  	private static String GUIXER_VERSION;
30  
31  	private static final Map<File, DumperLogger> LOGGERS = newHashMap();
32  
33  	private final Context context;
34  	private final File logXmlFile;
35  	private final Dumper dumper;
36  	private Dumper commandDumper;
37  	private Dumper stepDumper;
38  	private long currentStartedAtMs;
39  
40  	private DumperLogger(final Context context, final File logXmlFile) throws IOException {
41  
42  		this.context = checkNotNull(context, "context");
43  		this.logXmlFile = checkNotNull(logXmlFile, "logXmlFile");
44  
45  		final DateTime now = new DateTime().withZone(UTC);
46  
47  		dumper = XMLDumpers.newDumper("test", logXmlFile, UTF_8) //
48  				.addAttribute("guixerVersion", getGuixerVersion()) //
49  				.addAttribute("startedAt", now.toString()) //
50  				.addAttribute("startedAtMs", Long.toString(now.getMillis())) //
51  				.addAttribute("seleniumServerURL", context.getSeleniumServerURL()) //
52  				.addAttribute("seleniumDesiredCapabilities", context.getSeleniumDesiredCapabilities()) //
53  				.addAttribute("baseURL", context.getBaseURL());
54  	}
55  
56  	@Override
57  	public void setTestContext(final Class<?> testClass, final Method testMethod) throws IOException {
58  
59  		checkNotNull(testClass, "testClass");
60  		checkNotNull(testMethod, "testMethod");
61  
62  		dumper //
63  				.addAttribute("testClassName", testClass.getName()) //
64  				.addAttribute("testClassSimpleName", testClass.getSimpleName()) //
65  				.addAttribute("testMethodName", testMethod.getName());
66  	}
67  
68  	@Override
69  	public int hashCode() {
70  
71  		return logXmlFile.hashCode();
72  	}
73  
74  	@Override
75  	public boolean equals(@Nullable final Object arg) {
76  
77  		if (arg == null || !(arg instanceof DumperLogger)) {
78  
79  			return false;
80  		}
81  
82  		return logXmlFile.equals(((DumperLogger) arg).logXmlFile);
83  	}
84  
85  	@Override
86  	public String toString() {
87  
88  		return "[" + logXmlFile.getAbsolutePath() + "]";
89  	}
90  
91  	public static DumperLogger getLogger(final Context context) throws IOException {
92  
93  		checkNotNull(context, "context");
94  
95  		final File dir = context.getSubDir();
96  
97  		if (!dir.isDirectory()) {
98  
99  			throw new FileNotFoundException("dir should be a directory: " + dir.getCanonicalPath());
100 		}
101 
102 		final File logXmlFile = new File(dir, "log.xml");
103 
104 		if (logXmlFile.isFile() && LOGGERS.containsKey(logXmlFile)) {
105 
106 			return LOGGERS.get(logXmlFile);
107 		}
108 
109 		final DumperLoggergger.html#DumperLogger">DumperLogger logger = new DumperLogger(context, logXmlFile);
110 
111 		LOGGERS.put(logXmlFile, logger);
112 
113 		return logger;
114 	}
115 
116 	public void close() throws IOException {
117 
118 		commandDumper = null;
119 
120 		dumper.close();
121 
122 		LOGGERS.remove(logXmlFile);
123 	}
124 
125 	public void startCommand(@Nullable final String actionShortDescription) throws IOException {
126 
127 		commandDumper = dumper.addElement("command");
128 
129 		if (actionShortDescription != null) {
130 
131 			commandDumper.addAttribute("actionShortDescription", actionShortDescription);
132 		}
133 	}
134 
135 	public void startStep(final String label) throws IOException {
136 
137 		checkNotNull(label, "label");
138 
139 		checkState(commandDumper != null, //
140 				"commandDumper should not be null; startCommand() should have been called prior to this call");
141 
142 		stepDumper = commandDumper.addElement("step");
143 
144 		currentStartedAtMs = System.currentTimeMillis();
145 
146 		stepDumper.addElement("startedAtMs") //
147 				.addCharacters(Long.toString(currentStartedAtMs));
148 
149 		stepDumper.addElement("literal").addCharacters(label);
150 	}
151 
152 	public void endStep() throws IOException {
153 
154 		checkState(stepDumper != null,
155 				"stepDumper should not be null; startStep() should have been called prior to this call");
156 
157 		stepDumper.addElement("index") //
158 				.addCharacters(Integer.toString(context.getStepCount()));
159 
160 		final long endedAtMs = System.currentTimeMillis();
161 		final long elapsedMs = endedAtMs - currentStartedAtMs;
162 
163 		stepDumper.addElement("endedAtMs") //
164 				.addCharacters(Long.toString(endedAtMs));
165 
166 		stepDumper.addElement("elapsedMs") //
167 				.addCharacters(Long.toString(elapsedMs));
168 
169 		stepDumper = null;
170 	}
171 
172 	public void error(final Throwable throwable) throws IOException {
173 
174 		checkNotNull(throwable, "throwable");
175 
176 		checkState(stepDumper != null,
177 				"stepDumper should not be null; startStep() should have been called prior to this call");
178 
179 		stepDumper.addElement("error") //
180 				.addCharacters(throwable.toString());
181 
182 		final long endedAtMs = System.currentTimeMillis();
183 		final long elapsedMs = endedAtMs - currentStartedAtMs;
184 
185 		stepDumper.addElement("endedAtMs") //
186 				.addCharacters(Long.toString(endedAtMs));
187 
188 		stepDumper.addElement("elapsedMs") //
189 				.addCharacters(Long.toString(elapsedMs));
190 
191 		stepDumper = null;
192 	}
193 
194 	private static String getGuixerVersion() throws IOException {
195 
196 		if (GUIXER_VERSION != null) {
197 
198 			return GUIXER_VERSION;
199 		}
200 
201 		final Properties properties = new Properties();
202 
203 		final String resourceName = "appinfo.properties";
204 
205 		properties.load(new ByteArrayInputStream(toByteArray(getResource(resourceName))));
206 
207 		GUIXER_VERSION = properties.getProperty("guixerVersion");
208 
209 		if (isBlank(GUIXER_VERSION)) {
210 			throw new IOException("Cannot read GUIXER_VERSION from: " + resourceName);
211 		}
212 
213 		return GUIXER_VERSION;
214 	}
215 }