Skip to content

Commit f03ce12

Browse files
committed
Make the tests pass on windows.
The main points of note are: - Use temporary_filename() to get a filename without any open handles (Windows will not let you unlink a file that has open handles). - Get rid of NamedTemporaryFile/temporary_file in lieu of using our helper in util. - Pexs aren't executable on windows directly - run them as `python ...` - Skip soft/hard link tests on windows - Small fixes w.r.t. / vs \ and s/\r//g
1 parent edd0b3e commit f03ce12

File tree

9 files changed

+67
-49
lines changed

9 files changed

+67
-49
lines changed

pex/testing.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import random
77
import subprocess
8+
import sys
89
import tempfile
910
import zipfile
1011
from textwrap import dedent
@@ -13,7 +14,7 @@
1314
from .compatibility import nested
1415
from .installer import EggInstaller, Packager
1516
from .pex_builder import PEXBuilder
16-
from .util import DistributionHelper
17+
from .util import DistributionHelper, named_temporary_file
1718

1819

1920
@contextlib.contextmanager
@@ -25,6 +26,19 @@ def temporary_dir():
2526
safe_rmtree(td)
2627

2728

29+
@contextlib.contextmanager
30+
def temporary_filename():
31+
"""Creates a temporary filename.
32+
33+
This is useful when you need to pass a filename to an API. Windows requires all
34+
handles to a file be closed before deleting/renaming it, so this makes it a bit
35+
simpler."""
36+
with named_temporary_file() as fp:
37+
fp.write(b'')
38+
fp.close()
39+
yield fp.name
40+
41+
2842
def random_bytes(length):
2943
return ''.join(
3044
map(chr, (random.randint(ord('a'), ord('z')) for _ in range(length)))).encode('utf-8')
@@ -162,12 +176,12 @@ def write_simple_pex(td, exe_contents, dists=None, coverage=False):
162176
# TODO(wickman) Why not PEX.run?
163177
def run_simple_pex(pex, args=(), env=None):
164178
po = subprocess.Popen(
165-
[pex] + list(args),
179+
[sys.executable, pex] + list(args),
166180
stdout=subprocess.PIPE,
167181
stderr=subprocess.STDOUT,
168182
env=env)
169183
po.wait()
170-
return po.stdout.read(), po.returncode
184+
return po.stdout.read().replace(b'\r', b''), po.returncode
171185

172186

173187
def run_simple_pex_test(body, args=(), env=None, dists=None, coverage=False):

tests/test_environment.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
import os
55
from contextlib import contextmanager
66

7-
from twitter.common.contextutil import temporary_dir, temporary_file
7+
from twitter.common.contextutil import temporary_dir
88

99
from pex.compatibility import nested
1010
from pex.environment import PEXEnvironment
1111
from pex.pex_builder import PEXBuilder
1212
from pex.pex_info import PexInfo
13-
from pex.testing import make_bdist
13+
from pex.testing import make_bdist, temporary_filename
1414

1515

1616
@contextmanager
@@ -22,64 +22,65 @@ def yield_pex_builder(zip_safe=True):
2222

2323

2424
def test_force_local():
25-
with nested(yield_pex_builder(), temporary_dir(), temporary_file()) as (pb, pex_root, pex_file):
25+
with nested(yield_pex_builder(), temporary_dir(), temporary_filename()) as (
26+
pb, pex_root, pex_file):
2627
pb.info.pex_root = pex_root
27-
pb.build(pex_file.name)
28+
pb.build(pex_file)
2829

29-
code_cache = PEXEnvironment.force_local(pex_file.name, pb.info)
30+
code_cache = PEXEnvironment.force_local(pex_file, pb.info)
3031
assert os.path.exists(pb.info.zip_unsafe_cache)
3132
assert len(os.listdir(pb.info.zip_unsafe_cache)) == 1
3233
assert [os.path.basename(code_cache)] == os.listdir(pb.info.zip_unsafe_cache)
3334
assert set(os.listdir(code_cache)) == set([PexInfo.PATH, '__main__.py', '__main__.pyc'])
3435

