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}