#!/usr/bin/env python3

# THIS FILE IS PART OF THE CYLC SUITE ENGINE.
# Copyright (C) 2008-2019 NIWA & British Crown (Met Office) & Contributors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import unittest

from parsec.upgrade import *


class TestUpgrade(unittest.TestCase):

    def setUp(self):
        self.cfg = OrderedDict()
        self.cfg['section'] = OrderedDict()
        self.cfg['section']['a'] = '1'
        self.cfg['section']['b'] = '2'
        self.u = upgrader(self.cfg, "1.0 to 2.0")

    def test_converter(self):
        def callback(i):
            return 1 + i

        c = converter(callback=callback, descr="My callback")
        self.assertEqual("My callback", c.describe())
        self.assertEqual(2, c.convert(1))

    def test_constructor(self):
        self.assertEqual(self.cfg, self.u.cfg)
        self.assertEqual("1.0 to 2.0", self.u.descr)

    def test_deprecate(self):
        # b is being deprecated; use c instead
        self.u.deprecate('entry', ['section', 'b'],
                         ['section', 'c'])
        self.assertTrue('entry' in self.u.upgrades)

        def callback(i):
            return 10 * i

        c = converter(callback=callback, descr="My callback")
        self.u.deprecate(vn='entry',
                         oldkeys=[1, 2, 3],
                         newkeys=[10, 20, 30],
                         cvtr=c)

        # assert the key exists before deprecation
        self.assertTrue('b' in self.cfg['section'])
        self.assertFalse('c' in self.cfg['section'])
        self.u.upgrade()
        # assert the key exists before deprecation
        self.assertFalse('b' in self.cfg['section'])
        self.assertTrue('c' in self.cfg['section'])

    def test_obsolete(self):
        # b is obsolete, so the value is omitted
        self.u.obsolete(vn='entry', oldkeys=['section', 'b'], silent=True)
        self.assertTrue('entry' in self.u.upgrades)
        self.assertEqual(True, self.u.upgrades['entry'][0]['silent'])

        self.assertTrue('b' in self.cfg['section'])
        self.u.upgrade()
        self.assertFalse('b' in self.cfg['section'])

        self.u.obsolete(
            vn='entry',
            oldkeys=['section', 'b'],
            newkeys=['section', 'c'],
            silent=False)
        self.assertEqual(True, self.u.upgrades['entry'][0]['silent'])
        self.assertEqual(False, self.u.upgrades['entry'][1]['silent'])

        self.u.obsolete(vn='whocalled?', oldkeys=['section', 'b'], silent=True)

    def test_get_item(self):
        for keys, results in [
            (['section', 'a'], '1'),
            (['section', 'b'], '2'),
            (['section'], {'a': '1', 'b': '2'})
        ]:
            item = self.u.get_item(keys)
            self.assertEqual(results, item)

    def test_put_item(self):
        for keys, value in [
            (['section', 'a'], '100'),
            (['section', 'b'], '200'),
            (['special', 'c'], '3'),
        ]:
            self.u.put_item(keys, value)
            self.assertEqual(self.u.get_item(keys), value)

    def test_expand_not_many(self):
        upg = {
            'new': None,
            'cvt': None,
            'silent': True,
            'old': [
            ]
        }
        self.assertEqual([upg], self.u.expand(upg))

    def test_expand_too_many(self):
        upg = {
            'new': None,
            'cvt': None,
            'silent': True,
            'old': [
                'section', '__MANY__', '__MANY__'
            ]
        }
        with self.assertRaises(UpgradeError) as cm:
            self.u.expand(upg)
        self.assertTrue('Multiple simultaneous __MANY__ not supported' in
                        str(cm.exception))

    def test_expand_deprecate_many_mismatch(self):
        upg = {
            'new': [
                'section', '__MANY__'
            ],
            'cvt': None,
            'silent': True,
            'old': [
                'section', '__MANY__', 'b'
            ]
        }
        with self.assertRaises(UpgradeError) as cm:
            self.u.expand(upg)
        self.assertEqual('__MANY__ mismatch', str(cm.exception))

    def test_expand_deprecate(self):

        def callback(i):
            return i

        c = converter(callback=callback, descr="My callback")
        upg = {
            'new': [
                'section', '__MANY__', 'e'
            ],
            'cvt': c,
            'silent': True,
            'old': [
                'section', '__MANY__', 'c'
            ]
        }
        self.u.upgrade()
        expanded = self.u.expand(upg)
        self.assertEqual(2, len(expanded))
        self.assertEqual(['section', 'a', 'e'], expanded[0]['new'])

    def test_expand_obsolete(self):
        upg = {
            'new': None,
            'cvt': None,
            'silent': True,
            'old': [
                'section', '__MANY__', 'a'
            ]
        }
        self.cfg['__MANY__'] = OrderedDict()
        self.cfg['__MANY__']['name'] = 'Arthur'
        self.u.obsolete('entry', ['section', '__MANY__'])
        self.u.upgrade()
        expanded = self.u.expand(upg)
        self.assertEqual(1, len(expanded))
        self.assertTrue(expanded[0]['new'] is None)


if __name__ == '__main__':
    unittest.main()