3536
# idempotence
36-
assert PEXEnvironment.force_local(pex_file.name, pb.info) == code_cache
37+
assert PEXEnvironment.force_local(pex_file, pb.info) == code_cache
3738

3839

3940
def normalize(path):
40-
return os.path.normpath(os.path.realpath(path))
41+
return os.path.normpath(os.path.realpath(path)).lower()
4142

4243

4344
def test_write_zipped_internal_cache():
4445
# zip_safe pex will not be written to install cache unless always_write_cache
45-
with nested(yield_pex_builder(zip_safe=True), temporary_dir(), temporary_file()) as (
46+
with nested(yield_pex_builder(zip_safe=True), temporary_dir(), temporary_filename()) as (
4647
pb, pex_root, pex_file):
4748

4849
pb.info.pex_root = pex_root
49-
pb.build(pex_file.name)
50+
pb.build(pex_file)
5051

51-
existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file.name, pb.info)
52+
existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file, pb.info)
5253
assert len(zip_safe) == 1
5354
assert normalize(zip_safe[0].location).startswith(
54-
normalize(os.path.join(pex_file.name, pb.info.internal_cache))), (
55+
normalize(os.path.join(pex_file, pb.info.internal_cache))), (
5556
'loc: %s, cache: %s' % (
5657
normalize(zip_safe[0].location),
57-
normalize(os.path.join(pex_file.name, pb.info.internal_cache))))
58+
normalize(os.path.join(pex_file, pb.info.internal_cache))))
5859

5960
pb.info.always_write_cache = True
60-
existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file.name, pb.info)
61+
existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file, pb.info)
6162
assert len(new) == 1
6263
assert normalize(new[0].location).startswith(normalize(pb.info.install_cache))
6364

6465
# Check that we can read from the cache
65-
existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file.name, pb.info)
66+
existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file, pb.info)
6667
assert len(existing) == 1
6768
assert normalize(existing[0].location).startswith(normalize(pb.info.install_cache))
6869

6970
# non-zip_safe pex will be written to install cache
70-
with nested(yield_pex_builder(zip_safe=False), temporary_dir(), temporary_file()) as (
71+
with nested(yield_pex_builder(zip_safe=False), temporary_dir(), temporary_filename()) as (
7172
pb, pex_root, pex_file):
7273

7374
pb.info.pex_root = pex_root
74-
pb.build(pex_file.name)
75+
pb.build(pex_file)
7576

76-
existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file.name, pb.info)
77+
existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file, pb.info)
7778
assert len(new) == 1
7879
assert normalize(new[0].location).startswith(normalize(pb.info.install_cache))
7980
original_location = normalize(new[0].location)
8081

8182
# do the second time to validate idempotence of caching
82-
existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file.name, pb.info)
83+
existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file, pb.info)
8384
assert len(existing) == 1
8485
assert normalize(existing[0].location) == original_location
8586

tests/test_http.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
from io import BytesIO
77

88
import pytest
9-
from twitter.common.contextutil import temporary_file
109

1110
from pex.compatibility import PY2
1211
from pex.http import Context, RequestsContext, StreamFilelike, UrllibContext
1312
from pex.link import Link
13+
from pex.util import named_temporary_file
1414
from pex.variables import Variables
1515

1616
try:
@@ -107,7 +107,7 @@ def test_requests_context():
107107
assert fp.read() == BLOB
108108

109109
# test local reading
110-
with temporary_file() as tf:
110+
with named_temporary_file() as tf:
111111
tf.write(b'goop')
112112
tf.flush()
113113
assert context.read(Link.wrap(tf.name)) == b'goop'
@@ -225,7 +225,7 @@ def test_requests_context_retries_read_timeout_retries_exhausted():
225225
def test_urllib_context_utf8_encoding():
226226
BYTES = b'this is a decoded utf8 string'
227227

228-
with temporary_file() as tf:
228+
with named_temporary_file() as tf:
229229
tf.write(BYTES)
230230
tf.flush()
231231
local_link = Link.wrap(tf.name)

