"""
@package tests.test_files
unit tests for pmsco.files

the purpose of these tests is to help debugging the code.

to run the tests, change to the directory which contains the tests directory, and execute =nosetests=.

@pre nose must be installed (python-nose package on Debian).

@author Matthias Muntwiler, matthias.muntwiler@psi.ch

@copyright (c) 2015-18 by Paul Scherrer Institut @n
Licensed under the Apache License, Version 2.0 (the "License"); @n
  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
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import unittest
import mock
import os
import pmsco.files as files


class TestFileTracker(unittest.TestCase):
    """
    unit tests of pmsco.files.FileTracker

    for a description of the mock and patch mechanism, cf.
    - http://blog.thedigitalcatonline.com/blog/2016/03/06/python-mocks-a-gentle-introduction-part-1
    - http://blog.thedigitalcatonline.com/blog/2016/09/27/python-mocks-a-gentle-introduction-part-2
    """
    def setUp(self):
        # before each test method
        self.files = files.FileTracker()
        self.files.keep_rfac = 1

        self.files.add_file("model 1 file 1 cluster K", 1, 'cluster')
        self.files.add_file("model 1 file 2 output D", 1, 'output')
        self.files.add_file("model 2 file 1 cluster K", 2, 'cluster')
        self.files.add_file("model 2 file 2 output D", 2, 'output')
        self.files.add_file("model 3 file 1 cluster K", 3, 'cluster')
        self.files.add_file("model 3 file 2 output D", 3, 'output')
        self.files.add_file("model 4 file 1 cluster K", 4, 'cluster')
        self.files.add_file("model 4 file 2 output D", 4, 'output')
        self.files.add_file("model 5 file 1 cluster K", 5, 'cluster')
        self.files.add_file("model 5 file 2 output D", 5, 'output')

        self.files.update_model_rfac(2, 0.0)
        self.files.update_model_rfac(3, 0.1)
        self.files.update_model_rfac(4, 0.3)
        self.files.update_model_rfac(1, 0.5)
        self.files.update_model_rfac(5, 0.6)

        self.files.set_model_complete(1, True)
        self.files.set_model_complete(2, True)
        self.files.set_model_complete(3, False)
        self.files.set_model_complete(5, True)

    def tearDown(self):
        # after each test method
        pass

    @classmethod
    def setup_class(cls):
        # before any methods in this class
        pass

    @classmethod
    def teardown_class(cls):
        # teardown_class() after any methods in this class
        pass

    def test_add_file(self):
        self.assertEqual(self.files.get_file_count(), 10, "file count after setup")
        # add a new file of an existing model
        self.files.add_file("model 2 file 1 input K", 2, 'input')
        self.assertEqual(self.files.get_file_count(), 11, "file count after add")
        # change the category of an existing file
        self.files.add_file("model 2 file 2 output D", 2, 'report')
        self.assertEqual(self.files.get_file_count(), 11, "file count after change")

    def test_rename_file(self):
        self.files.rename_file("model 2 file 2 output D", "renamed file")
        self.assertEqual(self.files.get_file_count(), 10, "file count after rename")
        self.files.rename_file("inexistant file", "renamed file")
        self.assertEqual(self.files.get_file_count(), 10, "file count after rename")

    @mock.patch('os.remove')
    def test_remove_file(self, os_remove_mock):
        self.files.remove_file("model 3 file 1 cluster K")
        self.assertEqual(self.files.get_file_count(), 9, "file count after remove")
        self.assertFalse(os_remove_mock.called, "file deleted")

    def test_update_model_rfac(self):
        pass

    def test_set_model_complete(self):
        self.assertEqual(self.files.get_complete_models_count(), 3, "complete model count after setup")
        self.files.set_model_complete(3, True)
        self.assertEqual(self.files.get_complete_models_count(), 4, "complete model count after add")
        self.files.set_model_complete(2, False)
        self.assertEqual(self.files.get_complete_models_count(), 3, "complete model count after remove")
        self.files.set_model_complete(5, True)
        self.assertEqual(self.files.get_complete_models_count(), 3, "complete model count after no change")

    @mock.patch('os.remove')
    def test_delete_files(self, os_remove_mock):
        self.files.keep_rfac = 10
        self.files.delete_files()
        os_remove_mock.assert_any_call(os.path.abspath("model 1 file 2 output D"))
        os_remove_mock.assert_any_call(os.path.abspath("model 2 file 2 output D"))
        os_remove_mock.assert_any_call(os.path.abspath("model 5 file 2 output D"))
        self.assertEqual(self.files.get_file_count(), 5+2)

    @mock.patch('os.remove')
    def test_delete_file(self, os_remove_mock):
        self.files.delete_file("model 3 file 1 cluster K")
        self.assertEqual(self.files.get_file_count(), 9, "file count after remove")
        self.assertTrue(os_remove_mock.called, "file not deleted")

    @mock.patch('os.remove')
    def test_delete_bad_rfac(self, os_remove_mock):
        self.files.delete_bad_rfac(keep=2, force_delete=True)
        self.assertEqual(self.files.get_file_count(), 6)
        os_remove_mock.assert_any_call(os.path.abspath("model 1 file 1 cluster K"))
        os_remove_mock.assert_any_call(os.path.abspath("model 5 file 1 cluster K"))

    @mock.patch('os.remove')
    def test_delete_category(self, os_remove_mock):
        self.files.delete_category('cluster')
        self.assertEqual(self.files.get_file_count(), 7)
        os_remove_mock.assert_any_call(os.path.abspath("model 1 file 1 cluster K"))
        os_remove_mock.assert_any_call(os.path.abspath("model 2 file 1 cluster K"))
        os_remove_mock.assert_any_call(os.path.abspath("model 5 file 1 cluster K"))

    @mock.patch('os.remove')
    def test_delete_models(self, os_remove_mock):
        n = self.files.delete_models()
        self.assertEqual(n, 0)
        n = self.files.delete_models(keep={2, 5})
        self.assertEqual(n, 1)
        self.assertEqual(self.files.get_file_count(), 8, "file count after remove")
        self.assertTrue(os_remove_mock.called, "file not deleted")
        n = self.files.delete_models(delete={2, 3})
        self.assertEqual(n, 1)
        self.assertEqual(self.files.get_file_count(), 6, "file count after remove")
