/* * 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 gobblin.data.management.trash; import java.io.IOException; import java.util.List; import java.util.Properties; import java.util.regex.Pattern; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.PathFilter; import org.apache.hadoop.fs.permission.FsAction; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.security.UserGroupInformation; import org.joda.time.DateTime; import org.joda.time.DateTimeUtils; import org.joda.time.DateTimeZone; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.testng.Assert; import org.testng.annotations.Test; import org.testng.internal.collections.Pair; import com.google.common.collect.Lists; import static org.mockito.Mockito.*; @Test(groups = { "SystemTimeTests"}) public class TrashTest { @Test public void testCreateTrash() throws IOException { new TrashTestBase(new Properties()); } @Test public void testCreationCases() throws IOException { TrashTestBase trash; // If trash ident file doesn't exist, but trash is empty, create trash = new TrashTestBase(new Properties(), true, false, true); verify(trash.fs).createNewFile(new Path(trash.trash.getTrashLocation(), Trash.TRASH_IDENTIFIER_FILE)); // If trash ident file doesn't exist, but trash is not empty, fail try { trash = new TrashTestBase(new Properties(), true, false, false); Assert.fail(); } catch(IOException ioe) {} // If trash doesn't exist, create trash = new TrashTestBase(new Properties(), false, false, true); verify(trash.fs).mkdirs(trash.trash.getTrashLocation(), new FsPermission(FsAction.ALL, FsAction.NONE, FsAction.NONE)); verify(trash.fs).createNewFile(new Path(trash.trash.getTrashLocation(), Trash.TRASH_IDENTIFIER_FILE)); } @Test public void testUserReplacement() throws IOException { Properties properties = new Properties(); properties.setProperty(Trash.TRASH_LOCATION_KEY, "/trash/$USER/dir"); Path expectedTrashPath = new Path("/trash/" + UserGroupInformation.getCurrentUser().getUserName() + "/dir"); TrashTestBase trash = new TrashTestBase(properties); Assert.assertTrue(trash.trash.getTrashLocation().equals(expectedTrashPath)); } @Test public void testMoveToTrash() throws IOException { TrashTestBase trash = new TrashTestBase(new Properties()); Path pathToDelete = new Path("/path/to/delete"); final List<Pair<Path, Path>> movedPaths = Lists.newArrayList(); when(trash.fs.exists(any(Path.class))).thenReturn(false); when(trash.fs.rename(any(Path.class), any(Path.class))).thenAnswer(new Answer<Boolean>() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); movedPaths.add(new Pair<Path, Path>((Path) args[0], (Path) args[1])); return true; } }); Assert.assertTrue(trash.trash.moveToTrash(pathToDelete)); verify(trash.fs, times(1)).mkdirs(any(Path.class)); Assert.assertEquals(movedPaths.size(), 1); Assert.assertTrue(movedPaths.get(0).first().equals(pathToDelete)); Assert.assertTrue(movedPaths.get(0).second().toString().endsWith(pathToDelete.toString())); Assert.assertTrue(movedPaths.get(0).second().getParent().getParent().getParent().equals(trash.trash.getTrashLocation())); } @Test public void testMoveToTrashExistingFile() throws IOException { TrashTestBase trash = new TrashTestBase(new Properties()); String fileName = "delete"; Path pathToDelete = new Path("/path/to", fileName); Pattern expectedNamePattern = Pattern.compile("^" + fileName + "_[0-9]+$"); final List<Pair<Path, Path>> movedPaths = Lists.newArrayList(); when(trash.fs.exists(any(Path.class))).thenReturn(true); when(trash.fs.rename(any(Path.class), any(Path.class))).thenAnswer(new Answer<Boolean>() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); movedPaths.add(new Pair<Path, Path>((Path) args[0], (Path) args[1])); return true; } }); Assert.assertTrue(trash.trash.moveToTrash(pathToDelete)); verify(trash.fs, times(0)).mkdirs(any(Path.class)); Assert.assertEquals(movedPaths.size(), 1); Assert.assertTrue(movedPaths.get(0).first().equals(pathToDelete)); Assert.assertTrue(movedPaths.get(0).second().getParent().toString().endsWith(pathToDelete.getParent().toString())); Assert.assertTrue(expectedNamePattern.matcher(movedPaths.get(0).second().getName()).matches()); Assert.assertTrue(movedPaths.get(0).second().getParent().getParent().getParent().equals(trash.trash.getTrashLocation())); } @Test public void testCreateSnapshot() throws IOException { try { TrashTestBase trash = new TrashTestBase(new Properties()); Path pathInTrash = new Path(trash.trash.getTrashLocation(), "dirInTrash"); DateTimeUtils.setCurrentMillisFixed(new DateTime(2015, 7, 15, 10, 0).getMillis()); final List<Path> createdDirs = Lists.newArrayList(); final List<Pair<Path, Path>> movedPaths = Lists.newArrayList(); when(trash.fs.listStatus(eq(trash.trash.getTrashLocation()), any(PathFilter.class))). thenReturn(Lists.newArrayList(new FileStatus(0, true, 0, 0, 0, pathInTrash)).toArray(new FileStatus[]{})); when(trash.fs.exists(any(Path.class))).thenReturn(false); when(trash.fs.mkdirs(any(Path.class), any(FsPermission.class))).thenAnswer(new Answer<Boolean>() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { createdDirs.add((Path) invocation.getArguments()[0]); return true; } }); when(trash.fs.rename(any(Path.class), any(Path.class))).thenAnswer(new Answer<Boolean>() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); movedPaths.add(new Pair<Path, Path>((Path) args[0], (Path) args[1])); return true; } }); trash.trash.createTrashSnapshot(); Assert.assertEquals(createdDirs.size(), 1); Path createdDir = createdDirs.get(0); Assert.assertTrue(Trash.TRASH_SNAPSHOT_NAME_FORMATTER.parseDateTime(createdDir.getName()).equals(new DateTime().withZone( DateTimeZone.UTC))); Assert.assertEquals(movedPaths.size(), 1); Assert.assertTrue(movedPaths.get(0).first().equals(pathInTrash)); Assert.assertTrue(movedPaths.get(0).second().getName().equals(pathInTrash.getName())); Assert.assertTrue(movedPaths.get(0).second().getParent().equals(createdDir)); } finally { DateTimeUtils.setCurrentMillisSystem(); } } @Test public void testPurgeSnapshots() throws IOException { try { Properties properties = new Properties(); properties.setProperty(Trash.SNAPSHOT_CLEANUP_POLICY_CLASS_KEY, TestCleanupPolicy.class.getCanonicalName()); TrashTestBase trash = new TrashTestBase(properties); DateTimeUtils.setCurrentMillisFixed(new DateTime(2015, 7, 15, 10, 0).withZone(DateTimeZone.UTC).getMillis()); final List<Path> deletedPaths = Lists.newArrayList(); Path snapshot1 = new Path(trash.trash.getTrashLocation(), Trash.TRASH_SNAPSHOT_NAME_FORMATTER.print(new DateTime())); Path snapshot2 = new Path(trash.trash.getTrashLocation(), Trash.TRASH_SNAPSHOT_NAME_FORMATTER.print(new DateTime().minusDays(1))); when(trash.fs.listStatus(any(Path.class), any(PathFilter.class))). thenReturn( Lists.newArrayList( new FileStatus(0, true, 0, 0, 0, snapshot1), new FileStatus(0, true, 0, 0, 0, snapshot2)) .toArray(new FileStatus[]{})); when(trash.fs.delete(any(Path.class), anyBoolean())).thenAnswer(new Answer<Boolean>() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { deletedPaths.add((Path) invocation.getArguments()[0]); return true; } }); trash.trash.purgeTrashSnapshots(); Assert.assertEquals(deletedPaths.size(), 1); Assert.assertTrue(deletedPaths.get(0).equals(snapshot2)); } finally { DateTimeUtils.setCurrentMillisSystem(); } } }