/* (c) 2014-2016 Open Source Geospatial Foundation - all rights reserved * (c) 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.platform.resource; import static org.junit.Assert.*; import static org.junit.Assume.*; import static org.hamcrest.Matchers.*; import static org.geoserver.platform.resource.ResourceMatchers.*; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; import java.util.List; import org.apache.commons.io.IOUtils; import org.geoserver.platform.resource.Resource; import org.geoserver.platform.resource.Resource.Type; import org.junit.Rule; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; /** * JUnit Theory test class for Resource invariants. Subclasses should provide representative * DataPoints to test. * * @author Kevin Smith, Boundless * */ @RunWith(Theories.class) public abstract class ResourceTheoryTest { @Rule public ExpectedException exception = ExpectedException.none(); protected abstract Resource getResource(String path) throws Exception; /** * @return a resource that is not a data point of type DIRECTORY. */ protected abstract Resource getDirectory(); /** * @return a resource that is not a data point of type RESOURCE. */ protected abstract Resource getResource(); /** * @return a resource that is not a data point of type UNDEFINED. */ protected abstract Resource getUndefined(); @Theory public void theoryNotNull(String path) throws Exception { Resource res = getResource(path); assertThat(res, notNullValue()); } @Theory public void theoryExtantHaveDate(String path) throws Exception { Resource res = getResource(path); assumeThat(res, defined()); long result = res.lastmodified(); assertThat(result, notNullValue()); } @Theory public void theoryHaveSamePath(String path) throws Exception { Resource res = getResource(path); String result = res.path(); assertThat(result, is(equalTo(path))); } @Theory public void theoryHaveName(String path) throws Exception { Resource res = getResource(path); String result = res.name(); assertThat(result, notNullValue()); } @Theory public void theoryNameIsEndOfPath(String path) throws Exception { Resource res = getResource(path); List<String> elements = Paths.names(path); String lastElement = elements.get(elements.size()-1); String result = res.name(); assertThat(result, equalTo(lastElement)); } @Theory public void theoryLeavesHaveIstream(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(resource())); try (InputStream result = res.in()) { assertThat(result, notNullValue()); } } @Theory public void theoryLeavesHaveOstream(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(resource())); try (OutputStream result = res.out()) { assertThat(result, notNullValue()); } } @Theory public void theoryUndefinedHaveIstreamAndBecomeResource(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(undefined())); try (InputStream result = res.in()) { assertThat(result, notNullValue()); assertThat(res, is(resource())); } } @Theory public void theoryUndefinedHaveOstreamAndBecomeResource(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(undefined())); try (OutputStream result = res.out()) { assertThat(result, notNullValue()); assertThat(res, is(resource())); } } @Theory public void theoryNonDirectoriesPersistData(String path) throws Exception { Resource res = getResource(path); assumeThat(res, not(directory())); byte[] test = {42, 29, 32, 120, 69, 0, 1}; try (OutputStream ostream = res.out()) { ostream.write(test); } byte[] result=new byte[test.length]; try (InputStream istream = res.in()) { istream.read(result); assertThat(istream.read(), is(-1)); } assertThat(result, equalTo(test)); } @Theory public void theoryDirectoriesHaveNoIstreams(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(directory())); exception.expect(IllegalStateException.class); res.in().close(); } @Theory public void theoryDirectoriesHaveNoOstream(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(directory())); exception.expect(IllegalStateException.class); res.out().close(); } @Theory public void theoryLeavesHaveEmptyListOfChildren(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(resource())); Collection<Resource> result = res.list(); assertThat(result, empty()); } @Theory public void theoryUndefinedHaveEmptyListOfChildren(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(undefined())); Collection<Resource> result = res.list(); assertThat(result, empty()); } @Theory public void theoryDirectoriesHaveChildren(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(directory())); Collection<Resource> result = res.list(); assertThat(result, notNullValue()); } @Theory public void theoryChildrenKnowTheirParents(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(directory())); Collection<Resource> children = res.list(); assumeThat(children, not(empty())); // Make sure this resource has children for(Resource child: children) { Resource parent = child.parent(); assertThat(parent, equalTo(res)); } } @Theory public void theoryParentsKnowTheirChildren(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(directory())); Resource parent = res.parent(); assumeThat(path,parent, notNullValue()); // Make sure this resource has a parent Collection<Resource> result = parent.list(); assertThat(path,result, hasItem(res)); // this assumed equals was written! } @Theory public void theorySamePathGivesEquivalentResource(String path) throws Exception { Resource res1 = getResource(path); Resource res2 = getResource(path); assertThat(res2, equalTo(res1)); } @Theory public void theoryParentIsDirectory(String path) throws Exception { Resource res = getResource(path); Resource parent = res.parent(); assumeThat(path+" not root", parent, notNullValue()); if (res.getType() != Type.UNDEFINED) { assertThat(path+" directory",parent, is(directory())); } } @Theory public void theoryHaveFile(String path) throws Exception { Resource res = getResource(path); assumeThat(res, resource()); File result = res.file(); assertThat(result, notNullValue()); } @Theory public void theoryHaveDir(String path) throws Exception { Resource res = getResource(path); assumeThat(res, directory()); File result = res.dir(); assertThat(result, notNullValue()); } @Theory public void theoryDeletedResourcesAreUndefined(String path) throws Exception { Resource res = getResource(path); assumeThat(res, resource()); assertThat(res.delete(), is(true)); assertThat(res, undefined()); } @Theory public void theoryUndefinedNotDeleted(String path) throws Exception { Resource res = getResource(path); assumeThat(res, undefined()); assertThat(res.delete(), is(false)); assertThat(res, undefined()); } @Theory public void theoryRenamedAreUndefined(String path) throws Exception { Resource res = getResource(path); assumeThat(res, defined()); Resource target = getUndefined(); assertThat(res.renameTo(target), is(true)); assertThat(res, undefined()); } @Theory public void theoryRenamedResourcesAreEquivalent(String path) throws Exception { final Resource res = getResource(path); assumeThat(res, resource()); final byte[] expectedContent; try(InputStream in = res.in()) { expectedContent = IOUtils.toByteArray(in); } final Resource target = getUndefined(); assertThat(res.renameTo(target), is(true)); assertThat(target, resource()); final byte[] resultContent; try(InputStream in = target.in()) { resultContent = IOUtils.toByteArray(in); } assertThat(resultContent, equalTo(expectedContent)); } @Theory public void theoryNonDirectoriesHaveFileWithSameContents(String path) throws Exception { Resource res = getResource(path); assumeThat(res, not(directory())); byte[] test = {42, 29, 32, 120, 69, 0, 1}; try (OutputStream ostream = res.out()) { ostream.write(test); } byte[] result=new byte[test.length]; try (InputStream istream = new FileInputStream(res.file())) { istream.read(result); assertThat(istream.read(), is(-1)); } assertThat(result, equalTo(test)); } @Theory public void theoryDirectoriesHaveFileWithSameNamedChildren(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(directory())); File dir = res.dir(); Collection<Resource> resChildren = res.list(); String[] fileChildrenNames = dir.list(); String[] resChildrenNames = new String[resChildren.size()]; int i=0; for (Resource child: resChildren) { resChildrenNames[i]=child.name(); i++; } assertThat(fileChildrenNames, arrayContainingInAnyOrder(resChildrenNames)); } // This is the behaviour of the file based implementation. Should this be required or left // undefined with clear documentation indicating that it's implementation dependent? //@Ignore @Theory public void theoryAlteringFileAltersResource(String path) throws Exception { Resource res = getResource(path); assumeThat(res, not(directory())); byte[] testResource = {42, 29, 32, 120, 69, 0, 1}; byte[] testFile = {27, 3, 5, 90, -120, -3}; // Write to resource try (OutputStream ostream = res.out()) { ostream.write(testResource); } // Write to file try (OutputStream ostream = new FileOutputStream(res.file())) { ostream.write(testFile); } // Read from resource byte[] result=new byte[testFile.length]; try (InputStream istream = res.in()) { istream.read(result); assertThat(istream.read(), is(-1)); } // Should be what was written to the file assertThat(result, equalTo(testFile)); } // This is the behaviour of the file based implementation. Should this be required or left // undefined with clear documentation indicating that it's implementation dependent? //@Ignore @Theory public void theoryAddingFileToDirectoryAddsResource(String path) throws Exception { Resource res = getResource(path); assumeThat(res, is(directory())); File dir = res.dir(); File file = new File(dir, "newFileCreatedDirectly"); assumeTrue(file.createNewFile()); Resource child = getResource(Paths.path(res.path(), "newFileCreatedDirectly")); Collection<Resource> children = res.list(); assertThat(child, is(defined())); assertThat(children, hasItem(child)); } @Theory public void theoryMultipleOutputStreamsAreSafe(String path) throws Exception { final Resource res = getResource(path); assumeThat(res, is(resource())); final byte[] thread1Content = "This is the content for thread 1".getBytes(); final byte[] thread2Content = "Thread 2 has this content".getBytes(); try (OutputStream out1 = res.out()) { try (OutputStream out2 = res.out()) { for(int i=0; i<thread1Content.length || i<thread2Content.length; i++) { if(i<thread1Content.length) { out1.write(thread1Content[i]); } if(i<thread2Content.length) { out2.write(thread2Content[i]); } } } } final byte[] resultContent; try (InputStream in = res.in()) { resultContent = IOUtils.toByteArray(in); } // 2 streams being written to concurrently should result in the resource containing // what was written to one of the two streams. assertThat(resultContent, anyOf(equalTo(thread1Content), equalTo(thread2Content))); } @Theory public void theoryDoubleClose(String path) throws Exception { final Resource res = getResource(path); assumeThat(res, is(resource())); OutputStream os = res.out(); os.close(); os.close(); } @Theory public void theoryRecursiveDelete(String path) throws Exception { final Resource res = getResource(path); assumeThat(res, is(directory())); assumeThat(res, is(directory())); Collection<Resource> result = res.list(); assumeThat(result.size(), greaterThan(0)); assertTrue(res.delete()); } @Theory public void theoryRootSlashIsIgnored(String path) throws Exception { final Resource res = getResource(path); final Resource res2 = getResource("/" + path); assertTrue(res.equals(res2)); assertTrue(res.path().equals(res2.path())); } }