aboutsummaryrefslogtreecommitdiff
path: root/app/tests/test_backends_common.py
blob: b8a8e90855db8b2bedfd60e14e4203e8f55fb4fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""Tests for backends.common subprocess/git helpers.

The streaming mode of ``run_console_subprocess`` (used by the in-TUI task
view) is exercised with a real child process: output lines are captured and
forwarded, cancellation kills the child and returns 130, and an on_cancel
hook runs first.
"""

import sys
import threading
import unittest
from unittest import mock

from backends import common


class RunConsoleSubprocessStreamingTests(unittest.TestCase):
    def test_streaming_emits_merged_lines(self):
        lines = []
        rc = common.run_console_subprocess(
            [sys.executable, "-c",
             "import sys; sys.stdout.write('hello\\nworld\\n'); "
             "sys.stderr.write('oops\\n')"],
            emit=lines.append)
        self.assertEqual(rc, 0)
        # stdout/stderr are merged, in arrival order.
        self.assertEqual(sorted(lines), ["hello", "oops", "world"])

    def test_streaming_returns_the_exit_code(self):
        rc = common.run_console_subprocess(
            [sys.executable, "-c", "import sys; sys.exit(3)"],
            emit=lambda line: None)
        self.assertEqual(rc, 3)

    def test_cancel_kills_the_process_and_returns_130(self):
        cancel = threading.Event()
        cancel.set()
        rc = common.run_console_subprocess(
            [sys.executable, "-c", "import time; time.sleep(60)"],
            emit=lambda line: None, cancel=cancel)
        self.assertEqual(rc, 130)

    def test_on_cancel_hook_runs_before_kill(self):
        cancel = threading.Event()
        cancel.set()
        touched = []
        common.run_console_subprocess(
            [sys.executable, "-c", "import time; time.sleep(60)"],
            emit=lambda line: None, cancel=cancel,
            on_cancel=lambda: touched.append(True))
        self.assertEqual(touched, [True])


class GitCloneTests(unittest.TestCase):
    def test_git_clone_console_passes_through(self):
        with mock.patch.object(common, "run_console_subprocess",
                                        return_value=0) as run:
            self.assertEqual(common.git_clone("url", common.Path("/t")), 0)
        # Console mode: no --progress flag, plain git clone.
        self.assertEqual(run.call_args[0][0],
                         ["git", "clone", "url", "/t"])

    def test_git_clone_streaming_adds_progress(self):
        emit = lambda line: None
        with mock.patch.object(common, "run_console_subprocess",
                                        return_value=0) as run:
            self.assertEqual(common.git_clone("url", common.Path("/t"),
                                              emit=emit), 0)
        argv = run.call_args[0][0]
        self.assertEqual(argv[:3], ["git", "clone", "--progress"])
        self.assertIn("url", argv)
        self.assertEqual(run.call_args[1]["emit"], emit)


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