Repository: hylang/hy

Issue:
Tests are failing on Windows
I'm not getting these on Linux, so Travis didn't notice. 

```
============================= test session starts =============================
platform win32 -- Python 3.6.1, pytest-3.0.7, py-1.4.33, pluggy-0.4.0
rootdir: C:\Users\ME\hytest\hy-master, inifile: setup.cfg
collected 153 items

tests\test_bin.py ......F................F....
tests\test_lex.py .................................
tests\test_models.py ..............
tests\compilers\test_ast.py .................................................................
tests\compilers\test_compiler.py ...
tests\importer\test_importer.py .....
tests\importer\test_pyc.py .
tests\macros\test_macro_processor.py ...
tests\macros\test_sharp_macros.py .

================================== FAILURES ===================================
_________________ test_bin_hy_stdin_error_underline_alignment _________________

    def test_bin_hy_stdin_error_underline_alignment():
        _, err = run_cmd("hy", "(defmacro mabcdefghi [x] x)\n(mabcdefghi)")
>       assert "\n  (mabcdefghi)\n  ^----------^" in err
E       assert '\n  (mabcdefghi)\n  ^----------^' in 'hy unknown using CPython(v3.6.1:69c0db5) 3.6.1 on Windows\r\n  File "<input>", line 1, column 1\r\n\r\n  (mabcdefghi)...or: b"expanding `mabcdefghi\': missing 1 required positional argument: \'x\'"\r\n\r\n\r\n\r\nnow exiting HyREPL...\r\n'

tests\test_bin.py:125: AssertionError
__________________________ test_bin_hy_byte_compile ___________________________

    def test_bin_hy_byte_compile():
    
        modname = "tests.resources.bin.bytecompile"
        fpath = modname.replace(".", "/") + ".hy"
    
        for can_byte_compile in [True, False]:
            for cmd in ["hy " + fpath,
                        "hy -m " + modname,
                        "hy -c '(import {})'".format(modname)]:
    
                rm(get_bytecode_path(fpath))
    
                if not can_byte_compile:
                    # Keep Hy from being able to byte-compile the module by
                    # creating a directory at the target location.
                    os.mkdir(get_bytecode_path(fpath))
    
                # Whether or not we can byte-compile the module, we should be able
                # to run it.
>               output, _ = run_cmd(cmd)

tests\test_bin.py:278: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cmd = "hy -c '(import tests.resources.bin.bytecompile)'", stdin_data = None
expect = 0

    def run_cmd(cmd, stdin_data=None, expect=0):
        p = subprocess.Popen(os.path.join(hy_dir, cmd),
                             stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             shell=True)
        if stdin_data is not None:
            p.stdin.write(stdin_data.encode('ASCII'))
            p.stdin.flush()
            p.stdin.close()
        # Read stdout and stderr otherwise if the PIPE buffer is full, we might
        # wait for ever…
        stdout = ""
        stderr = ""
        while p.poll() is None:
            stdout += p.stdout.read().decode('utf-8')
            stderr += p.stderr.read().decode('utf-8')
>       assert p.returncode == expect
E       assert 1 == 0
E        +  where 1 = <subprocess.Popen object at 0x0000021EDA2D2470>.returncode

