from __future__ import absolute_import
import json
import mock
import os
import six
import sys
import unittest

from mock import call
from six.moves import range

from koji_cli.lib import watch_tasks
from .fakeclient import FakeClientSession, RecordingClientSession


class TestWatchTasks(unittest.TestCase):

    def setUp(self):
        self.options = mock.MagicMock()
        self.session = FakeClientSession('SERVER', {})
        self.recording = False
        self.record_file = None
        self.args = mock.MagicMock()

    def tearDown(self):
        mock.patch.stopall()
        if self.recording:
            # save recorded calls
            if self.record_file:
                with open(self.record_file, 'w') as fp:
                    json.dump(self.session.get_calls(), fp, indent=4)
            else:
                json.dump(self.session.get_calls(), sys.stderr, indent=4)
            self.recording = False
            self.record_file = None

    def setup_record(self, filename=None):
        self.session = RecordingClientSession('http://localhost/kojihub', {})
        self.recording = True
        self.record_file = filename

    # Show long diffs in error output...
    maxDiff = None

    @mock.patch('sys.stdout', new_callable=six.StringIO)
    def test_watch_tasks_no_tasklist(self, stdout):
        returned = watch_tasks(self.session, [], poll_interval=0, topurl=self.options.topurl)
        actual = stdout.getvalue()
        expected = ""
        self.assertIsNone(returned)
        self.assertEqual(actual, expected)

    @mock.patch('sys.stdout', new_callable=six.StringIO)
    def test_watch_tasks(self, stdout):
        # self.setup_record('foo.json')
        cfile = os.path.dirname(__file__) + '/data/calls/watchtasks1.json'
        with open(cfile) as fp:
            cdata = json.load(fp)
        self.session.load_calls(cdata)
        rv = watch_tasks(self.session, [1188], quiet=False, poll_interval=0,
                         topurl=self.options.topurl)
        self.assertEqual(rv, 0)
        expected = (
'''Watching tasks (this may be safely interrupted)...
1188 build (f24, /users/mikem/fake.git:adaf62586b4b4a23b24394da5586abd7cd9f679e): closed
  1189 buildSRPMFromSCM (/users/mikem/fake.git:adaf62586b4b4a23b24394da5586abd7cd9f679e): closed
  1190 buildArch (fake-1.1-21.src.rpm, noarch): closed

1188 build (f24, /users/mikem/fake.git:adaf62586b4b4a23b24394da5586abd7cd9f679e) completed successfully
''')
        self.assertMultiLineEqual(stdout.getvalue(), expected)

    @mock.patch('time.sleep')
    @mock.patch('sys.stdout', new_callable=six.StringIO)
    def test_watch_tasks_fail(self, stdout, sleep):
        # self.setup_record('foo.json')
        cfile = os.path.dirname(__file__) + '/data/calls/watchtasks2.json'
        with open(cfile) as fp:
            cdata = json.load(fp)
        self.session.load_calls(cdata)
        rv = watch_tasks(self.session, [1208], quiet=False, poll_interval=5, topurl=None)
        self.assertEqual(rv, 1)
        expected = ('''Watching tasks (this may be safely interrupted)...
1208 build (f24, /users/mikem/fake.git:master): free
1208 build (f24, /users/mikem/fake.git:master): free -> open (builder-01)
  1209 buildSRPMFromSCM (/users/mikem/fake.git:master): free
  1209 buildSRPMFromSCM (/users/mikem/fake.git:master): free -> open (builder-01)
1208 build (f24, /users/mikem/fake.git:master): open (builder-01) -> FAILED: GenericError: Build already exists (id=425, state=COMPLETE): {'name': 'fake', 'task_id': 1208, 'extra': None, 'pkg_id': 298, 'epoch': 7, 'completion_time': None, 'state': 0, 'version': '1.1', 'source': None, 'volume_id': 0, 'owner': 1, 'release': '22', 'start_time': 'NOW'}
  0 free  1 open  0 done  1 failed
  1209 buildSRPMFromSCM (/users/mikem/fake.git:master): open (builder-01) -> closed
  0 free  0 open  1 done  1 failed

1208 build (f24, /users/mikem/fake.git:master) failed
''')
        self.assertMultiLineEqual(stdout.getvalue(), expected)

    @mock.patch('time.sleep')
    @mock.patch('sys.stdout', new_callable=six.StringIO)
    def test_watch_tasks_with_keyboardinterrupt(self, stdout, sleep):
        """Raise KeyboardInterrupt inner watch_tasks.
        Raising it by SIGNAL might be better"""
        cfile = os.path.dirname(__file__) + '/data/calls/watchtasks2.json'
        with open(cfile) as fp:
            cdata = json.load(fp)
        self.session.load_calls(cdata)
        sleep.side_effect = [None] * 10  + [KeyboardInterrupt]
        with self.assertRaises(KeyboardInterrupt):
            # watch_tasks catches and re-raises it to display a message
            watch_tasks(self.session, [1208], quiet=False, poll_interval=5,
                        topurl=self.options.topurl)
        expected = ('''Watching tasks (this may be safely interrupted)...
1208 build (f24, /users/mikem/fake.git:master): free
1208 build (f24, /users/mikem/fake.git:master): free -> open (builder-01)
  1209 buildSRPMFromSCM (/users/mikem/fake.git:master): free
  1209 buildSRPMFromSCM (/users/mikem/fake.git:master): free -> open (builder-01)
Tasks still running. You can continue to watch with the '%s watch-task' command.
Running Tasks:
1208 build (f24, /users/mikem/fake.git:master): open (builder-01)
  1209 buildSRPMFromSCM (/users/mikem/fake.git:master): open (builder-01)
''' % (os.path.basename(sys.argv[0]) or 'koji'))
        self.assertMultiLineEqual(stdout.getvalue(), expected)

    @mock.patch('time.sleep')
    @mock.patch('sys.stdout', new_callable=six.StringIO)
    def test_watch_tasks_with_keyboardinterrupt_handler(self, stdout, sleep):
        """Raise KeyboardInterrupt inner watch_tasks with a ki_handler"""
        cfile = os.path.dirname(__file__) + '/data/calls/watchtasks2.json'
        with open(cfile) as fp:
            cdata = json.load(fp)
        self.session.load_calls(cdata)
        sleep.side_effect = [None] * 10 + [KeyboardInterrupt]

        def customized_handler(progname, tasks, quiet):
            print('some output')

        with self.assertRaises(KeyboardInterrupt):
            # watch_tasks catches and re-raises it to display a message
            watch_tasks(self.session, [1208], quiet=False, poll_interval=5,
                        ki_handler=customized_handler, topurl=self.options.topurl)
        expected = ('''Watching tasks (this may be safely interrupted)...
1208 build (f24, /users/mikem/fake.git:master): free
1208 build (f24, /users/mikem/fake.git:master): free -> open (builder-01)
  1209 buildSRPMFromSCM (/users/mikem/fake.git:master): free
  1209 buildSRPMFromSCM (/users/mikem/fake.git:master): free -> open (builder-01)
some output
''')
        self.assertMultiLineEqual(stdout.getvalue(), expected)


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