/*
* Copyright (c) 2012 Aleksey Shipilev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.shipilev.concurrent.torture;
import com.google.common.base.Predicate;
import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;
import net.shipilev.concurrency.torture.schema.descr.Case;
import net.shipilev.concurrency.torture.schema.descr.ExpectType;
import net.shipilev.concurrency.torture.schema.descr.Ref;
import net.shipilev.concurrency.torture.schema.descr.Test;
import net.shipilev.concurrency.torture.schema.descr.Testsuite;
import net.shipilev.concurrency.torture.schema.result.Result;
import net.shipilev.concurrency.torture.schema.result.State;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class XMLtoHTMLResultPrinter {
private final String resultDir;
private final Multimap<String, Test> testSuites;
public XMLtoHTMLResultPrinter(Options opts) throws JAXBException, FileNotFoundException {
resultDir = opts.getResultDest();
testSuites = TreeMultimap.create(String.CASE_INSENSITIVE_ORDER, new Comparator<Test>() {
@Override
public int compare(Test o1, Test o2) {
return o1.getName().compareTo(o2.getName());
}
});
readDescriptions();
}
private void readDescriptions() throws JAXBException {
Reflections r = new Reflections(
new ConfigurationBuilder()
.filterInputsBy(new FilterBuilder().include("net.shipilev.concurrent.torture.desc.*"))
.setUrls(ClasspathHelper.forClassLoader())
.setScanners(new ResourcesScanner()));
Set<String> resources = r.getResources(new Predicate<String>() {
@Override
public boolean apply(String s) {
return s != null && s.endsWith(".xml");
}
});
for (String res : resources) {
loadDescription(res);
}
}
private void loadDescription(String name) throws JAXBException {
Testsuite suite = unmarshal(Testsuite.class, this.getClass().getResourceAsStream("/" + name));
for (Test t : suite.getTest()) {
testSuites.put(suite.getName(), t);
}
}
public void parse() throws FileNotFoundException, JAXBException {
File[] files = new File(resultDir).listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith("xml");
}
});
Arrays.sort(files);
Map<String, Result> results = new HashMap<String, Result>();
for (File f : files) {
Result r = unmarshal(Result.class, new FileInputStream(f));
results.put(r.getName(), r);
}
PrintWriter output = new PrintWriter(resultDir + "/index.html");
output.println("<html>");
output.println("<head><title>Java Concurrency Torture report</title></head>");
output.println("<body>");
for (String k : testSuites.keySet()) {
Collection<Test> tests = testSuites.get(k);
output.println("<h1>Suite \"" + k + "\"</h1>");
for (Test test : tests) {
Result result = results.get(test.getName());
if (result != null) {
parse(output, result, test);
} else {
output.println("Missing description for " + test.getName());
}
}
}
// output.println("Missing description for " + r.getName());
// System.err.println("Missing description for " + r.getName());
output.println("<p>Please report the errors in test grading to <a href='https://github.com/shipilev/java-concurrency-torture/issues'>https://github.com/shipilev/java-concurrency-torture/issues</a></p>");
output.println("</body>");
output.println("</html>");
output.close();
}
public void parse(PrintWriter output, Result r, Test test) throws FileNotFoundException, JAXBException {
output.println("<h2>" + r.getName() + "</h2>");
output.println("<p>" + test.getDescription() + "</p>");
output.println("<table width=100%>");
output.println("<tr>");
output.println("<th width=250>Observed state</th>");
output.println("<th width=50>Occurrence</th>");
output.println("<th width=50>Expectation</th>");
output.println("<th>Interpretation</th>");
output.println("<th width=50>Refs</th>");
output.println("</tr>");
List<State> unmatchedStates = new ArrayList<State>();
unmatchedStates.addAll(r.getState());
for (Case c : test.getCase()) {
boolean matched = false;
for (State s : r.getState()) {
if (c.getMatch().contains(s.getId())) {
// match!
output.println("<tr bgColor=" + selectHTMLColor(c.getExpect(), s.getCount() == 0) + ">");
output.println("<td>" + s.getId() + "</td>");
output.println("<td align=center>" + s.getCount() + "</td>");
output.println("<td align=center>" + c.getExpect() + "</td>");
output.println("<td>" + c.getDescription() + "</td>");
output.println("<td bgColor='white'>");
List<Ref> list = c.getRefs();
for (int i = 0; i < list.size(); i++) {
output.println("<a href=\"" + list.get(i).getUrl() + "\">[" + (i+1) + "]</a>");
}
output.println("</td>");
output.println("</tr>");
matched = true;
unmatchedStates.remove(s);
}
}
if (!matched) {
for (String m : c.getMatch()) {
output.println("<tr bgColor=" + selectHTMLColor(c.getExpect(), true) + ">");
output.println("<td>" + m + "</td>");
output.println("<td align=center>" + 0 + "</td>");
output.println("<td align=center>" + c.getExpect() + "</td>");
output.println("<td>" + c.getDescription() + "</td>");
output.println("<td bgColor='white'>");
List<Ref> list = c.getRefs();
for (int i = 0; i < list.size(); i++) {
output.println("<a href=\"" + list.get(i).getUrl() + "\">[" + (i+1) + "]</a>");
}
output.println("</td>");
output.println("</tr>");
}
}
}
for (State s : unmatchedStates) {
output.println("<tr bgColor=" + selectHTMLColor(test.getUnmatched().getExpect(), s.getCount() == 0) + ">");
output.println("<td>" + s.getId() + "</td>");
output.println("<td align=center>" + s.getCount() + "</td>");
output.println("<td align=center>" + test.getUnmatched().getExpect() + "</td>");
output.println("<td>" + test.getUnmatched().getDescription() + "</td>");
output.println("<td bgColor='white'>");
List<Ref> list = test.getUnmatched().getRefs();
for (int i = 0; i < list.size(); i++) {
output.println("<a href=\"" + list.get(i).getUrl() + "\">[" + (i+1) + "]</a>");
}
output.println("</td>");
output.println("</tr>");
}
output.println("</table>");
}
public String selectHTMLColor(ExpectType type, boolean isZero) {
String rgb = Integer.toHexString(selectColor(type, isZero).getRGB());
return "#" + rgb.substring(2, rgb.length());
}
public Color selectColor(ExpectType type, boolean isZero) {
switch (type) {
case REQUIRED:
return isZero ? Color.RED : Color.GREEN;
case ACCEPTABLE:
return isZero ? Color.LIGHT_GRAY : Color.GREEN;
case FORBIDDEN:
return isZero ? Color.LIGHT_GRAY : Color.RED;
case KNOWN_ACCEPTABLE:
return isZero ? Color.LIGHT_GRAY : Color.CYAN;
case KNOWN_FORBIDDEN:
return isZero ? Color.LIGHT_GRAY : Color.YELLOW;
default:
throw new IllegalStateException();
}
}
public <T> T unmarshal(Class<T> docClass, InputStream inputStream)
throws JAXBException {
String packageName = docClass.getPackage().getName();
JAXBContext jc = JAXBContext.newInstance(packageName);
Unmarshaller u = jc.createUnmarshaller();
@SuppressWarnings("unchecked")
T unmarshal = (T) u.unmarshal(inputStream);
return unmarshal;
}
}