tests\test_bin.py:55: AssertionError
==================== 2 failed, 151 passed in 30.66 seconds ====================
```
This is using the latest Hy master downloaded as a zip from github, and a fresh virtualenv.

Also, http://docs.hylang.org/en/latest/hacking.html is still showing instructions to use **nose** instead of **pytest**. Why isn't that updating?


Discussion:
> hy unknown

Are you sure the test is running the right version of Hy? If you ran setup.py, the version file should've been generated and Hy should be able to read it. Of course, the new version of Hy needs to be in your PATH, or whatever it's called on Windows, in order for these tests to work. 

> Also, http://docs.hylang.org/en/latest/hacking.html is still showing instructions to use nose instead of pytest. Why isn't that updating?

[Read the Docs build failures.](https://readthedocs.org/projects/hy/builds/) I haven't looked into them because I still don't have edit access. I just sent Paul an email about that.
I thought that the `pip install -e .` step was supposed to run `setup.py`? There's no instruction to run it directly in the "Hacking on Hy" document. And in any case, the version shows up properly on Linux without any extra steps.

The version file doesn't seem to have been generated, but where exactly should it be located on Windows? I found it on Linux at `<my virtualenv for hy>` `/lib/python3.5/site-packages/hy/version.py`. The equivalent location doesn't seem to exist on a Windows virtualenv either. I found `.\Lib\site-packages\`, but no `hy\` in there, only `hy.egg-link`.

I don't have a global Hy install on this machine, only virtualenvs. What other Hy version could the tests possibly be finding? When the virtualenv is active, the `hy` command launches the Hy repl as expected. It still says the version is "unknown" though. I am, of course, launching `pytest` from the active virtualenv.
> I thought that the `pip install -e .` step was supposed to run `setup.py`?

It should, yeah.

> The version file doesn't seem to have been generated, but where exactly should it be located on Windows?

See `VERSIONFILE` in `setup.py`. Also, you can remove the `except` block there to see what error is preventing the version file from being generated.

> What other Hy version could the tests possibly be finding?

You could try putting something like this in one of the failing test functions:

    print(run_cmd("hy", "(import hy) (print hy.__file__)"))
> remove the `except` block there to see what error is preventing the version file from being generated.

I figured out why the version wasn't generated. We added the git hash to Hy's version string. I when I ran that first test, I had downloaded master as a zip from github, which it turns out, has no hidden .git folder. It's not a repository. No git, no hash. I think setup.py could handle this better and say `hy 0.12.1+unknown` instead of just `unknown` in such cases.

So I cloned the repo using Cygwin's git and installed into a new virtualenv. Now I've got the version, but I still get the two failing tests:
```
============================= test session starts =============================
platform win32 -- Python 3.6.1, pytest-3.0.7, py-1.4.33, pluggy-0.4.0
rootdir: C:\Users\ME\hytest\hy, inifile: setup.cfg
collected 153 items

tests\test_bin.py ......F................F....
tests\test_lex.py .................................
tests\test_models.py ..............
tests\compilers\test_ast.py .................................................................
tests\compilers\test_compiler.py ...
tests\importer\test_importer.py .....
tests\importer\test_pyc.py .
tests\macros\test_macro_processor.py ...
tests\macros\test_sharp_macros.py .

================================== FAILURES ===================================
_________________ test_bin_hy_stdin_error_underline_alignment _________________

    def test_bin_hy_stdin_error_underline_alignment():
        _, err = run_cmd("hy", "(defmacro mabcdefghi [x] x)\n(mabcdefghi)")
>       assert "\n  (mabcdefghi)\n  ^----------^" in err
E       assert '\n  (mabcdefghi)\n  ^----------^' in 'hy 0.12.1+87.ga537d33 using CPython(v3.6.1:69c0db5) 3.6.1 on Windows\r\n  File "<input>", line 1, column 1\r\n\r\n  (...or: b"expanding `mabcdefghi\': missing 1 required positional argument: \'x\'"\r\n\r\n\r\n\r\nnow exiting HyREPL...\r\n'

tests\test_bin.py:125: AssertionError
__________________________ test_bin_hy_byte_compile ___________________________

    def test_bin_hy_byte_compile():
    
        modname = "tests.resources.bin.bytecompile"
        fpath = modname.replace(".", "/") + ".hy"
    
        for can_byte_compile in [True, False]:
            for cmd in ["hy " + fpath,
                        "hy -m " + modname,
                        "hy -c '(import {})'".format(modname)]:
    
                rm(get_bytecode_path(fpath))
    
                if not can_byte_compile:
                    # Keep Hy from being able to byte-compile the module by
                    # creating a directory at the target location.
                    os.mkdir(get_bytecode_path(fpath))
    
                # Whether or not we can byte-compile the module, we should be able
                # to run it.
>               output, _ = run_cmd(cmd)

