import httmock
import mock
import os
import romanesco
import shutil
import subprocess
import unittest

_tmp = None


def setUpModule():
    global _tmp
    _tmp = os.path.join(
        os.path.dirname(os.path.abspath(__file__)), 'tmp', 'docker')
    romanesco.config.set('romanesco', 'tmp_root', _tmp)


def tearDownModule():
    if os.path.isdir(_tmp):
        shutil.rmtree(_tmp)


class TestDockerMode(unittest.TestCase):
    @mock.patch('subprocess.Popen')
    def testDockerMode(self, mockPopen):
        processMock = mock.Mock()
        processMock.configure_mock(**{
            'communicate.return_value': ('ouput', 'error'),
            'returncode': 0
        })
        mockPopen.return_value = processMock

        task = {
            'mode': 'docker',
            'docker_image': 'test/test:latest',
            'container_args': ['-f', '$input{foo}'],
            'inputs': [{
                'id': 'foo',
                'name': 'A variable',
                'format': 'string',
                'type': 'string',
                'target': 'filepath'
            }]
        }

        inputs = {
            'foo': {
                'mode': 'http',
                'url': 'https://foo.com/file.txt'
            }
        }

        @httmock.all_requests
        def fetchMock(url, request):
            if url.netloc == 'foo.com' and url.scheme == 'https':
                return 'dummy file contents'
            else:
                raise Exception('Unexpected url ' + repr(url))

        with httmock.HTTMock(fetchMock):
            # Use user-specified filename
            out = romanesco.run(
                task, inputs=inputs, cleanup=False, validate=False,
                auto_convert=False)

            self.assertEqual(out, {
                '_stderr': {
                    'script_data': 'error',
                    'format': 'string'
                },
                '_stdout': {
                    'script_data': 'ouput',
                    'format': 'string'
                }
            })

            self.assertEqual(mockPopen.call_count, 2)
            cmd1, cmd2 = [x[1]['args'] for x in mockPopen.call_args_list]

            self.assertEqual(cmd1, ('docker', 'pull', 'test/test:latest'))
            self.assertEqual(cmd2[:3], ['docker', 'run', '-v'])
            self.assertRegexpMatches(cmd2[3], _tmp + '/.*:/data')
            self.assertEqual(cmd2[4:],
                             ['test/test:latest', '-f', '/data/file.txt'])

@@ -59,60 +59,633 @@
 rt s
-hutil%0Aimport subprocess%0Aimport unittest%0A%0A_tmp = None
+elect%0Aimport shutil%0Aimport StringIO%0Aimport sys%0Aimport unittest%0A%0A_tmp = None%0AOUT_FD, ERR_FD = 100, 200%0A_out = StringIO.StringIO('output message')%0A_err = StringIO.StringIO('error message')%0A%0A%0A# Monkey patch select.select in the docker task module%0Adef _mockSelect(r, w, x, *args, **kwargs):%0A    return r, w, x%0Aromanesco.tasks.docker.select.select = _mockSelect%0A%0A%0A# Monkey patch os.read to simulate subprocess stdout and stderr%0Adef _mockOsRead(fd, *args, **kwargs):%0A    global _out, _err%0A    if fd == OUT_FD:%0A        return _out.read()%0A    elif fd == ERR_FD:%0A        return _err.read()%0Aromanesco.tasks.docker.os.read = _mockOsRead
 %0A%0A%0Ad
@@ -1187,24 +1187,150 @@
 : ('
-ouput', 'error')
+', ''),%0A            'poll.return_value': 0,%0A            'stdout.fileno.return_value': OUT_FD,%0A            'stderr.fileno.return_value': ERR_FD
 ,%0A  
@@ -1759,16 +1759,160 @@
 lepath'%0A
+            %7D%5D,%0A            'outputs': %5B%7B%0A                'id': '_stderr',%0A                'format': 'string',%0A                'type': 'string'%0A
         
@@ -2419,16 +2419,131 @@
 ilename%0A
+            _old = sys.stdout%0A            mockedStdOut = StringIO.StringIO()%0A            sys.stdout = mockedStdOut%0A
         
@@ -2671,17 +2671,16 @@
 =False)%0A
-%0A
         
@@ -2688,161 +2688,496 @@
    s
-elf.assertEqual(out, %7B%0A                '_stderr': %7B%0A                    'script_data': 'error',%0A                    'format': 'string'%0A                %7D,
+ys.stdout = _old%0A%0A            # We didn't specify _stdout as an output, so it should just get%0A            # printed to sys.stdout (which we mocked)%0A            lines = mockedStdOut.getvalue().splitlines()%0A            self.assertEqual(lines%5B0%5D,%0A                             'Pulling docker image: test/test:latest')%0A            self.assertEqual(lines%5B-1%5D, 'output message')%0A%0A            # We bound _stderr as a task output, so it should be in the output%0A            self.assertEqual(out, %7B
 %0A   
@@ -3194,19 +3194,19 @@
    '_std
-out
+err
 ': %7B%0A   
@@ -3227,23 +3227,16 @@
    '
-script_
 data': '
 oupu
@@ -3235,13 +3235,21 @@
 ': '
-ouput
+error message
 ',%0A 

Test stdout/stderr binding behavior