Skip to content

Commit

Permalink
Adds Windows support
Browse files Browse the repository at this point in the history
dbm_gnu library does not exist on Windows. Uses anydbm on
Windows instead.
Passing preexec_fn to Popen is not supported on Windows. Passes
None instead.
Makes the list command more Windows-friendly on Windows.
  • Loading branch information
claudiubelu authored and mtreinish committed Mar 8, 2017
1 parent 952d0d2 commit 2c2fc88
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 10 deletions.
6 changes: 4 additions & 2 deletions stestr/config_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ def get_run_command(self, options, test_ids=None, regexes=None):
top_dir = options.top_dir
elif self.parser.has_option('DEFAULT', 'top_dir'):
top_dir = self.parser.get('DEFAULT', 'top_dir')
command = "${PYTHON:-python} -m subunit.run discover -t" \
" %s %s $LISTOPT $IDOPTION" % (top_dir, test_path)

python = 'python' if sys.platform == 'win32' else '${PYTHON:-python}'
command = "%s -m subunit.run discover -t" \
" %s %s $LISTOPT $IDOPTION" % (python, top_dir, test_path)
listopt = "--list"
idoption = "--load-list $IDFILE"
# If the command contains $IDOPTION read that command from config
Expand Down
19 changes: 11 additions & 8 deletions stestr/test_listing_fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ def _clear_SIGPIPE(self):
"""Clear SIGPIPE : child processes expect the default handler."""
signal.signal(signal.SIGPIPE, signal.SIG_DFL)

def _start_process(self, cmd):
# NOTE(claudiub): Windows does not support passing in a preexec_fn
# argument.
preexec_fn = None if sys.platform == 'win32' else self._clear_SIGPIPE
return subprocess.Popen(cmd, shell=True,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
preexec_fn=preexec_fn)

def list_tests(self):
"""List the tests returned by list_cmd.
Expand All @@ -178,10 +187,7 @@ def list_tests(self):
if '$LISTOPT' not in self.template:
raise ValueError("LISTOPT not configured in .testr.conf")
output.output_values([('running', self.list_cmd)])
run_proc = subprocess.Popen(self.list_cmd, shell=True,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
preexec_fn=self._clear_SIGPIPE)
run_proc = self._start_process(self.list_cmd)
out, err = run_proc.communicate()
if run_proc.returncode != 0:
new_out = six.BytesIO()
Expand All @@ -208,10 +214,7 @@ def run_tests(self):
# in the parallel case)
if self.concurrency == 1 and (test_ids is None or test_ids):
output.output_values([('running', self.cmd)])
run_proc = subprocess.Popen(
self.cmd, shell=True,
stdout=subprocess.PIPE, stdin=subprocess.PIPE,
preexec_fn=self._clear_SIGPIPE)
run_proc = self._start_process(self.cmd)
# Prevent processes stalling if they read from stdin; we could
# pass this through in future, but there is no point doing that
# until we have a working can-run-debugger-inline story.
Expand Down
58 changes: 58 additions & 0 deletions stestr/tests/test_config_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import mock

from stestr import config_file
from stestr.tests import base


class TestTestrConf(base.TestCase):

@mock.patch.object(config_file.configparser, 'ConfigParser')
def setUp(self, mock_ConfigParser):
super(TestTestrConf, self).setUp()
self._testr_conf = config_file.TestrConf(mock.sentinel.config_file)
self._testr_conf.parser = mock.Mock()

@mock.patch.object(config_file.util, 'get_repo_open')
@mock.patch.object(config_file.test_listing_fixture, 'TestListingFixture')
@mock.patch.object(config_file, 'sys')
def _check_get_run_command(self, mock_sys, mock_TestListingFixture,
mock_get_repo_open, platform='win32',
expected_python='python'):
mock_sys.platform = platform
mock_options = mock.Mock()
mock_options.test_path = 'fake_test_path'
mock_options.top_dir = 'fake_top_dir'
mock_options.group_regex = '.*'

fixture = self._testr_conf.get_run_command(mock_options,
mock.sentinel.test_ids,
mock.sentinel.regexes)

self.assertEqual(mock_TestListingFixture.return_value, fixture)
mock_get_repo_open.assert_called_once_with(mock_options.repo_type,
mock_options.repo_url)
command = "%s -m subunit.run discover -t %s %s $LISTOPT $IDOPTION" % (
expected_python, mock_options.top_dir, mock_options.test_path)
mock_TestListingFixture.assert_called_once_with(
mock.sentinel.test_ids, mock_options, command, "--list",
"--load-list $IDFILE", mock_get_repo_open.return_value,
test_filters=mock.sentinel.regexes, group_callback=mock.ANY)

def test_get_run_command_linux(self):
self._check_get_run_command(platform='linux2',
expected_python='${PYTHON:-python}')

def test_get_run_command_win32(self):
self._check_get_run_command()
47 changes: 47 additions & 0 deletions stestr/tests/test_test_listing_fixture.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import subprocess

import mock

from stestr import test_listing_fixture
from stestr.tests import base


class TestTestListingFixture(base.TestCase):

def setUp(self):
super(TestTestListingFixture, self).setUp()
self._fixture = test_listing_fixture.TestListingFixture(
mock.sentinel.test_ids, mock.sentinel.options,
mock.sentinel.cmd_template, mock.sentinel.listopt,
mock.sentinel.idoption, mock.sentinel.repository)

@mock.patch.object(subprocess, 'Popen')
@mock.patch.object(test_listing_fixture, 'sys')
def _check_start_process(self, mock_sys, mock_Popen, platform='win32',
expected_fn=None):
mock_sys.platform = platform

self._fixture._start_process(mock.sentinel.cmd)

mock_Popen.assert_called_once_with(
mock.sentinel.cmd, shell=True, stdout=subprocess.PIPE,
stdin=subprocess.PIPE, preexec_fn=expected_fn)

def test_start_process_win32(self):
self._check_start_process()

def test_start_process_linux(self):
self._check_start_process(
platform='linux2', expected_fn=self._fixture._clear_SIGPIPE)

0 comments on commit 2c2fc88

Please sign in to comment.