tests\test_bin.py:278: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cmd = "hy -c '(import tests.resources.bin.bytecompile)'", stdin_data = None
expect = 0

    def run_cmd(cmd, stdin_data=None, expect=0):
        p = subprocess.Popen(os.path.join(hy_dir, cmd),
                             stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             shell=True)
        if stdin_data is not None:
            p.stdin.write(stdin_data.encode('ASCII'))
            p.stdin.flush()
            p.stdin.close()
        # Read stdout and stderr otherwise if the PIPE buffer is full, we might
        # wait for ever…
        stdout = ""
        stderr = ""
        while p.poll() is None:
            stdout += p.stdout.read().decode('utf-8')
            stderr += p.stderr.read().decode('utf-8')
>       assert p.returncode == expect
E       assert 1 == 0
E        +  where 1 = <subprocess.Popen object at 0x00000160BB8BD550>.returncode

tests\test_bin.py:55: AssertionError
==================== 2 failed, 151 passed in 38.34 seconds ====================
```
Pytest also prints a traceback on stderr:
```
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "c:\users\me\hytest\2venv-hy\lib\site-packages\clint\packages\colorama\initialise.py", line 17, in reset_all
    AnsiToWin32(orig_stdout).reset_all()
  File "c:\users\me\hytest\2venv-hy\lib\site-packages\clint\packages\colorama\ansitowin32.py", line 64, in __init__
    convert = on_windows and is_a_tty(wrapped)
  File "c:\users\me\hytest\2venv-hy\lib\site-packages\clint\packages\colorama\ansitowin32.py", line 15, in is_a_tty
    return hasattr(stream, 'isatty') and stream.isatty()
ValueError: I/O operation on closed file
```
None of the native tests are running on Windows!

I just noticed this, but you can see from the output that the last testfile is `tests\macros\test_sharp_macros.py`. The native tests should follow.

Pytest is clearly not set up properly for Windows. Am I running the commands wrong? Once I'm in my venv, at the root folder, I run `pytest`. Was there something else to it?
No, that's supposed to suffice. pytest-dev/pytest#2374 is supposed to allow the native tests to be run, and you have a sufficiently new version of pytest, and it looks like the pytest people do run their tests on Windows, using AppVeyor. So, I got nothin'.
Well, you also need to install it, but you probably already did that. I often say `git clean -dfx . && pip install -e . && pytest --ignore=tests/test_bin.py` to make sure everything's up to date.
Why ignore the `test_bin.py`? Is is just because it's slow? (Can we fix that?) The failing tests were in that file, so of course that avoids them. I still get an error after the tests though.
```
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "c:\users\me\workspace\hy36-gilch\lib\site-packages\clint\packages\colorama\initialise.py", line 17, in reset_all
    AnsiToWin32(orig_stdout).reset_all()
  File "c:\users\me\workspace\hy36-gilch\lib\site-packages\clint\packages\colorama\ansitowin32.py", line 64, in __init__
    convert = on_windows and is_a_tty(wrapped)
  File "c:\users\me\workspace\hy36-gilch\lib\site-packages\clint\packages\colorama\ansitowin32.py", line 15, in is_a_tty
    return hasattr(stream, 'isatty') and stream.isatty()
