package co.codewizards.cloudstore.local; import static co.codewizards.cloudstore.core.io.StreamUtil.*; import static co.codewizards.cloudstore.core.oio.OioFileFactory.*; import static org.assertj.core.api.Assertions.*; import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Random; import java.util.Set; import org.junit.After; import org.junit.Assert; import org.junit.Before; import co.codewizards.cloudstore.core.DevMode; import co.codewizards.cloudstore.core.Uid; import co.codewizards.cloudstore.core.config.ConfigDir; import co.codewizards.cloudstore.core.oio.File; import co.codewizards.cloudstore.core.oio.IoFile; import co.codewizards.cloudstore.core.oio.nio.NioFileFactory; import co.codewizards.cloudstore.core.repo.local.LocalRepoManager; import co.codewizards.cloudstore.core.repo.local.LocalRepoManagerFactory; import co.codewizards.cloudstore.core.repo.local.LocalRepoTransaction; import co.codewizards.cloudstore.core.util.IOUtil; import co.codewizards.cloudstore.local.persistence.Directory; import co.codewizards.cloudstore.local.persistence.NormalFile; import co.codewizards.cloudstore.local.persistence.RepoFile; import co.codewizards.cloudstore.local.persistence.RepoFileDao; import co.codewizards.cloudstore.local.persistence.Symlink; public abstract class AbstractTest { protected static String jvmInstanceDir; static { DevMode.enableDevMode(); final Uid jvmInstanceId = new Uid(); // for parallel test execution ;-) jvmInstanceDir = "target/jvm/" + jvmInstanceId; final String configDirString = jvmInstanceDir + "/.cloudstore"; System.setProperty(ConfigDir.SYSTEM_PROPERTY_CONFIG_DIR, configDirString); System.setProperty(LocalRepoManager.SYSTEM_PROPERTY_KEY_SIZE, "1024"); createFile(configDirString).mkdirs(); } protected static final Random random = new Random(); protected static LocalRepoManagerFactory localRepoManagerFactory = LocalRepoManagerFactory.Helper.getInstance(); private final Map<File, Set<File>> localRoot2FilesInRepo = new HashMap<File, Set<File>>(); protected File newTestRepositoryLocalRoot(final String suffix) throws IOException { assertThat(suffix).isNotNull(); final long timestamp = System.currentTimeMillis(); final int randomNumber = random.nextInt(BigInteger.valueOf(36).pow(5).intValue()); final String repoName = Long.toString(timestamp, 36) + '-' + Integer.toString(randomNumber, 36) + (suffix.isEmpty() ? "" : "-") + suffix; final File localRoot = createFile(getTestRepositoryBaseDir(), repoName); addToFilesInRepo(localRoot, localRoot); return localRoot; } protected File getTestRepositoryBaseDir() { final File dir = createFile(createFile("target"), "repo"); dir.mkdirs(); return dir; } @Before public void before() { localRoot2FilesInRepo.clear(); } @After public void after() { } protected File createDirectory(final File parent, final String name) throws IOException { final File dir = createFile(parent, name); return createDirectory(dir); } protected File createDirectory(final File dir) throws IOException { assertThat(dir.exists()).isFalse(); dir.mkdir(); assertThat(dir.isDirectory()).isTrue(); addToFilesInRepo(dir); return dir; } protected void addToFilesInRepo(File file) throws IOException { file = file.getAbsoluteFile(); final File localRoot = getLocalRootOrFail(file); addToFilesInRepo(localRoot, file); } protected void addToFilesInRepo(File localRoot, File file) throws IOException { localRoot = localRoot.getAbsoluteFile(); file = file.getAbsoluteFile(); Set<File> filesInRepo = localRoot2FilesInRepo.get(localRoot); if (filesInRepo == null) { filesInRepo = new HashSet<File>(); localRoot2FilesInRepo.put(localRoot, filesInRepo); } filesInRepo.add(file); } protected File createFileWithRandomContent(final File parent, final String name) throws IOException { return createFileWithRandomContent(parent, name, 0); } protected File createFileWithRandomContent(final File parent, final String name, final long minLength) throws IOException { final File file = createFile(parent, name); return createFileWithRandomContent(file, minLength); } protected File createFileWithRandomContent(final File file) throws IOException { return createFileWithRandomContent(file, 0); } protected File createFileWithRandomContent(final File file, final long minLength) throws IOException { assertThat(file.exists()).isFalse(); // prevent accidentally overwriting important data ;-) final OutputStream out = castStream(file.createOutputStream()); final byte[] buf = new byte[1 + random.nextInt(10241)]; final int loops = 1 + random.nextInt(100); for (int i = 0; i < loops; ++i) { random.nextBytes(buf); out.write(buf); } out.flush(); while (file.length() < minLength) { random.nextBytes(buf); out.write(buf); out.flush(); } out.close(); assertThat(file.isFile()).isTrue(); addToFilesInRepo(file); return file; } /** TODO Remove duplicate code: AbstractIT.java and AbstractTest.java */ protected File createRelativeSymlink(File symlink, final File target) throws IOException { assertThat(symlink.exists()).isFalse(); final File symlinkParent = symlink.getParentFile(); final String relativeTargetString = symlinkParent.relativize(target); if (symlink instanceof IoFile) { /* Only do this in a test! If symlink is instance of IoFile, * createSymbolicLink would throw an Exception (by intention)! * But in productive code you would never call this, besides you * know the environment and the implementation supports this. */ symlink = new NioFileFactory().createFile(symlink.getIoFile()); } symlink.createSymbolicLink(relativeTargetString); assertThat(symlink.getAbsoluteFile()).isEqualTo(symlink.getAbsoluteFile()); assertThat(symlink.existsNoFollow()).isTrue(); assertThat(symlink.isSymbolicLink()).isTrue(); addToFilesInRepo(symlink); return symlink; } protected void deleteFile(File file) throws IOException { file = file.getAbsoluteFile(); assertThat(file.exists()).isTrue(); file.delete(); assertThat(file.exists()).isFalse(); final File localRoot = getLocalRootOrFail(file); final Set<File> filesInRepo = localRoot2FilesInRepo.get(localRoot); if (filesInRepo == null) throw new IllegalStateException("No filesInRepo for localRoot: " + localRoot); if (!filesInRepo.remove(file)) throw new IllegalStateException("File did not exist in filesInRepo: " + file); } private File getLocalRootOrFail(final File file) throws IOException { final String filePath = file.getCanonicalPath(); final Set<File> localRoots = localRepoManagerFactory.getLocalRoots(); for (final File localRoot : localRoots) { final String localRootPath = localRoot.getPath(); if (filePath.startsWith(localRootPath)) { return localRoot; } } throw new IllegalArgumentException("file is not contained in any open repository: " + filePath); } protected void assertThatFilesInRepoAreCorrect(File localRoot) { final LocalRepoManager localRepoManager = localRepoManagerFactory.createLocalRepoManagerForExistingRepository(localRoot); localRoot = localRepoManager.getLocalRoot(); // get canonical File final LocalRepoTransaction transaction = localRepoManager.beginReadTransaction(); try { final RepoFileDao repoFileDao = transaction.getDao(RepoFileDao.class); Set<File> filesInRepo = localRoot2FilesInRepo.get(localRoot); assertThat(filesInRepo).isNotNull(); for (final File file : filesInRepo) { final RepoFile repoFile = repoFileDao.getRepoFile(localRoot, file); if (repoFile == null) { Assert.fail("Corresponding RepoFile missing in repository for file: " + file); } if (file.isSymbolicLink()) assertThat(repoFile).isInstanceOf(Symlink.class); else if (file.isFile()) assertThat(repoFile).isInstanceOf(NormalFile.class); else if (file.isDirectory()) assertThat(repoFile).isInstanceOf(Directory.class); } filesInRepo = new HashSet<File>(filesInRepo); final Collection<RepoFile> repoFiles = repoFileDao.getObjects(); final Map<File, RepoFile> file2RepoFile = new HashMap<File, RepoFile>(); for (final RepoFile repoFile : repoFiles) { final File file = repoFile.getFile(localRoot); final RepoFile duplicateRepoFile = file2RepoFile.put(file, repoFile); if (duplicateRepoFile != null) Assert.fail("There are 2 RepoFile instances for the same file! " + repoFile + " " + duplicateRepoFile + " " + file); if (!filesInRepo.remove(file)) Assert.fail("Corresponding file in file-system missing for RepoFile: " + repoFile + " " + file); } } finally { transaction.rollbackIfActive(); localRepoManager.close(); } } protected void assertDirectoriesAreEqualRecursively(final File dir1, final File dir2) throws IOException { assertThat(dir1.isDirectory()).isTrue(); assertThat(dir2.isDirectory()).isTrue(); final boolean dir1IsSymbolicLink = dir1.isSymbolicLink(); final boolean dir2IsSymbolicLink = dir2.isSymbolicLink(); assertThat(dir1IsSymbolicLink).isEqualTo(dir2IsSymbolicLink); if (dir1IsSymbolicLink) { final String target1 = dir1.readSymbolicLinkToPathString(); final String target2 = dir2.readSymbolicLinkToPathString(); assertThat(target1).isEqualTo(target2); return; } final String[] children1 = dir1.list(new FilenameFilterSkipMetaDir()); assertThat(children1).isNotNull(); final String[] children2 = dir2.list(new FilenameFilterSkipMetaDir()); assertThat(children2).isNotNull(); Arrays.sort(children1); Arrays.sort(children2); // assertThat(children1).containsOnly(children2); assertThat(children1).isEqualTo(children2); for (final String childName : children1) { final File child1 = createFile(dir1, childName); final File child2 = createFile(dir2, childName); final boolean child1IsSymbolicLink = child1.isSymbolicLink(); final boolean child2IsSymbolicLink = child2.isSymbolicLink(); assertThat(child1IsSymbolicLink).isEqualTo(child2IsSymbolicLink); if (child1.getLastModifiedNoFollow() != child2.getLastModifiedNoFollow()) fail("Different 'lastModified' timestamps: " + child1 + " vs. " + child2); if (child1IsSymbolicLink) { final String child1Symlink = child1.readSymbolicLinkToPathString(); final String child2Symlink = child2.readSymbolicLinkToPathString(); assertThat(child1Symlink).isEqualTo(child2Symlink); } else if (child1.isFile()) { assertThat(child2.isFile()); assertThat(IOUtil.compareFiles(child1, child2)).isTrue(); } else if (child1.isDirectory()) assertDirectoriesAreEqualRecursively(child1, child2); } } }