#
# Copyright (c) 2010 Testrepository Contributors
#
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
# license at the users choice. A copy of both licenses are available in the
# project source as Apache-2.0 and BSD. You may not use this file except in
# compliance with one of these two licences.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
# license you chose for the specific language governing permissions and
# limitations under that license.

"""Tests for the testcommand module."""

from io import BytesIO
import os.path
import optparse
import re

import subunit
from testtools.matchers import (
    Equals,
    MatchesAny,
    raises,
)

from testrepository.commands import run
from testrepository.ui.model import UI
from testrepository.repository import memory
from testrepository.testcommand import TestCommand
from testrepository.tests import ResourcedTestCase
from testrepository.tests.stubpackage import TempDirResource
from testrepository.tests.test_repository import run_timed


class FakeTestCommand(TestCommand):
    def __init__(self, ui, repo):
        TestCommand.__init__(self, ui, repo)
        self.oldschool = True


class TestTestCommand(ResourcedTestCase):
    resources = [("tempdir", TempDirResource())]

    def get_test_ui_and_cmd(self, options=(), args=(), repository=None):
        self.dirty()
        ui = UI(options=options, args=args)
        ui.here = self.tempdir
        return ui, self.useFixture(TestCommand(ui, repository))

    def get_test_ui_and_cmd2(self, options=(), args=()):
        self.dirty()
        ui = UI(options=options, args=args)
        ui.here = self.tempdir
        cmd = run.run(ui)
        ui.set_command(cmd)
        return ui, cmd

    def dirty(self):
        # Ugly: TODO - improve testresources to make this go away.
        dict(self.resources)["tempdir"]._dirty = True

    def config_path(self):
        return os.path.join(self.tempdir, ".testr.conf")

    def set_config(self, text):
        stream = open(self.config_path(), "wt")
        try:
            stream.write(text)
        finally:
            stream.close()

    def test_takes_ui(self):
        ui = UI()
        ui.here = self.tempdir
        command = TestCommand(ui, None)
        self.assertEqual(command.ui, ui)

    def test_TestCommand_is_a_fixture(self):
        ui = UI()
        ui.here = self.tempdir
        command = TestCommand(ui, None)
        command.setUp()
        command.cleanUp()

    def test_TestCommand_get_run_command_outside_setUp_fails(self):
        self.dirty()
        ui = UI()
        ui.here = self.tempdir
        command = TestCommand(ui, None)
        self.set_config("[DEFAULT]\ntest_command=foo\n")
        self.assertThat(command.get_run_command, raises(TypeError))
        command.setUp()
        command.cleanUp()
        self.assertThat(command.get_run_command, raises(TypeError))

    def test_TestCommand_cleanUp_disposes_instances(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo\ninstance_dispose=bar $INSTANCE_IDS\n"
        )
        command._instances.update([b"baz", b"quux"])
        command.cleanUp()
        command.setUp()
        self.assertEqual(
            [
                ("values", [("running", "bar baz quux")]),
                ("popen", ("bar baz quux",), {"shell": True}),
                ("communicate",),
            ],
            ui.outputs,
        )

    def test_TestCommand_cleanUp_disposes_instances_fail_raises(self):
        ui, command = self.get_test_ui_and_cmd()
        ui.proc_results = [1]
        self.set_config(
            "[DEFAULT]\ntest_command=foo\ninstance_dispose=bar $INSTANCE_IDS\n"
        )
        command._instances.update([b"baz", b"quux"])
        self.assertThat(
            command.cleanUp,
            raises(ValueError("Disposing of instances failed, return 1")),
        )
        command.setUp()

    def test_get_run_command_no_config_file_errors(self):
        ui, command = self.get_test_ui_and_cmd()
        self.assertThat(
            command.get_run_command, raises(ValueError("No .testr.conf config file"))
        )

    def test_get_run_command_no_config_settings_errors(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config("")
        self.assertThat(
            command.get_run_command,
            raises(ValueError("No test_command option present in .testr.conf")),
        )

    def test_get_run_command_returns_fixture_makes_IDFILE(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDOPTION\ntest_id_option=--load-list $IDFILE\n"
        )
        fixture = command.get_run_command(["failing", "alsofailing"])
        try:
            fixture.setUp()
            list_file_path = fixture.list_file_name
            source = open(list_file_path, "rt")
            try:
                list_file_content = source.read()
            finally:
                source.close()
            self.assertEqual("failing\nalsofailing\n", list_file_content)
        finally:
            fixture.cleanUp()
        self.assertFalse(os.path.exists(list_file_path))

    def test_get_run_command_IDFILE_variable_setting(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDOPTION\ntest_id_option=--load-list $IDFILE\n"
        )
        fixture = self.useFixture(command.get_run_command(["failing", "alsofailing"]))
        expected_cmd = "foo --load-list %s" % fixture.list_file_name
        self.assertEqual(expected_cmd, fixture.cmd)

    def test_get_run_command_IDLIST_variable_setting(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config("[DEFAULT]\ntest_command=foo $IDLIST\n")
        fixture = self.useFixture(command.get_run_command(["failing", "alsofailing"]))
        expected_cmd = "foo failing alsofailing"
        self.assertEqual(expected_cmd, fixture.cmd)

    def test_get_run_command_IDLIST_default_is_empty(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config("[DEFAULT]\ntest_command=foo $IDLIST\n")
        fixture = self.useFixture(command.get_run_command())
        expected_cmd = "foo "
        self.assertEqual(expected_cmd, fixture.cmd)

    def test_get_run_command_default_and_list_expands(self):
        ui, command = self.get_test_ui_and_cmd()
        buffer = BytesIO()
        stream = subunit.StreamResultToBytes(buffer)
        stream.status(test_id="returned", test_status="exists")
        stream.status(test_id="ids", test_status="exists")
        subunit_bytes = buffer.getvalue()
        ui.proc_outputs = [subunit_bytes]
        ui.options = optparse.Values()
        ui.options.parallel = True
        ui.options.concurrency = 2
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDLIST $LISTOPT\n"
            "test_id_list_default=whoo yea\n"
            "test_list_option=--list\n"
        )
        fixture = self.useFixture(command.get_run_command())
        expected_cmd = "foo returned ids "
        self.assertEqual(expected_cmd, fixture.cmd)

    def test_get_run_command_IDLIST_default_passed_normally(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDLIST\ntest_id_list_default=whoo yea\n"
        )
        fixture = self.useFixture(command.get_run_command())
        expected_cmd = "foo whoo yea"
        self.assertEqual(expected_cmd, fixture.cmd)

    def test_IDOPTION_evalutes_empty_string_no_ids(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDOPTION\ntest_id_option=--load-list $IDFILE\n"
        )
        fixture = self.useFixture(command.get_run_command())
        expected_cmd = "foo "
        self.assertEqual(expected_cmd, fixture.cmd)

    def test_group_regex_option(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDOPTION\n"
            "test_id_option=--load-list $IDFILE\n"
            "group_regex=([^\\.]+\\.)+\n"
        )
        fixture = self.useFixture(command.get_run_command())
        self.assertEqual("pkg.class.", fixture._group_callback("pkg.class.test_method"))

    def test_extra_args_passed_in(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDOPTION\ntest_id_option=--load-list $IDFILE\n"
        )
        fixture = self.useFixture(command.get_run_command(testargs=("bar", "quux")))
        expected_cmd = "foo  bar quux"
        self.assertEqual(expected_cmd, fixture.cmd)

    def test_list_tests_requests_concurrency_instances(self):
        # testr list-tests is non-parallel, so needs 1 instance.
        # testr run triggering list-tests will want to run parallel on all, so
        # avoid latency by asking for whatever concurrency is up front.
        # This covers the case for non-listing runs as well, as the code path
        # is common.
        self.dirty()
        ui = UI(options=[("concurrency", 2), ("parallel", True)])
        ui.here = self.tempdir
        cmd = run.run(ui)
        ui.set_command(cmd)
        ui.proc_outputs = [b"returned\ninstances\n"]
        command = self.useFixture(TestCommand(ui, None))
        self.set_config(
            "[DEFAULT]\ntest_command=foo $LISTOPT $IDLIST\ntest_id_list_default=whoo yea\n"
            "test_list_option=--list\n"
            "instance_provision=provision -c $INSTANCE_COUNT\n"
            "instance_execute=quux $INSTANCE_ID -- $COMMAND\n"
        )
        fixture = self.useFixture(command.get_run_command(test_ids=["1"]))
        fixture.list_tests()
        self.assertEqual(set([b"returned", b"instances"]), command._instances)
        self.assertEqual(set([]), command._allocated_instances)
        self.assertThat(
            ui.outputs,
            MatchesAny(
                Equals(
                    [
                        ("values", [("running", "provision -c 2")]),
                        ("popen", ("provision -c 2",), {"shell": True, "stdout": -1}),
                        ("communicate",),
                        (
                            "values",
                            [("running", "quux instances -- foo --list whoo yea")],
                        ),
                        (
                            "popen",
                            ("quux instances -- foo --list whoo yea",),
                            {"shell": True, "stdin": -1, "stdout": -1},
                        ),
                        ("communicate",),
                    ]
                ),
                Equals(
                    [
                        ("values", [("running", "provision -c 2")]),
                        ("popen", ("provision -c 2",), {"shell": True, "stdout": -1}),
                        ("communicate",),
                        (
                            "values",
                            [("running", "quux returned -- foo --list whoo yea")],
                        ),
                        (
                            "popen",
                            ("quux returned -- foo --list whoo yea",),
                            {"shell": True, "stdin": -1, "stdout": -1},
                        ),
                        ("communicate",),
                    ]
                ),
            ),
        )

    def test_list_tests_uses_instances(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $LISTOPT $IDLIST\ntest_id_list_default=whoo yea\n"
            "test_list_option=--list\n"
            "instance_execute=quux $INSTANCE_ID -- $COMMAND\n"
        )
        fixture = self.useFixture(command.get_run_command())
        command._instances.add(b"bar")
        fixture.list_tests()
        self.assertEqual(set([b"bar"]), command._instances)
        self.assertEqual(set([]), command._allocated_instances)
        self.assertEqual(
            [
                ("values", [("running", "quux bar -- foo --list whoo yea")]),
                (
                    "popen",
                    ("quux bar -- foo --list whoo yea",),
                    {"shell": True, "stdin": -1, "stdout": -1},
                ),
                ("communicate",),
            ],
            ui.outputs,
        )

    def test_list_tests_cmd(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $LISTOPT $IDLIST\ntest_id_list_default=whoo yea\n"
            "test_list_option=--list\n"
        )
        fixture = self.useFixture(command.get_run_command())
        expected_cmd = "foo --list whoo yea"
        self.assertEqual(expected_cmd, fixture.list_cmd)

    def test_list_tests_parsing(self):
        buffer = BytesIO()
        stream = subunit.StreamResultToBytes(buffer)
        stream.status(test_id="returned", test_status="exists")
        stream.status(test_id="ids", test_status="exists")
        subunit_bytes = buffer.getvalue()
        ui, command = self.get_test_ui_and_cmd()
        ui.proc_outputs = [subunit_bytes]
        self.set_config(
            "[DEFAULT]\ntest_command=foo $LISTOPT $IDLIST\ntest_id_list_default=whoo yea\n"
            "test_list_option=--list\n"
        )
        fixture = self.useFixture(command.get_run_command())
        self.assertEqual(set(["returned", "ids"]), set(fixture.list_tests()))

    def test_list_tests_nonzero_exit(self):
        ui, command = self.get_test_ui_and_cmd()
        ui.proc_results = [1]
        self.set_config(
            "[DEFAULT]\ntest_command=foo $LISTOPT $IDLIST\ntest_id_list_default=whoo yea\n"
            "test_list_option=--list\n"
        )
        fixture = self.useFixture(command.get_run_command())
        self.assertThat(lambda: fixture.list_tests(), raises(ValueError))

    def test_partition_tests_smoke(self):
        repo = memory.RepositoryFactory().initialise("memory:")
        # Seed with 1 slow and 2 tests making up 2/3 the time.
        result = repo.get_inserter()
        result.startTestRun()
        run_timed("slow", 3, result)
        run_timed("fast1", 1, result)
        run_timed("fast2", 1, result)
        result.stopTestRun()
        ui, command = self.get_test_ui_and_cmd(repository=repo)
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDLIST $LISTOPT\ntest_list_option=--list\n"
        )
        fixture = self.useFixture(command.get_run_command())
        # partitioning by two generates 'slow' and the two fast ones as partitions
        # flushed out by equal numbers of unknown duration tests.
        test_ids = frozenset(
            ["slow", "fast1", "fast2", "unknown1", "unknown2", "unknown3", "unknown4"]
        )
        partitions = fixture.partition_tests(test_ids, 2)
        self.assertTrue("slow" in partitions[0])
        self.assertFalse("fast1" in partitions[0])
        self.assertFalse("fast2" in partitions[0])
        self.assertFalse("slow" in partitions[1])
        self.assertTrue("fast1" in partitions[1])
        self.assertTrue("fast2" in partitions[1])
        self.assertEqual(3, len(partitions[0]))
        self.assertEqual(4, len(partitions[1]))

    def test_partition_tests_914359(self):
        # When two partitions have the same duration, timed tests should be
        # appended to the shortest partition. In theory this doesn't matter,
        # but in practice, if a test is recorded with 0 duration (e.g. due to a
        # bug), it is better to have them split out rather than all in one
        # partition. 0 duration tests are unlikely to really be 0 duration.
        repo = memory.RepositoryFactory().initialise("memory:")
        # Seed with two 0-duration tests.
        result = repo.get_inserter()
        result.startTestRun()
        run_timed("zero1", 0, result)
        run_timed("zero2", 0, result)
        result.stopTestRun()
        ui, command = self.get_test_ui_and_cmd(repository=repo)
        self.set_config("[DEFAULT]\ntest_command=foo $IDLIST\n")
        fixture = self.useFixture(command.get_run_command())
        # partitioning by two should generate two one-entry partitions.
        test_ids = frozenset(["zero1", "zero2"])
        partitions = fixture.partition_tests(test_ids, 2)
        self.assertEqual(1, len(partitions[0]))
        self.assertEqual(1, len(partitions[1]))

    def test_partition_tests_with_grouping(self):
        repo = memory.RepositoryFactory().initialise("memory:")
        result = repo.get_inserter()
        result.startTestRun()
        run_timed("TestCase1.slow", 3, result)
        run_timed("TestCase2.fast1", 1, result)
        run_timed("TestCase2.fast2", 1, result)
        result.stopTestRun()
        ui, command = self.get_test_ui_and_cmd(repository=repo)
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDLIST $LISTOPT\ntest_list_option=--list\n"
        )
        fixture = self.useFixture(command.get_run_command())
        test_ids = frozenset(
            [
                "TestCase1.slow",
                "TestCase1.fast",
                "TestCase1.fast2",
                "TestCase2.fast1",
                "TestCase3.test1",
                "TestCase3.test2",
                "TestCase2.fast2",
                "TestCase4.test",
                "testdir.testfile.TestCase5.test",
            ]
        )

        def group_id(test_id, regex=re.compile("TestCase[0-5]")):
            match = regex.match(test_id)
            if match:
                return match.group(0)

        # There isn't a public way to define a group callback [as yet].
        fixture._group_callback = group_id
        partitions = fixture.partition_tests(test_ids, 2)
        # Timed groups are deterministic:
        self.assertTrue("TestCase2.fast1" in partitions[0])
        self.assertTrue("TestCase2.fast2" in partitions[0])
        self.assertTrue("TestCase1.slow" in partitions[1])
        self.assertTrue("TestCase1.fast" in partitions[1])
        self.assertTrue("TestCase1.fast2" in partitions[1])
        # Untimed groups just need to be kept together:
        if "TestCase3.test1" in partitions[0]:
            self.assertTrue("TestCase3.test2" in partitions[0])
        if "TestCase4.test" not in partitions[0]:
            self.assertTrue("TestCase4.test" in partitions[1])
        if "testdir.testfile.TestCase5.test" not in partitions[0]:
            self.assertTrue("testdir.testfile.TestCase5.test" in partitions[1])

    def test_run_tests_with_instances(self):
        # when there are instances and no instance_execute, run_tests acts as
        # normal.
        ui, command = self.get_test_ui_and_cmd()
        self.set_config("[DEFAULT]\ntest_command=foo $IDLIST\n")
        command._instances.update([b"foo", b"bar"])
        fixture = self.useFixture(command.get_run_command())
        fixture.run_tests()
        self.assertEqual(
            [
                ("values", [("running", "foo ")]),
                ("popen", ("foo ",), {"shell": True, "stdin": -1, "stdout": -1}),
            ],
            ui.outputs,
        )

    def test_run_tests_with_existing_instances_configured(self):
        # when there are instances present, they are pulled out for running
        # tests.
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDLIST\n"
            "instance_execute=quux $INSTANCE_ID -- $COMMAND\n"
        )
        command._instances.add(b"bar")
        fixture = self.useFixture(command.get_run_command(test_ids=["1"]))
        procs = fixture.run_tests()
        self.assertEqual(
            [
                ("values", [("running", "quux bar -- foo 1")]),
                (
                    "popen",
                    ("quux bar -- foo 1",),
                    {"shell": True, "stdin": -1, "stdout": -1},
                ),
            ],
            ui.outputs,
        )
        # No --parallel, so the one instance should have been allocated.
        self.assertEqual(set([b"bar"]), command._instances)
        self.assertEqual(set([b"bar"]), command._allocated_instances)
        # And after the process is run, bar is returned for re-use.
        procs[0].stdout.read()
        procs[0].wait()
        self.assertEqual(0, procs[0].returncode)
        self.assertEqual(set([b"bar"]), command._instances)
        self.assertEqual(set(), command._allocated_instances)

    def test_run_tests_allocated_instances_skipped(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDLIST\n"
            "instance_execute=quux $INSTANCE_ID -- $COMMAND\n"
        )
        command._instances.update([b"bar", b"baz"])
        command._allocated_instances.add(b"baz")
        fixture = self.useFixture(command.get_run_command(test_ids=["1"]))
        procs = fixture.run_tests()
        self.assertEqual(
            [
                ("values", [("running", "quux bar -- foo 1")]),
                (
                    "popen",
                    ("quux bar -- foo 1",),
                    {"shell": True, "stdin": -1, "stdout": -1},
                ),
            ],
            ui.outputs,
        )
        # No --parallel, so the one instance should have been allocated.
        self.assertEqual(set([b"bar", b"baz"]), command._instances)
        self.assertEqual(set([b"bar", b"baz"]), command._allocated_instances)
        # And after the process is run, bar is returned for re-use.
        procs[0].wait()
        procs[0].stdout.read()
        self.assertEqual(0, procs[0].returncode)
        self.assertEqual(set([b"bar", b"baz"]), command._instances)
        self.assertEqual(set([b"baz"]), command._allocated_instances)

    def test_run_tests_list_file_in_FILES(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config(
            "[DEFAULT]\ntest_command=foo $IDFILE\n"
            "instance_execute=quux $INSTANCE_ID $FILES -- $COMMAND\n"
        )
        command._instances.add(b"bar")
        fixture = self.useFixture(command.get_run_command(test_ids=["1"]))
        list_file = fixture.list_file_name
        procs = fixture.run_tests()
        expected_cmd = "quux bar %s -- foo %s" % (list_file, list_file)
        self.assertEqual(
            [
                ("values", [("running", expected_cmd)]),
                ("popen", (expected_cmd,), {"shell": True, "stdin": -1, "stdout": -1}),
            ],
            ui.outputs,
        )
        # No --parallel, so the one instance should have been allocated.
        self.assertEqual(set([b"bar"]), command._instances)
        self.assertEqual(set([b"bar"]), command._allocated_instances)
        # And after the process is run, bar is returned for re-use.
        procs[0].stdout.read()
        self.assertEqual(0, procs[0].returncode)
        self.assertEqual(set([b"bar"]), command._instances)
        self.assertEqual(set(), command._allocated_instances)

    def test_filter_tags_parsing(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config("[DEFAULT]\nfilter_tags=foo bar\n")
        self.assertEqual(set(["foo", "bar"]), command.get_filter_tags())

    def test_callout_concurrency(self):
        ui, command = self.get_test_ui_and_cmd()
        ui.proc_outputs = [b"4"]
        self.set_config("[DEFAULT]\ntest_run_concurrency=probe\ntest_command=foo\n")
        fixture = self.useFixture(command.get_run_command())
        self.assertEqual(4, fixture.callout_concurrency())
        self.assertEqual(
            [
                ("popen", ("probe",), {"shell": True, "stdin": -1, "stdout": -1}),
                ("communicate",),
            ],
            ui.outputs,
        )

    def test_callout_concurrency_failed(self):
        ui, command = self.get_test_ui_and_cmd()
        ui.proc_results = [1]
        self.set_config("[DEFAULT]\ntest_run_concurrency=probe\ntest_command=foo\n")
        fixture = self.useFixture(command.get_run_command())
        self.assertThat(
            lambda: fixture.callout_concurrency(),
            raises(ValueError("test_run_concurrency failed: exit code 1, stderr=''")),
        )
        self.assertEqual(
            [
                ("popen", ("probe",), {"shell": True, "stdin": -1, "stdout": -1}),
                ("communicate",),
            ],
            ui.outputs,
        )

    def test_callout_concurrency_not_set(self):
        ui, command = self.get_test_ui_and_cmd()
        self.set_config("[DEFAULT]\ntest_command=foo\n")
        fixture = self.useFixture(command.get_run_command())
        self.assertEqual(None, fixture.callout_concurrency())
        self.assertEqual([], ui.outputs)

    def test_filter_tests_by_regex_only(self):
        buffer = BytesIO()
        stream = subunit.StreamResultToBytes(buffer)
        stream.status(test_id="returned", test_status="exists")
        stream.status(test_id="ids", test_status="exists")
        subunit_bytes = buffer.getvalue()
        ui, command = self.get_test_ui_and_cmd()
        ui.proc_outputs = [subunit_bytes]
        self.set_config(
            "[DEFAULT]\ntest_command=foo $LISTOPT $IDLIST\ntest_id_list_default=whoo yea\n"
            "test_list_option=--list\n"
        )
        filters = ["return"]
        fixture = self.useFixture(command.get_run_command(test_filters=filters))
        self.assertEqual(["returned"], fixture.test_ids)

    def test_filter_tests_by_regex_supplied_ids(self):
        ui, command = self.get_test_ui_and_cmd()
        ui.proc_outputs = [b"returned\nids\n"]
        self.set_config(
            "[DEFAULT]\ntest_command=foo $LISTOPT $IDLIST\ntest_id_list_default=whoo yea\n"
            "test_list_option=--list\n"
        )
        filters = ["return"]
        fixture = self.useFixture(
            command.get_run_command(
                test_ids=["return", "of", "the", "king"], test_filters=filters
            )
        )
        self.assertEqual(["return"], fixture.test_ids)

    def test_filter_tests_by_regex_supplied_ids_multi_match(self):
        ui, command = self.get_test_ui_and_cmd()
        ui.proc_outputs = [b"returned\nids\n"]
        self.set_config(
            "[DEFAULT]\ntest_command=foo $LISTOPT $IDLIST\ntest_id_list_default=whoo yea\n"
            "test_list_option=--list\n"
        )
        filters = ["return"]
        fixture = self.useFixture(
            command.get_run_command(
                test_ids=["return", "of", "the", "king", "thereisnoreturn"],
                test_filters=filters,
            )
        )
        self.assertEqual(["return", "thereisnoreturn"], fixture.test_ids)