ValueError: I/O operation on closed file
```
I think this is just the colorama problem from pytest-dev/pytest#2465, which is not our issue.

I tried the `git clean -dfx` and `pip install -e .` and still get failing tests in `test_bin.py`. That part might be more about how those particular tests are written than about Pytest on Windows, but the native tests still aren't running either.
> Why ignore the `test_bin.py`? Is is just because it's slow?

Yes. I don't mean to suggest you should literally ignore `test_bin`. I just don't usually run it, unless I'm working on something related or I'm about to push.

> Can we fix that?

Possibly. I haven't explored this because my guess is that most ways of speeding these tests up would avoid running a real process for each subtest and hence wouldn't quite test everything that the current `test_bin` does.

Resolving patch:
diff --git a/conftest.py b/conftest.py
--- a/conftest.py
+++ b/conftest.py
@@ -1,10 +1,13 @@
 import _pytest
 import hy
+import os
 from hy._compat import PY3, PY35
 
+NATIVE_TESTS = os.path.join("", "tests", "native_tests", "")
+
 def pytest_collect_file(parent, path):
     if (path.ext == ".hy"
-            and "/tests/native_tests/" in path.dirname + "/"
+            and NATIVE_TESTS in path.dirname + os.sep
             and path.basename != "__init__.hy"
             and not ("py3_only" in path.basename and not PY3)
             and not ("py35_only" in path.basename and not PY35)):
diff --git a/hy/importer.py b/hy/importer.py
--- a/hy/importer.py
+++ b/hy/importer.py
@@ -98,7 +98,7 @@ def import_file_to_module(module_name, fpath, loader=None):
         try:
             _ast = import_file_to_ast(fpath, module_name)
             module = imp.new_module(module_name)
-            module.__file__ = fpath
+            module.__file__ = os.path.normpath(fpath)
             code = ast_compile(_ast, fpath, "exec")
             if not os.environ.get('PYTHONDONTWRITEBYTECODE'):
                 try:
@@ -121,7 +121,7 @@ def import_file_to_module(module_name, fpath, loader=None):
         sys.modules[module_name] = module
         module.__name__ = module_name
 
-    module.__file__ = fpath
+    module.__file__ = os.path.normpath(fpath)
     if loader:
         module.__loader__ = loader
     if is_package(module_name):


Test patch:
diff --git a/tests/test_bin.py b/tests/test_bin.py
--- a/tests/test_bin.py
+++ b/tests/test_bin.py
@@ -5,12 +5,16 @@
 # license. See the LICENSE.
 
 import os
-import subprocess
+from pipes import quote
 import re
-from hy._compat import PY3, PY35
-from hy.importer import get_bytecode_path
+import shlex
+import subprocess
+
 import pytest
 
+from hy._compat import PY3, PY35, builtins
+from hy.importer import get_bytecode_path
+
 
 hy_dir = os.environ.get('HY_DIR', '')
 
@@ -24,12 +28,14 @@ def run_cmd(cmd, stdin_data=None, expect=0, dontwritebytecode=False):
     if dontwritebytecode:
         env = dict(os.environ)
         env["PYTHONDONTWRITEBYTECODE"] = "1"
-    p = subprocess.Popen(os.path.join(hy_dir, cmd),
+    cmd = shlex.split(cmd)
+    cmd[0] = os.path.join(hy_dir, cmd[0])
+    p = subprocess.Popen(cmd,
                          stdin=subprocess.PIPE,
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE,
                          universal_newlines=True,
-                         shell=True,
+                         shell=False,
                          env=env)
     if stdin_data is not None:
         p.stdin.write(stdin_data)
@@ -223,18 +229,22 @@ def test_hy2py():
                 if f == "py35_only_tests.hy" and not PY35:
                     continue
                 i += 1
-                output, err = run_cmd("hy2py -s -a " +
-                                      os.path.join(dirpath, f))
+                output, err = run_cmd("hy2py -s -a " + quote(os.path.join(dirpath, f)))
                 assert len(output) > 1, f
                 assert len(err) == 0, f
     assert i
 
 
 def test_bin_hy_builtins():
+    # hy.cmdline replaces builtins.exit and builtins.quit
+    # for use by hy's repl.
     import hy.cmdline  # NOQA
-
-    assert str(exit) == "Use (exit) or Ctrl-D (i.e. EOF) to exit"
-    assert str(quit) == "Use (quit) or Ctrl-D (i.e. EOF) to exit"
+    # this test will fail if run from IPython because IPython deletes
+    # builtins.exit and builtins.quit
+    assert str(builtins.exit) == "Use (exit) or Ctrl-D (i.e. EOF) to exit"
+    assert type(builtins.exit) is hy.cmdline.HyQuitter
+    assert str(builtins.quit) == "Use (quit) or Ctrl-D (i.e. EOF) to exit"
+    assert type(builtins.quit) is hy.cmdline.HyQuitter
 
 
 def test_bin_hy_main():