tests/test_integration.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
import os
55
import sys
66

7-
from twitter.common.contextutil import environment_as, temporary_dir, temporary_file
7+
import pytest
8+
from twitter.common.contextutil import environment_as, temporary_dir
89

10+
from pex.compatibility import WINDOWS
911
from pex.testing import run_simple_pex_test
12+
from pex.util import named_temporary_file
1013

1114

1215
def test_pex_execute():
@@ -21,7 +24,7 @@ def test_pex_raise():
2124

2225

2326
def test_pex_interpreter():
24-
with temporary_file() as fp:
27+
with named_temporary_file() as fp:
2528
fp.write(b"print('Hello world')")
2629
fp.flush()
2730

@@ -33,6 +36,7 @@ def test_pex_interpreter():
3336
assert rc == 0
3437

3538

39+
@pytest.mark.skipif(WINDOWS, reason='No symlinks on windows')
3640
def test_pex_python_symlink():
3741
with temporary_dir() as td:
3842
with environment_as(HOME=td):

tests/test_pex.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import pytest
1010
from twitter.common.contextutil import temporary_dir
1111

12-
from pex.compatibility import nested, to_bytes
12+
from pex.compatibility import WINDOWS, nested, to_bytes
1313
from pex.installer import EggInstaller, WheelInstaller
1414
from pex.pex import PEX
1515
from pex.testing import make_installer, run_simple_pex_test
@@ -137,6 +137,7 @@ def test_site_libs():
137137
assert site_packages in site_libs
138138

139139

140+
@pytest.mark.skipif(WINDOWS, reason='No symlinks on windows')
140141
def test_site_libs_symlink():
141142
with nested(mock.patch.object(PEX, '_get_site_packages'), temporary_dir()) as (
142143
mock_site_packages, tempdir):

tests/test_pex_builder.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
import zipfile
77
from contextlib import closing
88

9+
import pytest
910
from twitter.common.contextutil import temporary_dir
1011
from twitter.common.dirutil import safe_mkdir
1112

12-
from pex.compatibility import nested
13+
from pex.compatibility import WINDOWS, nested
1314
from pex.pex import PEX
1415
from pex.pex_builder import PEXBuilder
1516
from pex.testing import write_simple_pex as write_pex
@@ -102,6 +103,7 @@ def build_and_check(path, precompile):
102103
build_and_check(td3, True)
103104

104105

106+
@pytest.mark.skipif(WINDOWS, reason='No hardlinks on windows')
105107
def test_pex_builder_copy_or_link():
106108
with nested(temporary_dir(), temporary_dir(), temporary_dir()) as (td1, td2, td3):
107109
src = os.path.join(td1, 'exe.py')

tests/test_pex_info.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
22
# Licensed under the Apache License, Version 2.0 (see LICENSE).
33

4+
import os.path
5+
46
import pytest
57

68
from pex.orderedset import OrderedSet
@@ -46,15 +48,16 @@ def test_from_empty_env():
4648

4749

