Repository: bokeh/bokeh

Issue:
Unit tests failing in Windows 7
Bokeh version 1.0.0dev5(?) (Cloned on 8/6/2018)
Platform: Win7x64, py36

I cloned the Bokeh repo and followed all of the setup instructions listed on [this page](https://bokeh.pydata.org/en/latest/docs/dev_guide/setup.html). Without making any code changes, I then ran the following command from the top level of the cloned repo:

```
py.test -m unit
```

**Expected behavior**: All tests to pass.
**Observed behavior**: 15 test failures, details tabulated in attachments.
[bokeh_Windows_unit_test_failures.pdf](https://github.com/bokeh/bokeh/files/2270743/bokeh_Windows_unit_test_failures.pdf)
[bokeh_Windows_unit_test_failures.xlsx](https://github.com/bokeh/bokeh/files/2270744/bokeh_Windows_unit_test_failures.xlsx)

Per Bryan Van de Ven: "The majority of those [test failures] look to be related to platform specific path and line ending differences." Per his request, I am creating this issue to document which Windows tests might need to be addressed/updated.


Discussion:

Resolving patch:
diff --git a/bokeh/util/compiler.py b/bokeh/util/compiler.py
--- a/bokeh/util/compiler.py
+++ b/bokeh/util/compiler.py
@@ -153,8 +153,13 @@ def _npmjs_path():
     global _npmjs
     if _npmjs is None:
         _npmjs = join(dirname(_nodejs_path()), "npm")
+        if sys.platform == "win32":
+            _npmjs += '.cmd'
     return _npmjs
 
+def _crlf_cr_2_lf(s):
+    return re.sub(r"\\r\\n|\\r|\\n", r"\\n", s)
+
 def _run(app, argv, input=None):
     proc = Popen([app] + argv, stdout=PIPE, stderr=PIPE, stdin=PIPE)
     (stdout, errout) = proc.communicate(input=None if input is None else json.dumps(input).encode())
@@ -162,7 +167,7 @@ def _run(app, argv, input=None):
     if proc.returncode != 0:
         raise RuntimeError(errout)
     else:
-        return stdout.decode('utf-8')
+        return _crlf_cr_2_lf(stdout.decode('utf-8'))
 
 def _run_nodejs(argv, input=None):
     return _run(_nodejs_path(), argv, input)


Test patch:
diff --git a/bokeh/application/handlers/tests/test_notebook.py b/bokeh/application/handlers/tests/test_notebook.py
--- a/bokeh/application/handlers/tests/test_notebook.py
+++ b/bokeh/application/handlers/tests/test_notebook.py
@@ -20,7 +20,9 @@
 # Standard library imports
 
 # External imports
+from packaging import version
 import nbformat
+import nbconvert
 
 # Bokeh imports
 from bokeh.document import Document
@@ -59,7 +61,10 @@ def load(filename):
         with_script_contents(source, load)
 
         assert result['handler']._runner.path == result['filename']
-        assert result['handler']._runner.source == "\n# coding: utf-8\n"
+        if version.parse(nbconvert.__version__) < version.parse("5.4"):
+            assert result['handler']._runner.source == "\n# coding: utf-8\n"
+        else:
+            assert result['handler']._runner.source == "#!/usr/bin/env python\n# coding: utf-8\n"
         assert not doc.roots
 
     def test_missing_filename_raises(self):
diff --git a/bokeh/command/tests/test_util.py b/bokeh/command/tests/test_util.py
--- a/bokeh/command/tests/test_util.py
+++ b/bokeh/command/tests/test_util.py
@@ -2,6 +2,7 @@
 
 from mock import patch
 import pytest
+import os
 
 import bokeh.command.util as util
 from bokeh.document import Document
@@ -32,10 +33,12 @@ def test_build_single_handler_application_unknown_file():
 
 @patch('warnings.warn')
 def test_build_single_handler_application_main_py(mock_warn):
-    f = tempfile.NamedTemporaryFile(suffix="main.py")
+    f = tempfile.NamedTemporaryFile(suffix="main.py", delete=False)
+    f.close() #close file to open it later on windows
     util.build_single_handler_application(f.name)
     assert mock_warn.called
     assert mock_warn.call_args[0] == (DIRSTYLE_MAIN_WARNING_COPY,)
+    os.remove(f.name)
 
 _SIZE_WARNING = "Width/height arguments will be ignored for this muliple layout. (Size valus only apply when exporting single plots.)"
 
diff --git a/bokeh/core/tests/test_templates.py b/bokeh/core/tests/test_templates.py
--- a/bokeh/core/tests/test_templates.py
+++ b/bokeh/core/tests/test_templates.py
@@ -9,6 +9,10 @@ def compute_sha256(data):
     sha256.update(data)
     return sha256.hexdigest()
 
+def _crlf_cr_2_lf_bin(s):
+    import re
+    return re.sub(b"\r\n|\r|\n", b"\n", s)
+
 pinned_template_sha256 = "22c80ab04af3eb44b5be510bedd46c89d6c6d1d73bb3437050b799ee0e2ec044"
 
 def test_autoload_template_has_changed():
@@ -18,7 +22,7 @@ def test_autoload_template_has_changed():
     created as part of https://github.com/bokeh/bokeh/issues/7125.
     """
     with io.open(join(TOP_PATH, '_templates/autoload_nb_js.js'), mode='rb') as f:
-        assert pinned_template_sha256 == compute_sha256(f.read()), \
+        assert pinned_template_sha256 == compute_sha256(_crlf_cr_2_lf_bin(f.read())), \
         """It seems that the template autoload_nb_js.js has changed.
         If this is voluntary and that proper testing of plots insertion
         in notebooks has been completed successfully, update this test
diff --git a/bokeh/io/tests/test_util.py b/bokeh/io/tests/test_util.py
--- a/bokeh/io/tests/test_util.py
+++ b/bokeh/io/tests/test_util.py
@@ -18,6 +18,7 @@
 #-----------------------------------------------------------------------------
 
 # Standard library imports
+import os
 from mock import Mock, patch, PropertyMock
 
 # External imports
@@ -40,7 +41,7 @@
 #-----------------------------------------------------------------------------
 
 def test_detect_current_filename():
-    assert biu.detect_current_filename().endswith(("py.test", "pytest"))
+    assert biu.detect_current_filename().endswith(("py.test", "pytest", "py.test-script.py"))
 
 @patch('bokeh.io.util.NamedTemporaryFile')
 def test_temp_filename(mock_tmp):
@@ -69,19 +70,19 @@ def test_default_filename():
         # a current file, access, and no share exec
         biu._no_access = lambda x: False
         r = biu.default_filename("test")
-        assert r == "/a/b/foo.test"
+        assert os.path.normpath(r) == os.path.normpath("/a/b/foo.test")
 
         # a current file, NO access, and no share exec
         biu._no_access = lambda x: True
         r = biu.default_filename("test")
-        assert r != "/a/b/foo.test"
+        assert os.path.normpath(r) != os.path.normpath("/a/b/foo.test")
         assert r.endswith(".test")
 
         # a current file, access, but WITH share exec
         biu._no_access = lambda x: False
         biu._shares_exec_prefix = lambda x: True
         r = biu.default_filename("test")
-        assert r != "/a/b/foo.test"
+        assert os.path.normpath(r) != os.path.normpath("/a/b/foo.test")
         assert r.endswith(".test")
 
         # no current file
@@ -89,7 +90,7 @@ def test_default_filename():
         biu._no_access = lambda x: False
         biu._shares_exec_prefix = lambda x: False
         r = biu.default_filename("test")
-        assert r != "/a/b/foo.test"
+        assert os.path.normpath(r) != os.path.normpath("/a/b/foo.test")
         assert r.endswith(".test")
 
     finally:
@@ -103,7 +104,6 @@ def test_default_filename():
 
 @patch('os.access')
 def test__no_access(mock_access):
-    import os
     biu._no_access("test")
     assert mock_access.called
     assert mock_access.call_args[0] == ("test", os.W_OK | os.X_OK)
diff --git a/bokeh/models/tests/test_sources.py b/bokeh/models/tests/test_sources.py
--- a/bokeh/models/tests/test_sources.py
+++ b/bokeh/models/tests/test_sources.py
@@ -664,21 +664,21 @@ def test_set_data_from_json_list(self):
 
     def test_set_data_from_json_base64(self):
         ds = ColumnDataSource()
-        data = {"foo": np.arange(3)}
+        data = {"foo": np.arange(3, dtype=np.int64)}
         json = transform_column_source_data(data)
         ds.set_from_json('data', json)
         assert np.array_equal(ds.data["foo"], data["foo"])
 
     def test_set_data_from_json_nested_base64(self):
         ds = ColumnDataSource()
-        data = {"foo": [[np.arange(3)]]}
+        data = {"foo": [[np.arange(3, dtype=np.int64)]]}
         json = transform_column_source_data(data)
         ds.set_from_json('data', json)
         assert np.array_equal(ds.data["foo"], data["foo"])
 
     def test_set_data_from_json_nested_base64_and_list(self):
         ds = ColumnDataSource()
-        data = {"foo": [np.arange(3), [1, 2, 3]]}
+        data = {"foo": [np.arange(3, dtype=np.int64), [1, 2, 3]]}
         json = transform_column_source_data(data)
         ds.set_from_json('data', json)
         assert np.array_equal(ds.data["foo"], data["foo"])
diff --git a/bokeh/server/tests/test_server.py b/bokeh/server/tests/test_server.py
--- a/bokeh/server/tests/test_server.py
+++ b/bokeh/server/tests/test_server.py
@@ -4,7 +4,7 @@
 import pytest
 import logging
 import re
-
+import sys
 import mock
 
 from tornado import gen
@@ -137,6 +137,8 @@ def on_periodic_session(self):
         self.hooks.append("periodic_session")
         self.session_periodic_remover()
 
+@pytest.mark.skipif(sys.platform == "win32",
+                    reason="Lifecycle hooks order different on Windows (TODO open issue)")
 def test__lifecycle_hooks():
     application = Application()
     handler = HookTestHandler()
@@ -577,6 +579,8 @@ def test__no_generate_session_doc():
         sessions = server.get_sessions('/')
         assert 0 == len(sessions)
 
+@pytest.mark.skipif(sys.platform == "win32",
+                    reason="multiple processes not supported on Windows")
 def test__server_multiple_processes():
 
     # Can't use an ioloop in this test
diff --git a/bokeh/util/tests/test_browser.py b/bokeh/util/tests/test_browser.py
--- a/bokeh/util/tests/test_browser.py
+++ b/bokeh/util/tests/test_browser.py
@@ -1,4 +1,5 @@
 import os
+import sys
 import webbrowser
 
 import pytest
@@ -70,7 +71,10 @@ def test_view_args():
 
     # test non-http locations treated as local files
     bub.view("/foo/bar", browser="none")
-    assert _open_args == (('file:///foo/bar',), {'autoraise': True, 'new': 0})
+    if sys.platform == "win32":
+        assert _open_args == (('file://' + os.path.splitdrive(os.getcwd())[0] + '\\foo\\bar',), {'autoraise': True, 'new': 0})
+    else:
+        assert _open_args == (('file:///foo/bar',), {'autoraise': True, 'new': 0})
 
     # test autoraise passed to open
     bub.view("http://foo", browser="none", autoraise=False)