/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.jena.rdfxml.xmloutput; import java.io.* ; import java.util.Random ; import java.util.Vector ; import junit.framework.Test ; import junit.framework.TestSuite ; import org.apache.jena.rdf.model.* ; import org.apache.jena.rdf.model.test.ModelTestBase ; import org.apache.jena.shared.JenaException ; import org.apache.jena.vocabulary.RDFSyntax ; import org.slf4j.Logger ; import org.slf4j.LoggerFactory ; /** * This will test any Writer and Reader pair. * It writes out a random model, and reads it back in. * The test fails if the models are not 'the same'. * Quite what 'the same' means is debatable. */ public class testWriterAndReader extends ModelTestBase implements RDFErrorHandler { static private boolean showProgress = false; //static private boolean errorDetail = false; static private int firstTest = 4; static private int lastTest = 9; static private int repetitionsJ = 6; protected static Logger logger = LoggerFactory.getLogger( testWriterAndReader.class ); final String lang; final int fileNumber; final int options; String test; testWriterAndReader( String name, String lang, int fileNumber ) { this( name, lang, fileNumber, 0 ); } testWriterAndReader(String name, String lang, int fileNumber, int options) { super( name ); this.lang = lang; this.fileNumber = fileNumber; this.options = options; } @Override public String toString() { return getName() + " " + lang + " t" + fileNumber + "000.rdf" + (options != 0 ? ("[" + options + "]") : ""); } static Test suiteN_TRIPLE() { return baseSuite( "N-TRIPLE" ); } static TestSuite suiteXML() { TestSuite baseTests = baseSuite( "RDF/XML" ); baseTests.addTestSuite( TestXMLFeatures_XML.class ); baseTests.addTest( addXMLtests( "RDF/XML", false ) ); return baseTests; } static Test suiteXML_ABBREV() { TestSuite suite = baseSuite( "RDF/XML-ABBREV" ); suite.addTestSuite( TestXMLFeatures_XML_ABBREV.class ); suite.addTestSuite( TestXMLAbbrev.class ); suite.addTest( addXMLtests( "RDF/XML-ABBREV", false ) ); return suite; } public static TestSuite repeatedAbbrevSuite() { TestSuite suite = baseSuite( "RDF/XML-ABBREV" ); suite.addTestSuite( TestXMLFeatures_XML_ABBREV.class ); suite.addTestSuite( TestXMLAbbrev.class ); suite.addTest( addXMLtests( "RDF/XML-ABBREV", true ) ); return suite; } static TestSuite baseSuite( String lang ) { TestSuite langsuite = new TestSuite(); langsuite.setName( lang ); langsuite.addTest( new TestWriterInterface( "testWriting", lang ) ); langsuite.addTest( new TestWriterInterface( "testLineSeparator", lang ) ); return langsuite; } public static class TestXMLFeatures_XML extends TestXMLFeatures { public TestXMLFeatures_XML( String name ) { super( name, "RDF/XML" ); } } public static class TestXMLFeatures_XML_ABBREV extends TestXMLFeatures { public TestXMLFeatures_XML_ABBREV( String name ) { super( name, "RDF/XML-ABBREV" ); } } static private boolean nBits( int i, int [] ok ) { int bitCount = 0; while (i > 0) { if ((i & 1) == 1) bitCount += 1; i >>= 1; } for ( int anOk : ok ) { if ( bitCount == anOk ) { return true; } } return false; } private static TestSuite addXMLtests( String lang, boolean lots ) { TestSuite suite = new TestSuite(); int optionLimit = (lang.equals( "RDF/XML-ABBREV" ) ? 1 << blockRules.length : 2); for (int fileNumber = firstTest; fileNumber <= lastTest; fileNumber++) { suite.addTest(new testWriterAndReader("testRandom", lang, fileNumber ) ); suite.addTest( new testWriterAndReader( "testLongId", lang, fileNumber ) ); for (int optionMask = 1; optionMask < optionLimit; optionMask += 1) { if (lots || nBits( optionMask, new int[] { 1, /* 2,3,4,5, */ 6,7 } )) suite.addTest( createTestOptions( lang, fileNumber, optionMask ) ); } } return suite; } private static testWriterAndReader createTestOptions( String lang, int fileNumber, int optionMask ) { return new testWriterAndReader( "testOptions " + fileNumber + " " + optionMask, lang, fileNumber, optionMask ) { @Override public void runTest() throws IOException { testOptions(); } }; } public void testRandom() throws IOException { doTest( new String[] {}, new Object[] {} ); } public void testLongId() throws IOException { doTest( new String[] {"longId"}, new Object[] {Boolean.TRUE} ); } static Resource [] blockRules = { RDFSyntax.parseTypeLiteralPropertyElt, RDFSyntax.parseTypeCollectionPropertyElt, RDFSyntax.propertyAttr, RDFSyntax.sectionReification, RDFSyntax.sectionListExpand, RDFSyntax.parseTypeResourcePropertyElt, }; public void testOptions() throws IOException { Vector<Resource> v = new Vector<>(); for (int i = 0; i < blockRules.length; i += 1) { if ((options & (1 << i)) != 0) v.add( blockRules[i] ); } Resource blocked[] = new Resource[v.size()]; v.copyInto( blocked ); doTest( new String[] { "blockRules" }, new Resource[][] { blocked } ); } public void doTest( String[] propNames, Object[] propVals ) throws IOException { test( lang, 35, 1, propNames, propVals ); } static final String baseUris[] = { "http://foo.com/Hello", }; ByteArrayOutputStream tmpOut; /** * @param rwLang Use Writer for this lang * @param seed A seed for the random number generator * @param variationMax Number of random variations * @param wopName Property names to set on Writer * @param wopVal Property values to set on Writer */ public void test( String rwLang, int seed, int variationMax, String[] wopName, Object[] wopVal) throws IOException { Model m1 = createMemModel(); test = "testWriterAndReader lang=" + rwLang + " seed=" + seed; String filebase = "testing/regression/testWriterAndReader/"; if (showProgress) System.out.println("Beginning " + test); Random random = new Random(seed); RDFReader rdfRdr = m1.getReader( rwLang ); RDFWriter rdfWtr = m1.getWriter( rwLang ); setWriterOptionsAndHandlers( wopName, wopVal, rdfRdr, rdfWtr ); for (int variationIndex = 0; variationIndex < variationMax; variationIndex++) testVariation( filebase, random, rdfRdr, rdfWtr ); if (showProgress) System.out.println("End of " + test); } /** @param wopName @param wopVal @param rdfRdr @param rdfWtr */ private void setWriterOptionsAndHandlers( String[] wopName, Object[] wopVal, RDFReader rdfRdr, RDFWriter rdfWtr ) { rdfRdr.setErrorHandler( this ); rdfWtr.setErrorHandler( this ); if (wopName != null) for (int i = 0; i < wopName.length; i++) rdfWtr.setProperty( wopName[i], wopVal[i] ); } /** @param filebase @param random @param rdfRdr @param rdfWtr @throws FileNotFoundException @throws IOException */ private void testVariation( String filebase, Random random, RDFReader rdfRdr, RDFWriter rdfWtr ) throws FileNotFoundException, IOException { Model m1 = createMemModel(); Model m2; String fileName = "t" + (fileNumber * 1000) + ".rdf"; String baseUriRead; if (fileNumber < baseUris.length) baseUriRead = baseUris[fileNumber]; else baseUriRead = "http://foo.com/Hello"; try ( InputStream rdr = new FileInputStream( filebase + fileName ) ) { m1.read(rdr, baseUriRead); } for (int j = 0; j < repetitionsJ; j++) { String baseUriWrite = j % 2 == 0 ? baseUriRead : "http://bar.com/irrelevant"; int cn = (int) m1.size(); if ((j % 2) == 0 && j > 0) prune(m1, random, 1 + cn / 10); if ((j % 2) == 0 && j > 0) expand(m1, random, 1 + cn / 10); tmpOut = new ByteArrayOutputStream() ; rdfWtr.write(m1, tmpOut, baseUriWrite); tmpOut.flush() ; tmpOut.close() ; m2 = createMemModel(); //empty(m2); try ( InputStream in = new ByteArrayInputStream( tmpOut.toByteArray() ) ) { rdfRdr.read(m2, in, baseUriWrite); } Model s1 = m1; Model s2 = m2; /* System.err.println("m1:"); m1.write(System.err,"N-TRIPLE"); System.err.println("m2:"); m2.write(System.err,"N-TRIPLE"); System.err.println("="); */ // assertTrue( // "Comparison of file written out, and file read in.", // s1.isIsomorphicWith(s2)); assertIsoModels( "Comparison of file written out, and file read in.", s1, s2 ); // Free resources explicitily. tmpOut.reset() ; tmpOut = null ; } if (showProgress) { System.out.print("+"); System.out.flush(); } } static boolean linuxFileDeleteErrorFlag = false; /**Deletes count edges from m chosen by random. * @param count The number of statements to delete. * @param m A model with more than count statements. */ private void prune(Model m, Random random, int count) { // System.out.println("Pruning from " + (int)m.size() + " by " + cnt ); Statement toRemove[] = new Statement[count]; int sz = (int) m.size(); StmtIterator ss = m.listStatements(); try { for (int i = 0; i < count; i++) toRemove[i] = ss.nextStatement(); while (ss.hasNext()) { int ix = random.nextInt(sz); if (ix < count) toRemove[ix] = ss.nextStatement(); } } finally { ss.close(); } for (int i = 0; i < count; i++) m.remove( toRemove[i] ); // System.out.println("Reduced to " + (int)m.size() ); } /** * Adds count edges to m chosen by random. * * @param count The number of statements to add. * @param m A model with more than cnt statements. */ private void expand(Model m, Random random, int count) { // System.out.println("Expanding from " + (int)m.size() + " by " + cnt ); Resource subject[] = new Resource[count]; Property predicate[] = new Property[count]; RDFNode object[] = new RDFNode[count]; int sz = (int) m.size(); StmtIterator ss = m.listStatements(); try { for (int i = 0; i < count; i++) { Statement s = ss.nextStatement(); subject[i] = s.getSubject(); predicate[i] = s.getPredicate(); object[i] = s.getObject(); } while (ss.hasNext()) { Statement s = ss.nextStatement(); Resource subj = s.getSubject(); RDFNode obj = s.getObject(); int ix = random.nextInt(sz); if (ix < count) subject[ix] = subj; ix = random.nextInt(sz); if (ix < count) object[ix] = subj; ix = random.nextInt(sz); if (ix < count) predicate[ix] = s.getPredicate(); ix = random.nextInt(sz); if (ix < count) object[ix] = obj; if (obj instanceof Resource) { ix = random.nextInt(sz); if (ix < count) subject[ix] = (Resource) obj; } } } finally { ss.close(); } for (int i = 0; i < count; i++) m.add(subject[i], predicate[i], object[i]); // System.out.println("Expanded to " + (int)m.size() ); } /** report a warning * @param e an exception representing the error */ @Override public void warning(Exception e) { // logger.warn( toString() + " " + e.getMessage(), e ); System.out.println(new String(tmpOut.toString())); throw new JenaException( e ); } @Override public void error(Exception e) { fail(e.getMessage()); } @Override public void fatalError(Exception e) { error(e); throw new JenaException(e); } }