4850
def test_from_env():
49-
environ = dict(PEX_ROOT='/pex_root',
51+
pex_root = os.path.realpath('/pex_root')
52+
environ = dict(PEX_ROOT=pex_root,
5053
PEX_MODULE='entry:point',
5154
PEX_SCRIPT='script.sh',
5255
PEX_FORCE_LOCAL='true',
5356
PEX_INHERIT_PATH='true',
5457
PEX_IGNORE_ERRORS='true',
5558
PEX_ALWAYS_CACHE='true')
5659

57-
info = dict(pex_root='/pex_root',
60+
info = dict(pex_root=pex_root,
5861
entry_point='entry:point',
5962
script='script.sh',
6063
zip_safe=False,

tests/test_util.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@
44
import contextlib
55
import functools
66
import os
7-
import subprocess
87
import zipfile
98
from hashlib import sha1
109
from textwrap import dedent
1110

12-
from twitter.common.contextutil import temporary_dir, temporary_file
11+
from twitter.common.contextutil import temporary_dir
1312

1413
from pex.common import safe_mkdir
1514
from pex.compatibility import nested
1615
from pex.installer import EggInstaller, WheelInstaller
1716
from pex.pex_builder import PEXBuilder
18-
from pex.testing import make_bdist, temporary_content, write_zipfile
17+
from pex.testing import make_bdist, run_simple_pex, temporary_content, write_zipfile
1918
from pex.util import CacheHelper, DistributionHelper, named_temporary_file
2019

2120
try:
@@ -27,17 +26,17 @@
2726
def test_hash():
2827
empty_hash = sha1().hexdigest()
2928

30-
with temporary_file() as fp:
29+
with named_temporary_file() as fp:
3130
fp.flush()
3231
assert empty_hash == CacheHelper.hash(fp.name)
3332

34-
with temporary_file() as fp:
33+
with named_temporary_file() as fp:
3534
string = b'asdf' * 1024 * sha1().block_size + b'extra padding'
3635
fp.write(string)
3736
fp.flush()
3837
assert sha1(string).hexdigest() == CacheHelper.hash(fp.name)
3938

40-
with temporary_file() as fp:
39+
with named_temporary_file() as fp:
4140
empty_hash = sha1()
4241
fp.write(b'asdf')
4342
fp.flush()
@@ -59,7 +58,7 @@ def test_hash_consistency():
5958
for reverse in (False, True):
6059
with temporary_content(CONTENT) as td:
6160
dir_hash = CacheHelper.dir_hash(td)
62-
with temporary_file() as tf:
61+
with named_temporary_file() as tf:
6362
write_zipfile(td, tf.name, reverse=reverse)
6463
with contextlib.closing(zipfile.ZipFile(tf.name, 'r')) as zf:
6564
zip_hash = CacheHelper.zip_hash(zf)
@@ -147,18 +146,13 @@ def test_access_zipped_assets_integration():
147146
pex = os.path.join(td2, 'app.pex')
148147
pb.build(pex)
149148

150-
po = subprocess.Popen(
151-
[pex],
152-
stdout=subprocess.PIPE,
153-
stderr=subprocess.STDOUT)
154-
po.wait()
155-
output = po.stdout.read()
149+
output, returncode = run_simple_pex(pex)
156150
try:
157151
output = output.decode('UTF-8')
158152
except ValueError:
159153
pass
160154
assert output == 'accessed\n'
161-
assert po.returncode == 0
155+
assert returncode == 0
162156

163157

164158
def test_named_temporary_file():

tests/test_variables.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
22
# Licensed under the Apache License, Version 2.0 (see LICENSE).
33

4-
import tempfile
5-
64
import pytest
75

6+
from pex.util import named_temporary_file
87
from pex.variables import Variables
98

109

@@ -85,23 +84,23 @@ def test_pex_get_kv():
8584

8685

8786
def test_pex_from_rc():
88-
with tempfile.NamedTemporaryFile(mode='w') as pexrc:
87+
with named_temporary_file(mode='w') as pexrc:
8988
pexrc.write('HELLO=42')
9089
pexrc.flush()
9190
v = Variables(rc=pexrc.name)
9291
assert v._get_int('HELLO') == 42
9392

9493

9594
def test_pexrc_precedence():
96-
with tempfile.NamedTemporaryFile(mode='w') as pexrc:
95+
with named_temporary_file(mode='w') as pexrc:
9796
pexrc.write('HELLO=FORTYTWO')
9897
pexrc.flush()
9998
v = Variables(environ={'HELLO': 42}, rc=pexrc.name)
10099
assert v._get_int('HELLO') == 42
101100

102101

103102
def test_rc_ignore():
104-
with tempfile.NamedTemporaryFile(mode='w') as pexrc:
103+
with named_temporary_file(mode='w') as pexrc:
105104
pexrc.write('HELLO=FORTYTWO')
106105
pexrc.flush()
107106
v = Variables(environ={'PEX_IGNORE_RCFILES': 'True'}, rc=pexrc.name)

0 commit comments

Comments
 (0)