class TestDataArchiver(unittest.TestCase):
    """Tests for DataArchiver class
    """
    def setUp(self):
        # Make a test data directory structure
        self.example_dir = ExampleDirLanguages()
        self.data_dir = self.example_dir.create_directory()
        self.archive_dir = TestUtils.make_dir()
        self.log_dir = TestUtils.make_dir()
        # Placeholders for additional data structures
        # These should be set in the tests where multiple
        # directories are required
        self.example_dir2 = None
        self.data_dir2 = None

    def tearDown(self):
        # Remove the test data directory (and copy)
        self.example_dir.delete_directory()
        if self.example_dir2 is not None:
            self.example_dir2.delete_directory()
        TestUtils.remove_dir(self.archive_dir)
        TestUtils.remove_dir(self.log_dir)

    def check_directory_contents(self,dir1,dir2):
        # Check that contents of dir1 are also in dir2
        # Checks that file and directory names match
        for dirpath,dirnames,filenames in os.walk(dir1):
            for d in dirnames:
                d2 = os.path.join(dir2,os.path.relpath(os.path.join(dirpath,d),dir1))
                self.assertTrue(os.path.isdir(d2))
            for f in filenames:
                f2 = os.path.join(dir2,os.path.relpath(os.path.join(dirpath,f),dir1))
                self.assertTrue(os.path.isfile(f2))

    def check_directory_group(self,dirn,group):
        # Check that contents of dirn belong to specified group
        gid = bcf_utils.get_gid_from_group(group)
        ##print "Checking: group %s GID %s" % (group,gid)
        for dirpath,dirnames,filenames in os.walk(dirn):
            for d in dirnames:
                d2 = os.path.join(dirpath,d)
                if os.path.islink(d2):
                    # Ignore links
                    continue
                ##print "%s/ %s" % (d2,bcf_utils.PathInfo(d2).gid)
                self.assertEqual(bcf_utils.PathInfo(d2).gid,gid)
            for f in filenames:
                f2 = os.path.join(dirpath,f)
                if os.path.islink(f2):
                    # Ignore links
                    continue
                ##print "%s %s" % (f2,bcf_utils.PathInfo(f2).gid)
                self.assertEqual(bcf_utils.PathInfo(f2).gid,gid)

    def test_archive_single_data_dir(self):
        """DataArchiver copies and verifies single data directory

        """
        # Initial checks
        dest_dir = os.path.join(self.archive_dir,os.path.basename(self.data_dir))
        self.assertFalse(os.path.exists(dest_dir))
        # Run the archiver
        archiver = DataArchiver(self.archive_dir,log_dir=self.log_dir)
        archiver.add_data_dir(self.data_dir)
        status = archiver.archive_dirs()
        # Check the copy
        self.assertTrue(os.path.exists(dest_dir))
        self.check_directory_contents(self.data_dir,dest_dir)
        # Check the status of each operation
        self.assertEqual(archiver.result(self.data_dir).completed,0)
        self.assertEqual(archiver.result(self.data_dir).copy_status,0)
        self.assertEqual(archiver.result(self.data_dir).group_status,None)
        self.assertEqual(archiver.result(self.data_dir).verify_status,0)
        # Check the overall status
        self.assertEqual(status,0)
        self.assertEqual(archiver.status,0)

    def test_archive_multiple_data_dirs(self):
        """DataArchiver copies and verifies multiple data directories

        """
        # Make additional data dir
        self.example_dir2 = ExampleDirLanguages()
        self.data_dir2 = self.example_dir2.create_directory()
        # Initial checks
        dest_dir = os.path.join(self.archive_dir,os.path.basename(self.data_dir))
        self.assertFalse(os.path.exists(dest_dir))
        dest_dir2 = os.path.join(self.archive_dir,os.path.basename(self.data_dir2))
        self.assertFalse(os.path.exists(dest_dir2))
        # Run the archiver
        archiver = DataArchiver(self.archive_dir,log_dir=self.log_dir)
        archiver.add_data_dir(self.data_dir)
        archiver.add_data_dir(self.data_dir2)
        status = archiver.archive_dirs()
        # Check the copies
        self.assertTrue(os.path.exists(dest_dir))
        self.check_directory_contents(self.data_dir,dest_dir)
        self.assertTrue(os.path.exists(dest_dir2))
        self.check_directory_contents(self.data_dir,dest_dir2)
        # Check the status of each operation
        archiver.report()
        self.assertEqual(archiver.result(self.data_dir).completed,0)
        self.assertEqual(archiver.result(self.data_dir).copy_status,0)
        self.assertEqual(archiver.result(self.data_dir).group_status,None)
        self.assertEqual(archiver.result(self.data_dir).verify_status,0)
        self.assertEqual(archiver.result(self.data_dir2).completed,0)
        self.assertEqual(archiver.result(self.data_dir2).copy_status,0)
        self.assertEqual(archiver.result(self.data_dir2).group_status,None)
        self.assertEqual(archiver.result(self.data_dir2).verify_status,0)
        # Check the overall status
        self.assertEqual(status,0)
        self.assertEqual(archiver.status,0)

    def test_archive_data_dir_set_group(self):
        """DataArchiver sets group when copying data directory

        """
        # Get a list of groups
        current_user = pwd.getpwuid(os.getuid()).pw_name
        groups = [g.gr_gid for g in grp.getgrall() if current_user in g.gr_mem]
        if len(groups) < 2:
            raise unittest.SkipTest("user '%s' must be in at least two groups" % current_user)
        # Get a second group
        gid = bcf_utils.PathInfo(self.archive_dir).gid
        new_gid = None
        for group in groups:
            if group != gid:
                new_gid = group
                break
        self.assertNotEqual(new_gid,gid)
        new_group = bcf_utils.get_group_from_gid(new_gid)
        ##print "Group %s GID %s" % (new_group,new_gid)
        # Initial checks
        dest_dir = os.path.join(self.archive_dir,os.path.basename(self.data_dir))
        self.assertFalse(os.path.exists(dest_dir))
        # Run the archiver
        archiver = DataArchiver(self.archive_dir,new_group=new_group,log_dir=self.log_dir)
        archiver.add_data_dir(self.data_dir)
        status = archiver.archive_dirs()
        # Check the copy
        self.assertTrue(os.path.exists(dest_dir))
        self.check_directory_contents(self.data_dir,dest_dir)
        self.check_directory_group(dest_dir,new_group)
        # Check the status of each operation
        self.assertEqual(archiver.result(self.data_dir).completed,0)
        self.assertEqual(archiver.result(self.data_dir).copy_status,0)
        self.assertEqual(archiver.result(self.data_dir).group_status,0)
        self.assertEqual(archiver.result(self.data_dir).verify_status,0)
        # Check the overall status
        self.assertEqual(status,0)
        self.assertEqual(archiver.status,0)

    def test_archive_single_data_dir_with_unreadable_file(self):
        """DataArchiver reports failure when copying directory with unreadable file

        """
        # Remove read permission from a file in source dir
        os.chmod(self.example_dir.path('hello'),0244)
        # Initial checks
        dest_dir = os.path.join(self.archive_dir,os.path.basename(self.data_dir))
        self.assertFalse(os.path.exists(dest_dir))
        # Run the archiver
        archiver = DataArchiver(self.archive_dir,log_dir=self.log_dir)
        archiver.add_data_dir(self.data_dir)
        status = archiver.archive_dirs()
        # Check the copy
        self.assertTrue(os.path.exists(dest_dir))
        ##self.check_directory_contents(self.data_dir,dest_dir)
        # Check the status of each operation
        self.assertEqual(archiver.result(self.data_dir).completed,0)
        self.assertEqual(archiver.result(self.data_dir).copy_status,23)
        self.assertEqual(archiver.result(self.data_dir).group_status,None)
        self.assertEqual(archiver.result(self.data_dir).verify_status,1)
        # Check the overall status
        self.assertEqual(status,1)
        self.assertEqual(archiver.status,1)

    def test_check_single_data_dir(self):
        """DataArchiver verifies single data directory (no copy)

        """
        # Make additional data dir
        dest_dir = os.path.join(self.archive_dir,os.path.basename(self.data_dir))
        self.example_dir2 = ExampleDirLanguages()
        self.data_dir2 = self.example_dir2.create_directory(dest_dir)
        # Initial checks
        self.assertTrue(os.path.exists(dest_dir))
        # Run the archiver checks
        archiver = DataArchiver(self.archive_dir,log_dir=self.log_dir)
        archiver.add_data_dir(self.data_dir)
        status = archiver.check_dirs()
        # Check the status of each operation
        self.assertEqual(archiver.result(self.data_dir).completed,0)
        self.assertEqual(archiver.result(self.data_dir).copy_status,None)
        self.assertEqual(archiver.result(self.data_dir).group_status,None)
        self.assertEqual(archiver.result(self.data_dir).verify_status,0)
        # Check the overall status
        self.assertEqual(status,0)
        self.assertEqual(archiver.status,0)