#!/usr/bin/env python3

from __future__ import division
from __future__ import print_function

import unittest

import os
import shutil
import tempfile

from changedirectory import cd


class ChangeDirectoryTests(unittest.TestCase):
    """
    Tests for ChangeDirectory.
    """

    def setUp(self):
        """ Setup method called before each test"""
        self.directories = list()
        os.chdir(self.temp_dir())

    def tearDown(self):
        """ TearDown called after each test"""
        for directory in self.directories:
            shutil.rmtree(directory, ignore_errors=True)

    def temp_dir(self):
        """ Returns a new temporary directory and add it to
        directoris to clean"""
        d = os.path.realpath(tempfile.mkdtemp())
        self.directories.append(d)
        return d

    def test_base_cwd_as_input(self):
        cwd = os.getcwd()
        with cd(cwd):
            self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))

    def test_base_dir_is_changed(self):
        d = self.temp_dir()
        cwd = os.getcwd()
        with cd(d):
            self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(d))
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))

    def test_base_chdir_still_allowed(self):
        d = self.temp_dir()
        d2 = self.temp_dir()
        cwd = os.getcwd()
        with cd(d):
            self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(d))
            os.chdir(d2)
            self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(d2))
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))

    def test_base_recover_even_with_exceptions(self):
        d = self.temp_dir()
        cwd = os.getcwd()
        with self.assertRaises(TypeError):
            with cd(d):
                raise TypeError
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))
        with self.assertRaises(ValueError):
            with cd(d):
                raise ValueError
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))

    def test_base_reentrant(self):
        d = self.temp_dir()
        d2 = self.temp_dir()
        cwd = os.getcwd()
        with cd(d):
            self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(d))
            with cd(d2):
                self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(d2))
            self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(d))
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))

    def test_base_dir_not_deleted(self):
        d = self.temp_dir()
        with cd(d):
            self.assertTrue(os.path.exists(d), "given directory was deleted!")
        self.assertTrue(os.path.exists(d), "given directory was deleted!")
        with cd(d):
            with open("test_file", "wt") as fd:
                fd.write("Hey you!")
            filename = os.path.abspath("test_file")
        self.assertTrue(os.path.exists(filename), "file in given directory was deleted!")

    def test_base_creation_outside_entering_context(self):
        d = self.temp_dir()
        d2 = self.temp_dir()
        cwd = os.getcwd()
        cd_obj = cd(d)
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))
        os.chdir(d2)
        with cd_obj:
            self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(d))
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(d2))

    # Comment the following line to force the check the bonus code
    @unittest.expectedFailure
    def test_bonus1(self):
        cwd = os.getcwd()
        with cd():
            self.assertNotEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))
            self.assertEqual(os.listdir(), [])
            with open('file.txt', "wt") as fd:
                fd.write("ABC")
            file_path = os.path.abspath('file.txt')
            self.assertEqual(os.listdir(), ['file.txt',])
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))
        self.assertFalse(os.path.exists(file_path), "Temporary directory not clean")

    # Comment the following line to force the check the bonus code
    @unittest.expectedFailure
    def test_bonus2(self):
        d = self.temp_dir()
        cwd = os.getcwd()
        with cd(d) as cd_object:
            self.assertTrue(cd_object is not None)
            self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(d))
            self.assertTrue(hasattr(cd_object, "old_cwd"), "missing old_cwd attribute")
            self.assertEqual(os.path.abspath(cd_object.old_cwd), os.path.abspath(cwd))
            self.assertTrue(hasattr(cd_object, "new_cwd"), "missing new_cwd attribute")
            self.assertEqual(os.path.abspath(cd_object.new_cwd), os.path.abspath(d))
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))

    # Comment the following line to force the check the bonus code
    @unittest.expectedFailure
    def test_bonus3(self):
        d = self.temp_dir()
        cwd = os.getcwd()
        cd_object = cd(d)
        cd_object.enter()
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(d))
        cd_object.leave()
        self.assertEqual(os.path.abspath(os.getcwd()), os.path.abspath(cwd))


if __name__ == "__main__":
    unittest.main(verbosity=2)
