001package org.w3.ldp.testsuite.reporter; 002 003import java.io.IOException; 004import java.lang.reflect.Method; 005import java.util.ArrayList; 006import java.util.Arrays; 007import java.util.Calendar; 008import java.util.GregorianCalendar; 009import java.util.Iterator; 010import java.util.List; 011import java.util.Map; 012 013import org.testng.annotations.Test; 014import org.w3.ldp.testsuite.LdpTestSuite; 015import org.w3.ldp.testsuite.annotations.SpecTest; 016import org.w3.ldp.testsuite.annotations.SpecTest.METHOD; 017import org.w3.ldp.testsuite.vocab.LDP; 018import org.w3.ldp.testsuite.vocab.TestDescription; 019 020import com.hp.hpl.jena.rdf.model.Literal; 021import com.hp.hpl.jena.rdf.model.Property; 022import com.hp.hpl.jena.rdf.model.RDFList; 023import com.hp.hpl.jena.rdf.model.Resource; 024import com.hp.hpl.jena.rdf.model.ResourceFactory; 025import com.hp.hpl.jena.sparql.vocabulary.EARL; 026import com.hp.hpl.jena.vocabulary.DCTerms; 027import com.hp.hpl.jena.vocabulary.RDF; 028import com.hp.hpl.jena.vocabulary.RDFS; 029import com.hp.hpl.jena.vocabulary.TestManifest; 030 031public class LdpEarlTestManifest extends AbstractEarlReporter { 032 033 private static String namespace = LDP.LDPT_NAMESPACE; 034 035 private static final Property declaredInClass = ResourceFactory 036 .createProperty(namespace + "declaredInClass"); 037 private static final Property declaredTestCase = DCTerms.source; 038 private static final Property conformanceLevel = ResourceFactory 039 .createProperty(namespace + "conformanceLevel"); 040 041 /** 042 * A link to the published Javadoc for the test case (which in turn links to 043 * the source code). 044 */ 045 private static final Property documentation = ResourceFactory 046 .createProperty(namespace + "documentation"); 047 048 /** 049 * The test method: {@link #automated}, {@link #manual}, {@link #clientOnly}, 050 * or {@link #notImplemented}. 051 * 052 * <p> 053 * earl:mode is inappropriate here since its domain is earl:Assertion. 054 * </p> 055 * 056 * @see SpecTest#testMethod() 057 * @see <a href="http://www.w3.org/TR/EARL10-Schema/#mode">EARL schema - 058 * earl:mode</a> 059 */ 060 private static final Property testMethod = ResourceFactory 061 .createProperty(namespace + "testMethod"); 062 063 /** 064 * @see SpecTest.METHOD#AUTOMATED 065 */ 066 private static final Resource automated = ResourceFactory 067 .createResource(namespace + "automated"); 068 069 /** 070 * @see SpecTest.METHOD#MANUAL 071 */ 072 private static final Resource manual = ResourceFactory 073 .createResource(namespace + "manual"); 074 075 /** 076 * @see SpecTest.METHOD#CLIENT_ONLY 077 */ 078 private static final Resource clientOnly = ResourceFactory 079 .createResource(namespace + "clientOnly"); 080 081 /** 082 * @see SpecTest.METHOD#NOT_IMPLEMENTED 083 */ 084 private static final Resource notImplemented = ResourceFactory 085 .createResource(namespace + "notImplemented"); 086 /** 087 * @see SpecTest.METHOD#INDIRECT 088 */ 089 private static final Resource indirect = ResourceFactory 090 .createResource(namespace + "indirect"); 091 092 /** 093 * @see SpecTest.steps 094 */ 095 private static final Property steps = ResourceFactory 096 .createProperty(namespace + "steps"); 097 098 /** 099 * List of GROUPS to include in reporting 100 */ 101 private static List<String> conformanceLevels = new ArrayList<String>(); 102 103 public void setConformanceLevels(List<String> list){ 104 conformanceLevels = list; 105 } 106 107 public void generate(Map<Class<?>, String> classes, String title) { 108 try { 109 createWriter(LdpTestSuite.OUTPUT_DIR, title); 110 System.out.println("Writing test manifest..."); 111 createModel(); 112 writeTestClasses(classes); 113 write(); 114 System.out.println("Done!"); 115 endWriter(); 116 } catch (IOException e) { 117 e.printStackTrace(System.err); 118 System.exit(1); 119 } 120 } 121 122 public void setNamespaceUri(String name){ 123 namespace = name; 124 } 125 126 private void writeManifest(ArrayList<Resource> testcases, String localName, String label, String description) { 127 if (testcases.size() == 0) return; 128 129 Resource manifest = model.createResource(namespace + localName+"Manifest", 130 TestManifest.Manifest); 131 manifest.addProperty(DCTerms.title, label); 132 manifest.addProperty(TestManifest.name, label); 133 manifest.addProperty(RDFS.comment, description); 134 Resource[] ra={}; 135 RDFList l = model.createList(testcases.toArray(ra)); 136 manifest.addProperty(TestManifest.entries, l); 137 } 138 139 private <T> void writeInfo(Class<T> testClass, String title, String description) { 140 ArrayList<ArrayList<Resource>> conformanceClasses = new ArrayList<ArrayList<Resource>>(); 141 conformanceClasses.add(new ArrayList<Resource>()); 142 conformanceClasses.add(new ArrayList<Resource>()); 143 conformanceClasses.add(new ArrayList<Resource>()); 144 conformanceClasses.add(new ArrayList<Resource>()); 145 146 String className = testClass.getCanonicalName(); 147 Method[] methods = testClass.getMethods(); 148 for (Method method : methods) { 149 if (method.isAnnotationPresent(Test.class)) { 150 generateInformation(method, className, conformanceClasses); 151 } 152 } 153 writeManifest(conformanceClasses.get(LdpTestCaseReporter.MUST), title 154 + "-MUST", title + " (MUST)", description 155 + " MUST conformance tests."); 156 writeManifest(conformanceClasses.get(LdpTestCaseReporter.SHOULD), title 157 + "-SHOULD", title + " (SHOULD)", description 158 + " SHOULD conformance tests."); 159 writeManifest(conformanceClasses.get(LdpTestCaseReporter.MAY), title 160 + "-MAY", title + " (MAY)", description 161 + " MAY conformance tests."); 162 writeManifest(conformanceClasses.get(LdpTestCaseReporter.OTHER), title 163 + "-OTHER", title + " (OTHER)", description 164 + " No official conformance status or test case is extension or incomplete."); 165 } 166 167 @SuppressWarnings("incomplete-switch") 168 private Resource generateInformation(Method method, String className, 169 ArrayList<ArrayList<Resource>> conformanceClasses) { 170 SpecTest testLdp = null; 171 Test test = null; 172 if (method.getAnnotation(SpecTest.class) != null 173 && method.getAnnotation(Test.class) != null) { 174 testLdp = method.getAnnotation(SpecTest.class); 175 test = method.getAnnotation(Test.class); 176 if(!testLdp.testMethod().equals(METHOD.INDIRECT)){ 177 178 Resource testCaseResource = createResource(className, method, test, testLdp, conformanceClasses); 179 testCaseResource.addProperty(RDF.type, EARL.TestCase); 180 switch (testLdp.testMethod()) { 181 case AUTOMATED: 182 testCaseResource.addProperty(testMethod, automated); 183 break; 184 case MANUAL: 185 testCaseResource.addProperty(testMethod, manual); 186 break; 187 case NOT_IMPLEMENTED: 188 testCaseResource.addProperty(testMethod, notImplemented); 189 break; 190 case CLIENT_ONLY: 191 testCaseResource.addProperty(testMethod, clientOnly); 192 break; 193 } 194 return testCaseResource; 195 } else { // for Indirect Tests 196 Resource indirectResource = createResource(className, method, test, testLdp, conformanceClasses); 197 indirectResource.addProperty(RDF.type, EARL.TestRequirement); 198 indirectResource.addProperty(testMethod, indirect); 199 if(testLdp.coveredByTests().length > 0 && testLdp.coveredByGroups().length > 0) { 200 for(Class<?> coverTest : testLdp.coveredByTests()) { 201 Method[] classMethod = coverTest.getDeclaredMethods(); 202 for(Method m : classMethod) { 203 if(m.getAnnotation(Test.class) != null) { 204 String group = Arrays.toString(m.getAnnotation(Test.class).groups()); 205 for(String groupCover : testLdp.coveredByGroups()) { 206 if(group.contains(groupCover)) { 207 String testCaseName = createTestCaseName(m.getDeclaringClass().getCanonicalName(), m.getName()); 208 String testCaseURL = namespace + testCaseName; 209 indirectResource.addProperty(DCTerms.hasPart, testCaseURL); 210 211 } 212 } 213 } 214 } 215 } 216 } 217 return indirectResource; 218 } 219 } 220 return null; 221 } 222 223 private Resource createResource(String className, Method method, Test test, SpecTest testLdp, 224 ArrayList<ArrayList<Resource>> conformanceClasses) { 225 String testCaseName = createTestCaseName(className, method.getName()); 226 227 // Client only tests should be managed in separate EARL manifest 228 if (testLdp.testMethod() == METHOD.CLIENT_ONLY) { 229 System.err.println("Wrongly received CLIENT_ONLY test for "+testCaseName+ 230 ". Client-only tests should be defined in separate RDF manifest file."); 231 } 232 233 String allGroups = groups(test.groups()); 234 235 Calendar cal = GregorianCalendar.getInstance(); 236 Literal date = model.createTypedLiteral(cal); 237 238 239 String testCaseDeclaringName = createTestCaseName(method.getDeclaringClass().getCanonicalName(), method.getName()); 240 String testCaseURL = namespace + testCaseName; 241 String testCaseDeclaringURL = namespace + testCaseDeclaringName; 242 243 Resource resource = model.createResource(testCaseURL); 244 resource.addProperty(RDFS.label, testCaseName); 245 resource.addProperty(TestManifest.name, testCaseName); 246 resource.addProperty(DCTerms.date, date); 247 248 resource.addProperty(RDFS.comment, test.description()); 249 if (allGroups != null) 250 resource.addProperty(DCTerms.subject, allGroups); 251 252 boolean added = false; 253 if (testLdp.approval() != SpecTest.STATUS.WG_EXTENSION) { 254 for (String group: test.groups()) { 255 group = group.trim(); 256 if (conformanceLevels.contains(group)) { 257 resource.addProperty(conformanceLevel, model.createResource(namespace + group)); 258 conformanceClasses.get(LdpTestCaseReporter.getConformanceIndex(group)).add(resource); 259 added = true; 260 } 261 } 262 } 263 // If not in a conformance group, add to general other bucket 264 if (!added) { 265 conformanceClasses.get(LdpTestCaseReporter.OTHER).add(resource); 266 } 267 268 String[] stepsArr = testLdp.steps(); 269 if (stepsArr != null && stepsArr.length > 0) { 270 ArrayList<Literal> arr = new ArrayList<Literal>(); 271 for (String s: stepsArr) { 272 arr.add(model.createLiteral(s)); 273 } 274 RDFList l = model.createList(arr.iterator()); 275 resource.addProperty(steps, l); 276 } 277 278 // Leave action property only to make earl-report happy 279 resource.addProperty(TestManifest.action, ""); 280 281 switch (testLdp.approval()) { 282 case WG_APPROVED: 283 resource.addProperty(TestDescription.reviewStatus, TestDescription.approved); 284 break; 285 case WG_PENDING: 286 resource.addProperty(TestDescription.reviewStatus, TestDescription.unreviewed); 287 break; 288 default: 289 resource.addProperty(TestDescription.reviewStatus, TestDescription.unreviewed); 290 break; 291 } 292 293 resource.addProperty(declaredInClass, className); 294 resource.addProperty(declaredTestCase, model.createResource(testCaseDeclaringURL)); 295 Resource specRef = null; 296 if (testLdp.specRefUri() != null) { 297 specRef = model.createResource(testLdp.specRefUri()); 298 resource.addProperty(RDFS.seeAlso, specRef); 299 } 300 301 if (test.description() != null && test.description().length() > 0) { 302 Resource excerpt = model.createResource(TestDescription.Excerpt); 303 excerpt.addLiteral(TestDescription.includesText, test.description()); 304 if (specRef != null) { 305 excerpt.addProperty(RDFS.seeAlso, specRef); 306 } 307 resource.addProperty(TestDescription.specificationReference, excerpt); 308 } 309 310 resource.addProperty(documentation, 311 model.createResource(ReportUtils.getJavadocLink(method))); 312 313 return resource; 314 315 } 316 317 private String groups(String[] list) { 318 if (list.length == 0) 319 return null; 320 String retList = ""; 321 for (int i = 0; i < list.length; i++) { 322 if (i == list.length - 1) 323 retList += list[i]; 324 else 325 retList += list[i] + ", "; 326 } 327 return retList; 328 } 329 330 private void writeTestClasses(Map<Class<?>, String> classes) { 331 // These are put in order so they are presented properly on earl-report 332 // writeInfo(commonResourceTest, testcases); 333 // writeInfo(rdfSourceTest, testcases); 334 // writeInfo(commonContainerTest, testcases); 335 Iterator<Class<?>> classNames = classes.keySet().iterator(); 336 String info = ""; 337 while(classNames.hasNext()){ 338 Class<?> classVal = classNames.next(); 339 info = classes.get(classVal); 340 String[] split = info.split(":"); 341 writeInfo(classVal, split[0], split[1]); 342 } 343 } 344 345 @Override 346 protected String getFilename() { 347 return "ldp-earl-manifest"; 348 } 349}