/** * Copyright 2008 The University of North Carolina at Chapel Hill * * 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 edu.unc.lib.dl.admin.collect; import static edu.unc.lib.dl.test.TestHelpers.setField; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMapOf; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Pattern; import org.apache.commons.io.FileUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import edu.unc.lib.dl.admin.collect.DepositBinCollector.ListFilesResult; import edu.unc.lib.dl.util.DepositStatusFactory; import edu.unc.lib.dl.util.PackagingType; /** * Note: powermock prevents some test coverage tools from working * * @author bbpennel * @date Jun 13, 2014 */ @RunWith(PowerMockRunner.class) @PrepareForTest({ FileUtils.class, DepositBinCollector.class }) public class DepositBinCollectorTest { @Rule public final TemporaryFolder tmpFolder = new TemporaryFolder(); private File depositsDirectory; private File binDirectory; @Mock private DepositStatusFactory depositStatusFactory; @Mock private DepositBinConfiguration config; private DepositBinCollector manager; @Before public void setup() throws Exception { initMocks(this); depositsDirectory = tmpFolder.newFolder("deposits"); binDirectory = tmpFolder.newFolder("bin"); List<String> binPaths = Arrays.asList(binDirectory.getAbsolutePath()); when(config.getPaths()).thenReturn(binPaths); when(config.getKeyLock()).thenReturn(new ReentrantLock()); Map<String, DepositBinConfiguration> configs = new HashMap<String, DepositBinConfiguration>(); configs.put("etd", config); manager = new DepositBinCollector(); setField(manager, "depositsDirectory", depositsDirectory); setField(manager, "depositStatusFactory", depositStatusFactory); setField(manager, "configs", configs); } @Test public void configTest() throws Exception { manager.setConfigPath("src/test/resources/depositBinConfig.json"); manager.init(); DepositBinConfiguration etdConfig = manager.getConfiguration("etd"); assertNotNull("Configuration was not loaded", etdConfig); assertEquals("Config name not assigned", "ETDs", etdConfig.getName()); assertNotNull("File name pattern not assigned", etdConfig.getFilePattern()); assertEquals("Packaging type was incorrect", PackagingType.PROQUEST_ETD.getUri(), etdConfig.getPackageType()); assertNotNull("Configuration was not loaded", manager.getConfiguration("stuff")); assertNull("Null expected for non-existent configuration", manager.getConfiguration("absent")); } @Test public void listFilesInvalidKeyTest() { assertNull(manager.listFiles("bad")); } @Test public void listFilesTest() throws Exception { addBinFiles(); when(config.hasFileFilters()).thenReturn(false); ListFilesResult files = manager.listFiles("etd"); List<File> appFiles = files.applicable; Collections.sort(appFiles); assertNotNull("Must return a list of files", files); assertEquals("Incorrect number of files return", 3, appFiles.size()); assertEquals("Expected file was not returned", binDirectory.getAbsolutePath() + "/testPackage1", appFiles.get(1) .getAbsolutePath()); assertEquals("Expected file was not returned", binDirectory.getAbsolutePath() + "/test2Package", appFiles.get(0) .getAbsolutePath()); assertEquals("Expected file was not returned", binDirectory.getAbsolutePath() + "/testPackage3", appFiles.get(2) .getAbsolutePath()); } @Test public void listFilesExcludeFileNameTest() throws Exception { addBinFiles(); when(config.getFilePattern()).thenReturn(Pattern.compile("testPackage\\d+$")); when(config.hasFileFilters()).thenReturn(true); ListFilesResult files = manager.listFiles("etd"); List<File> appFiles = files.applicable; Collections.sort(appFiles); assertEquals("Incorrect number of files return", 2, appFiles.size()); assertEquals("Expected file was not returned", binDirectory.getAbsolutePath() + "/testPackage1", appFiles.get(0) .getAbsolutePath()); assertEquals("Expected file was not returned", binDirectory.getAbsolutePath() + "/testPackage3", appFiles.get(1) .getAbsolutePath()); assertEquals("Incorrect number of files return", 1, files.nonapplicable.size()); assertEquals("Expected nonapplicable file was not returned", binDirectory.getAbsolutePath() + "/test2Package", files.nonapplicable.get(0).getAbsolutePath()); } @Test public void listFilesExcludeFileSizeTest() throws Exception { addBinFiles(); when(config.getMaxBytesPerFile()).thenReturn(1024L); when(config.hasFileFilters()).thenReturn(true); ListFilesResult files = manager.listFiles("etd"); List<File> appFiles = files.applicable; Collections.sort(appFiles); assertEquals("Incorrect number of files return", 2, appFiles.size()); assertEquals("Expected filed was not returned", binDirectory.getAbsolutePath() + "/testPackage1", appFiles.get(1) .getAbsolutePath()); assertEquals("Expected filed was not returned", binDirectory.getAbsolutePath() + "/test2Package", appFiles.get(0) .getAbsolutePath()); } @Test public void listFilesBinDoesNotExistTest() throws Exception { binDirectory.delete(); ListFilesResult files = manager.listFiles("etd"); verify(config).getPaths(); assertNull("Non-existent bin directory should return null", files); } @Test public void collectInvalidKeyTest() throws Exception { manager.collect(null, "bad", null); assertEquals("No deposits should have been created", 0, depositsDirectory.list().length); } @Test public void collectAllTest() throws Exception { addBinFiles(); manager.collect(null, "etd", new HashMap<String, String>()); File dataDir = new File(depositsDirectory.listFiles()[0], "data"); assertEquals("Incorrect number of files moved to data dir", 3, dataDir.list().length); assertEquals("Moved files should no longer be in the bin", 0, binDirectory.list().length); verify(depositStatusFactory).save(anyString(), anyMapOf(String.class, String.class)); } @Test public void collectFilteredTest() throws Exception { addBinFiles(); when(config.getFilePattern()).thenReturn(Pattern.compile("testPackage\\d+$")); when(config.hasFileFilters()).thenReturn(true); manager.collect(null, "etd", new HashMap<String, String>()); File dataDir = new File(depositsDirectory.listFiles()[0], "data"); assertEquals("Incorrect number of files moved to data dir", 2, dataDir.list().length); assertEquals("Filtered out file should remain", 1, binDirectory.list().length); verify(depositStatusFactory).save(anyString(), anyMapOf(String.class, String.class)); } @Test public void collectProvidedListTest() throws Exception { addBinFiles(); File binFiles[] = binDirectory.listFiles(); List<String> targetFiles = new ArrayList<String>(); for (File binFile : binFiles) { if (!"testPackage3".equals(binFile.getName())) { targetFiles.add(binFile.getAbsolutePath()); } } manager.collect(targetFiles, "etd", new HashMap<String, String>()); File dataDir = new File(depositsDirectory.listFiles()[0], "data"); assertEquals("Incorrect number of files moved to data dir", 2, dataDir.list().length); assertEquals("Filtered out file should remain in bin", 1, binDirectory.list().length); assertTrue("testPackage3".equals(binDirectory.list()[0])); verify(depositStatusFactory).save(anyString(), anyMapOf(String.class, String.class)); } @Test(expected = IOException.class) public void collectInvalidTargetTest() throws Exception { addBinFiles(); File binFiles[] = binDirectory.listFiles(); List<String> targetFiles = new ArrayList<String>(); for (File binFile : binFiles) { targetFiles.add(binFile.getAbsolutePath()); } File misplaced = tmpFolder.newFile("wrongPlace"); targetFiles.add(misplaced.getAbsolutePath()); try { manager.collect(targetFiles, "etd", new HashMap<String, String>()); } finally { assertEquals("Deposit directory should not have been created", 0, depositsDirectory.list().length); assertEquals("All files should have remained in bin", 3, binDirectory.list().length); assertTrue("File in non-bin path should be unaffected", misplaced.exists()); verify(depositStatusFactory, never()).save(anyString(), anyMapOf(String.class, String.class)); } } @Test(expected = IOException.class) public void collectNonExistentTargetTest() throws Exception { addBinFiles(); File binFiles[] = binDirectory.listFiles(); List<String> targetFiles = new ArrayList<String>(); for (File binFile : binFiles) { targetFiles.add(binFile.getAbsolutePath()); } targetFiles.add("/does/not/exist/dsfgkldmsflgkmdslfg"); try { manager.collect(targetFiles, "etd", new HashMap<String, String>()); } finally { assertEquals("Deposit directory should not have been created", 0, depositsDirectory.list().length); assertEquals("All files should have remained in bin", 3, binDirectory.list().length); verify(depositStatusFactory, never()).save(anyString(), anyMapOf(String.class, String.class)); } } @Test(expected = IOException.class) public void collectNotApplicableTargetTest() throws Exception { addBinFiles(); File binFiles[] = binDirectory.listFiles(); List<String> targetFiles = new ArrayList<String>(); for (File binFile : binFiles) { targetFiles.add(binFile.getAbsolutePath()); } when(config.getFilePattern()).thenReturn(Pattern.compile("testPackage\\d+$")); when(config.hasFileFilters()).thenReturn(true); try { manager.collect(targetFiles, "etd", new HashMap<String, String>()); } finally { assertEquals("Deposit directory should not have been created", 0, depositsDirectory.list().length); assertEquals("All files should have remained in bin", 3, binDirectory.list().length); verify(depositStatusFactory, never()).save(anyString(), anyMapOf(String.class, String.class)); } } @Test(expected = IOException.class) public void collectCopyProblemTest() throws Exception { addBinFiles(); mockStatic(FileUtils.class); when(FileUtils.contentEquals(any(File.class), any(File.class))).thenReturn(true).thenReturn(false); try { manager.collect(null, "etd", new HashMap<String, String>()); } finally { verifyStatic(times(2)); FileUtils.contentEquals(any(File.class), any(File.class)); verifyStatic(); FileUtils.deleteDirectory(any(File.class)); verify(depositStatusFactory, never()).save(anyString(), anyMapOf(String.class, String.class)); assertEquals("No files should have been removed from bin", 3, binDirectory.list().length); } } private void addBinFiles() throws Exception { File packageFile = new File(binDirectory, "testPackage1"); populateFile(packageFile, 1000); packageFile = new File(binDirectory, "test2Package"); populateFile(packageFile, 700); packageFile = new File(binDirectory, "testPackage3"); populateFile(packageFile, 2000); } private void populateFile(File file, long numberBytes) throws Exception { int byteLength = 512; Random random = new Random(); long bytesRemaining = numberBytes; try(OutputStream fos = new FileOutputStream(file)) { for (; bytesRemaining > byteLength; bytesRemaining = bytesRemaining - byteLength) { byte bytes[] = new byte[byteLength]; random.nextBytes(bytes); fos.write(bytes); } if (bytesRemaining > 0) { byte bytes[] = new byte[(int) bytesRemaining]; random.nextBytes(bytes); fos.write(bytes); } } } }