##// END OF EJS Templates
Merge pull request #5203 from minrk/test-log-warn...
Thomas Kluyver -
r15511:7a1f3e2c merge
parent child Browse files
Show More
@@ -1,18 +1,18 b''
1 # http://travis-ci.org/#!/ipython/ipython
1 # http://travis-ci.org/#!/ipython/ipython
2 language: python
2 language: python
3 python:
3 python:
4 - 2.7
4 - 2.7
5 - 3.3
5 - 3.3
6 before_install:
6 before_install:
7 # workaround for https://github.com/travis-ci/travis-cookbooks/issues/155
7 # workaround for https://github.com/travis-ci/travis-cookbooks/issues/155
8 - sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm
8 - sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm
9 - easy_install -q pyzmq
9 - easy_install -q pyzmq
10 - pip install jinja2 sphinx pygments tornado requests
10 - pip install jinja2 sphinx pygments tornado requests
11 # Pierre Carrier's PPA for PhantomJS and CasperJS
11 # Pierre Carrier's PPA for PhantomJS and CasperJS
12 - sudo add-apt-repository -y ppa:pcarrier/ppa
12 - sudo add-apt-repository -y ppa:pcarrier/ppa
13 - sudo apt-get update
13 - sudo apt-get update
14 - sudo apt-get install pandoc casperjs
14 - sudo apt-get install pandoc casperjs nodejs
15 install:
15 install:
16 - python setup.py install -q
16 - python setup.py install -q
17 script:
17 script:
18 - cd /tmp && iptest
18 - cd /tmp && iptest
@@ -1,73 +1,74 b''
1 """Test NotebookApp"""
1 """Test NotebookApp"""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import logging
14 import os
15 import os
15 from tempfile import NamedTemporaryFile
16 from tempfile import NamedTemporaryFile
16
17
17 import nose.tools as nt
18 import nose.tools as nt
18
19
19 from IPython.utils.tempdir import TemporaryDirectory
20 from IPython.utils.tempdir import TemporaryDirectory
20 from IPython.utils.traitlets import TraitError
21 from IPython.utils.traitlets import TraitError
21 import IPython.testing.tools as tt
22 import IPython.testing.tools as tt
22 from IPython.html import notebookapp
23 from IPython.html import notebookapp
23 NotebookApp = notebookapp.NotebookApp
24 NotebookApp = notebookapp.NotebookApp
24
25
25 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
26 # Test functions
27 # Test functions
27 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
28
29
29 def test_help_output():
30 def test_help_output():
30 """ipython notebook --help-all works"""
31 """ipython notebook --help-all works"""
31 tt.help_all_output_test('notebook')
32 tt.help_all_output_test('notebook')
32
33
33 def test_server_info_file():
34 def test_server_info_file():
34 nbapp = NotebookApp(profile='nbserver_file_test')
35 nbapp = NotebookApp(profile='nbserver_file_test', log=logging.getLogger())
35 def get_servers():
36 def get_servers():
36 return list(notebookapp.list_running_servers(profile='nbserver_file_test'))
37 return list(notebookapp.list_running_servers(profile='nbserver_file_test'))
37 nbapp.initialize(argv=[])
38 nbapp.initialize(argv=[])
38 nbapp.write_server_info_file()
39 nbapp.write_server_info_file()
39 servers = get_servers()
40 servers = get_servers()
40 nt.assert_equal(len(servers), 1)
41 nt.assert_equal(len(servers), 1)
41 nt.assert_equal(servers[0]['port'], nbapp.port)
42 nt.assert_equal(servers[0]['port'], nbapp.port)
42 nt.assert_equal(servers[0]['url'], nbapp.connection_url)
43 nt.assert_equal(servers[0]['url'], nbapp.connection_url)
43 nbapp.remove_server_info_file()
44 nbapp.remove_server_info_file()
44 nt.assert_equal(get_servers(), [])
45 nt.assert_equal(get_servers(), [])
45
46
46 # The ENOENT error should be silenced.
47 # The ENOENT error should be silenced.
47 nbapp.remove_server_info_file()
48 nbapp.remove_server_info_file()
48
49
49 def test_nb_dir():
50 def test_nb_dir():
50 with TemporaryDirectory() as td:
51 with TemporaryDirectory() as td:
51 app = NotebookApp(notebook_dir=td)
52 app = NotebookApp(notebook_dir=td)
52 nt.assert_equal(app.notebook_dir, td)
53 nt.assert_equal(app.notebook_dir, td)
53
54
54 def test_no_create_nb_dir():
55 def test_no_create_nb_dir():
55 with TemporaryDirectory() as td:
56 with TemporaryDirectory() as td:
56 nbdir = os.path.join(td, 'notebooks')
57 nbdir = os.path.join(td, 'notebooks')
57 app = NotebookApp()
58 app = NotebookApp()
58 with nt.assert_raises(TraitError):
59 with nt.assert_raises(TraitError):
59 app.notebook_dir = nbdir
60 app.notebook_dir = nbdir
60
61
61 def test_missing_nb_dir():
62 def test_missing_nb_dir():
62 with TemporaryDirectory() as td:
63 with TemporaryDirectory() as td:
63 nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing')
64 nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing')
64 app = NotebookApp()
65 app = NotebookApp()
65 with nt.assert_raises(TraitError):
66 with nt.assert_raises(TraitError):
66 app.notebook_dir = nbdir
67 app.notebook_dir = nbdir
67
68
68 def test_invalid_nb_dir():
69 def test_invalid_nb_dir():
69 with NamedTemporaryFile() as tf:
70 with NamedTemporaryFile() as tf:
70 app = NotebookApp()
71 app = NotebookApp()
71 with nt.assert_raises(TraitError):
72 with nt.assert_raises(TraitError):
72 app.notebook_dir = tf
73 app.notebook_dir = tf
73
74
@@ -1,54 +1,54 b''
1 """Base TestCase class for testing Exporters"""
1 """Base TestCase class for testing Exporters"""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2013, the IPython Development Team.
4 # Copyright (c) 2013, the IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 import os
15 import os
16
16
17 from IPython.testing.decorators import onlyif_cmds_exist
17 from IPython.testing.decorators import onlyif_any_cmd_exists
18
18
19 from ...tests.base import TestsBase
19 from ...tests.base import TestsBase
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Class
22 # Class
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 all_raw_mimetypes = {
25 all_raw_mimetypes = {
26 'text/x-python',
26 'text/x-python',
27 'text/markdown',
27 'text/markdown',
28 'text/html',
28 'text/html',
29 'text/restructuredtext',
29 'text/restructuredtext',
30 'text/latex',
30 'text/latex',
31 }
31 }
32
32
33 class ExportersTestsBase(TestsBase):
33 class ExportersTestsBase(TestsBase):
34 """Contains base test functions for exporters"""
34 """Contains base test functions for exporters"""
35
35
36 exporter_class = None
36 exporter_class = None
37 should_include_raw = None
37 should_include_raw = None
38
38
39 def _get_notebook(self, nb_name='notebook2.ipynb'):
39 def _get_notebook(self, nb_name='notebook2.ipynb'):
40 return os.path.join(self._get_files_path(), nb_name)
40 return os.path.join(self._get_files_path(), nb_name)
41
41
42 @onlyif_cmds_exist('pandoc')
42 @onlyif_any_cmd_exists('nodejs', 'node', 'pandoc')
43 def test_raw_cell_inclusion(self):
43 def test_raw_cell_inclusion(self):
44 """test raw cell inclusion based on raw_mimetype metadata"""
44 """test raw cell inclusion based on raw_mimetype metadata"""
45 if self.should_include_raw is None:
45 if self.should_include_raw is None:
46 return
46 return
47 exporter = self.exporter_class()
47 exporter = self.exporter_class()
48 (output, resources) = exporter.from_filename(self._get_notebook('rawtest.ipynb'))
48 (output, resources) = exporter.from_filename(self._get_notebook('rawtest.ipynb'))
49 for inc in self.should_include_raw:
49 for inc in self.should_include_raw:
50 self.assertIn('raw %s' % inc, output, "should include %s" % inc)
50 self.assertIn('raw %s' % inc, output, "should include %s" % inc)
51 self.assertIn('no raw_mimetype metadata', output)
51 self.assertIn('no raw_mimetype metadata', output)
52 for exc in all_raw_mimetypes.difference(self.should_include_raw):
52 for exc in all_raw_mimetypes.difference(self.should_include_raw):
53 self.assertNotIn('raw %s' % exc, output, "should exclude %s" % exc)
53 self.assertNotIn('raw %s' % exc, output, "should exclude %s" % exc)
54 self.assertNotIn('never be included', output)
54 self.assertNotIn('never be included', output)
@@ -1,61 +1,61 b''
1 """Tests for HTMLExporter"""
1 """Tests for HTMLExporter"""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2013, the IPython Development Team.
4 # Copyright (c) 2013, the IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 from .base import ExportersTestsBase
15 from .base import ExportersTestsBase
16 from ..html import HTMLExporter
16 from ..html import HTMLExporter
17 from IPython.testing.decorators import onlyif_cmds_exist
17 from IPython.testing.decorators import onlyif_any_cmd_exists
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Class
20 # Class
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 class TestHTMLExporter(ExportersTestsBase):
23 class TestHTMLExporter(ExportersTestsBase):
24 """Tests for HTMLExporter"""
24 """Tests for HTMLExporter"""
25
25
26 exporter_class = HTMLExporter
26 exporter_class = HTMLExporter
27 should_include_raw = ['html']
27 should_include_raw = ['html']
28
28
29 def test_constructor(self):
29 def test_constructor(self):
30 """
30 """
31 Can a HTMLExporter be constructed?
31 Can a HTMLExporter be constructed?
32 """
32 """
33 HTMLExporter()
33 HTMLExporter()
34
34
35
35
36 @onlyif_cmds_exist('pandoc')
36 @onlyif_any_cmd_exists('nodejs', 'node', 'pandoc')
37 def test_export(self):
37 def test_export(self):
38 """
38 """
39 Can a HTMLExporter export something?
39 Can a HTMLExporter export something?
40 """
40 """
41 (output, resources) = HTMLExporter().from_filename(self._get_notebook())
41 (output, resources) = HTMLExporter().from_filename(self._get_notebook())
42 assert len(output) > 0
42 assert len(output) > 0
43
43
44
44
45 @onlyif_cmds_exist('pandoc')
45 @onlyif_any_cmd_exists('nodejs', 'node', 'pandoc')
46 def test_export_basic(self):
46 def test_export_basic(self):
47 """
47 """
48 Can a HTMLExporter export using the 'basic' template?
48 Can a HTMLExporter export using the 'basic' template?
49 """
49 """
50 (output, resources) = HTMLExporter(template_file='basic').from_filename(self._get_notebook())
50 (output, resources) = HTMLExporter(template_file='basic').from_filename(self._get_notebook())
51 assert len(output) > 0
51 assert len(output) > 0
52
52
53
53
54 @onlyif_cmds_exist('pandoc')
54 @onlyif_any_cmd_exists('nodejs', 'node', 'pandoc')
55 def test_export_full(self):
55 def test_export_full(self):
56 """
56 """
57 Can a HTMLExporter export using the 'full' template?
57 Can a HTMLExporter export using the 'full' template?
58 """
58 """
59 (output, resources) = HTMLExporter(template_file='full').from_filename(self._get_notebook())
59 (output, resources) = HTMLExporter(template_file='full').from_filename(self._get_notebook())
60 assert len(output) > 0
60 assert len(output) > 0
61
61
@@ -1,51 +1,51 b''
1 """Tests for SlidesExporter"""
1 """Tests for SlidesExporter"""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2013, the IPython Development Team.
4 # Copyright (c) 2013, the IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 from .base import ExportersTestsBase
15 from .base import ExportersTestsBase
16 from ..slides import SlidesExporter
16 from ..slides import SlidesExporter
17 from IPython.testing.decorators import onlyif_cmds_exist
17 from IPython.testing.decorators import onlyif_any_cmd_exists
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Class
20 # Class
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 class TestSlidesExporter(ExportersTestsBase):
23 class TestSlidesExporter(ExportersTestsBase):
24 """Tests for SlidesExporter"""
24 """Tests for SlidesExporter"""
25
25
26 exporter_class = SlidesExporter
26 exporter_class = SlidesExporter
27 should_include_raw = ['html']
27 should_include_raw = ['html']
28
28
29 def test_constructor(self):
29 def test_constructor(self):
30 """
30 """
31 Can a SlidesExporter be constructed?
31 Can a SlidesExporter be constructed?
32 """
32 """
33 SlidesExporter()
33 SlidesExporter()
34
34
35
35
36 @onlyif_cmds_exist('pandoc')
36 @onlyif_any_cmd_exists('nodejs', 'node', 'pandoc')
37 def test_export(self):
37 def test_export(self):
38 """
38 """
39 Can a SlidesExporter export something?
39 Can a SlidesExporter export something?
40 """
40 """
41 (output, resources) = SlidesExporter().from_filename(self._get_notebook())
41 (output, resources) = SlidesExporter().from_filename(self._get_notebook())
42 assert len(output) > 0
42 assert len(output) > 0
43
43
44
44
45 @onlyif_cmds_exist('pandoc')
45 @onlyif_any_cmd_exists('nodejs', 'node', 'pandoc')
46 def test_export_reveal(self):
46 def test_export_reveal(self):
47 """
47 """
48 Can a SlidesExporter export using the 'reveal' template?
48 Can a SlidesExporter export using the 'reveal' template?
49 """
49 """
50 (output, resources) = SlidesExporter(template_file='slides_reveal').from_filename(self._get_notebook())
50 (output, resources) = SlidesExporter(template_file='slides_reveal').from_filename(self._get_notebook())
51 assert len(output) > 0
51 assert len(output) > 0
@@ -1,68 +1,69 b''
1 """
1 """
2 Module with tests for the PDF post-processor
2 Module with tests for the PDF post-processor
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import logging
17 import os
18 import os
18
19
19 from IPython.testing import decorators as dec
20 from IPython.testing import decorators as dec
20
21
21 from ...tests.base import TestsBase
22 from ...tests.base import TestsBase
22 from ..pdf import PDFPostProcessor
23 from ..pdf import PDFPostProcessor
23
24
24
25
25 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
26 # Constants
27 # Constants
27 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
28
29
29 HELLO_WORLD = r"""% hello.tex - Our first LaTeX example!
30 HELLO_WORLD = r"""% hello.tex - Our first LaTeX example!
30 \documentclass{article}
31 \documentclass{article}
31 \begin{document}
32 \begin{document}
32 Hello World!
33 Hello World!
33 \end{document}"""
34 \end{document}"""
34
35
35
36
36 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
37 # Class
38 # Class
38 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
39
40
40 class TestPDF(TestsBase):
41 class TestPDF(TestsBase):
41 """Contains test functions for pdf.py"""
42 """Contains test functions for pdf.py"""
42
43
43
44
44 def test_constructor(self):
45 def test_constructor(self):
45 """Can a PDFPostProcessor be constructed?"""
46 """Can a PDFPostProcessor be constructed?"""
46 PDFPostProcessor()
47 PDFPostProcessor()
47
48
48
49
49 @dec.onlyif_cmds_exist('pdflatex')
50 @dec.onlyif_cmds_exist('pdflatex')
50 def test_pdf(self):
51 def test_pdf(self):
51 """Can a PDF be made using the PDFPostProcessor?"""
52 """Can a PDF be made using the PDFPostProcessor?"""
52
53
53 # Work in a temporary directory with hello world latex in it.
54 # Work in a temporary directory with hello world latex in it.
54 with self.create_temp_cwd():
55 with self.create_temp_cwd():
55 with open('a.tex', 'w') as f:
56 with open('a.tex', 'w') as f:
56 f.write(HELLO_WORLD)
57 f.write(HELLO_WORLD)
57
58
58 # Construct post-processor
59 # Construct post-processor
59 processor = PDFPostProcessor()
60 processor = PDFPostProcessor(log=logging.getLogger())
60 processor.verbose = False
61 processor.verbose = False
61 processor('a.tex')
62 processor('a.tex')
62
63
63 # Check that the PDF was created.
64 # Check that the PDF was created.
64 assert os.path.isfile('a.pdf')
65 assert os.path.isfile('a.pdf')
65
66
66 # Make sure that temp files are cleaned up
67 # Make sure that temp files are cleaned up
67 for ext in processor.temp_file_exts:
68 for ext in processor.temp_file_exts:
68 assert not os.path.isfile('a'+ext)
69 assert not os.path.isfile('a'+ext)
@@ -1,209 +1,202 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Test NbConvertApp"""
2 """Test NbConvertApp"""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2013 The IPython Development Team
5 # Copyright (C) 2013 The IPython Development Team
6 #
6 #
7 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 import os
15 import os
16 import glob
16 import glob
17 import sys
17 import sys
18
18
19 from .base import TestsBase
19 from .base import TestsBase
20
20
21 import IPython.testing.tools as tt
21 import IPython.testing.tools as tt
22 from IPython.testing import decorators as dec
22 from IPython.testing import decorators as dec
23
23
24
25 #-----------------------------------------------------------------------------
26 # Constants
27 #-----------------------------------------------------------------------------
28
29
30 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
31 # Classes and functions
25 # Classes and functions
32 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
33
27
34 class TestNbConvertApp(TestsBase):
28 class TestNbConvertApp(TestsBase):
35 """Collection of NbConvertApp tests"""
29 """Collection of NbConvertApp tests"""
36
30
37
31
38 def test_notebook_help(self):
32 def test_notebook_help(self):
39 """Will help show if no notebooks are specified?"""
33 """Will help show if no notebooks are specified?"""
40 with self.create_temp_cwd():
34 with self.create_temp_cwd():
41 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
35 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
42 self.assertIn("see '--help-all'", out)
36 self.assertIn("see '--help-all'", out)
43
37
44 def test_help_output(self):
38 def test_help_output(self):
45 """ipython nbconvert --help-all works"""
39 """ipython nbconvert --help-all works"""
46 tt.help_all_output_test('nbconvert')
40 tt.help_all_output_test('nbconvert')
47
41
48 def test_glob(self):
42 def test_glob(self):
49 """
43 """
50 Do search patterns work for notebook names?
44 Do search patterns work for notebook names?
51 """
45 """
52 with self.create_temp_cwd(['notebook*.ipynb']):
46 with self.create_temp_cwd(['notebook*.ipynb']):
53 self.call('nbconvert --to python *.ipynb --log-level 0')
47 self.call('nbconvert --to python *.ipynb --log-level 0')
54 assert os.path.isfile('notebook1.py')
48 assert os.path.isfile('notebook1.py')
55 assert os.path.isfile('notebook2.py')
49 assert os.path.isfile('notebook2.py')
56
50
57
51
58 def test_glob_subdir(self):
52 def test_glob_subdir(self):
59 """
53 """
60 Do search patterns work for subdirectory notebook names?
54 Do search patterns work for subdirectory notebook names?
61 """
55 """
62 with self.create_temp_cwd():
56 with self.create_temp_cwd():
63 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
57 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
64 self.call('nbconvert --to python --log-level 0 ' +
58 self.call('nbconvert --to python --log-level 0 ' +
65 os.path.join('subdir', '*.ipynb'))
59 os.path.join('subdir', '*.ipynb'))
66 assert os.path.isfile('notebook1.py')
60 assert os.path.isfile('notebook1.py')
67 assert os.path.isfile('notebook2.py')
61 assert os.path.isfile('notebook2.py')
68
62
69
63
70 def test_explicit(self):
64 def test_explicit(self):
71 """
65 """
72 Do explicit notebook names work?
66 Do explicit notebook names work?
73 """
67 """
74 with self.create_temp_cwd(['notebook*.ipynb']):
68 with self.create_temp_cwd(['notebook*.ipynb']):
75 self.call('nbconvert --log-level 0 --to python notebook2')
69 self.call('nbconvert --log-level 0 --to python notebook2')
76 assert not os.path.isfile('notebook1.py')
70 assert not os.path.isfile('notebook1.py')
77 assert os.path.isfile('notebook2.py')
71 assert os.path.isfile('notebook2.py')
78
72
79
73
80 @dec.onlyif_cmds_exist('pdflatex')
74 @dec.onlyif_cmds_exist('pdflatex')
81 @dec.onlyif_cmds_exist('pandoc')
75 @dec.onlyif_cmds_exist('pandoc')
82 def test_filename_spaces(self):
76 def test_filename_spaces(self):
83 """
77 """
84 Generate PDFs with graphics if notebooks have spaces in the name?
78 Generate PDFs with graphics if notebooks have spaces in the name?
85 """
79 """
86 with self.create_temp_cwd(['notebook2.ipynb']):
80 with self.create_temp_cwd(['notebook2.ipynb']):
87 os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
81 os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
88 o,e = self.call('nbconvert --log-level 0 --to latex '
82 self.call('nbconvert --log-level 0 --to latex '
89 '"notebook with spaces" --post PDF '
83 '"notebook with spaces" --post PDF '
90 '--PDFPostProcessor.verbose=True')
84 '--PDFPostProcessor.verbose=True')
91 assert os.path.isfile('notebook with spaces.tex')
85 assert os.path.isfile('notebook with spaces.tex')
92 assert os.path.isdir('notebook with spaces_files')
86 assert os.path.isdir('notebook with spaces_files')
93 assert os.path.isfile('notebook with spaces.pdf')
87 assert os.path.isfile('notebook with spaces.pdf')
94
88
95 @dec.onlyif_cmds_exist('pdflatex')
89 @dec.onlyif_cmds_exist('pdflatex')
96 @dec.onlyif_cmds_exist('pandoc')
90 @dec.onlyif_cmds_exist('pandoc')
97 def test_post_processor(self):
91 def test_post_processor(self):
98 """
92 """
99 Do post processors work?
93 Do post processors work?
100 """
94 """
101 with self.create_temp_cwd(['notebook1.ipynb']):
95 with self.create_temp_cwd(['notebook1.ipynb']):
102 self.call('nbconvert --log-level 0 --to latex notebook1 '
96 self.call('nbconvert --log-level 0 --to latex notebook1 '
103 '--post PDF --PDFPostProcessor.verbose=True')
97 '--post PDF --PDFPostProcessor.verbose=True')
104 assert os.path.isfile('notebook1.tex')
98 assert os.path.isfile('notebook1.tex')
105 assert os.path.isfile('notebook1.pdf')
99 assert os.path.isfile('notebook1.pdf')
106
100
107 @dec.onlyif_cmds_exist('pandoc')
101 @dec.onlyif_cmds_exist('pandoc')
108 def test_spurious_cr(self):
102 def test_spurious_cr(self):
109 """Check for extra CR characters"""
103 """Check for extra CR characters"""
110 with self.create_temp_cwd(['notebook2.ipynb']):
104 with self.create_temp_cwd(['notebook2.ipynb']):
111 self.call('nbconvert --log-level 0 --to latex notebook2')
105 self.call('nbconvert --log-level 0 --to latex notebook2')
112 assert os.path.isfile('notebook2.tex')
106 assert os.path.isfile('notebook2.tex')
113 with open('notebook2.tex') as f:
107 with open('notebook2.tex') as f:
114 tex = f.read()
108 tex = f.read()
115 self.call('nbconvert --log-level 0 --to html notebook2')
109 self.call('nbconvert --log-level 0 --to html notebook2')
116 assert os.path.isfile('notebook2.html')
110 assert os.path.isfile('notebook2.html')
117 with open('notebook2.html') as f:
111 with open('notebook2.html') as f:
118 html = f.read()
112 html = f.read()
119 self.assertEqual(tex.count('\r'), tex.count('\r\n'))
113 self.assertEqual(tex.count('\r'), tex.count('\r\n'))
120 self.assertEqual(html.count('\r'), html.count('\r\n'))
114 self.assertEqual(html.count('\r'), html.count('\r\n'))
121
115
122 @dec.onlyif_cmds_exist('pandoc')
116 @dec.onlyif_cmds_exist('pandoc')
123 def test_png_base64_html_ok(self):
117 def test_png_base64_html_ok(self):
124 """Is embedded png data well formed in HTML?"""
118 """Is embedded png data well formed in HTML?"""
125 with self.create_temp_cwd(['notebook2.ipynb']):
119 with self.create_temp_cwd(['notebook2.ipynb']):
126 self.call('nbconvert --log-level 0 --to HTML '
120 self.call('nbconvert --log-level 0 --to HTML '
127 'notebook2.ipynb --template full')
121 'notebook2.ipynb --template full')
128 assert os.path.isfile('notebook2.html')
122 assert os.path.isfile('notebook2.html')
129 with open('notebook2.html') as f:
123 with open('notebook2.html') as f:
130 assert "data:image/png;base64,b'" not in f.read()
124 assert "data:image/png;base64,b'" not in f.read()
131
125
132 @dec.onlyif_cmds_exist('pandoc')
126 @dec.onlyif_cmds_exist('pandoc')
133 def test_template(self):
127 def test_template(self):
134 """
128 """
135 Do export templates work?
129 Do export templates work?
136 """
130 """
137 with self.create_temp_cwd(['notebook2.ipynb']):
131 with self.create_temp_cwd(['notebook2.ipynb']):
138 self.call('nbconvert --log-level 0 --to slides '
132 self.call('nbconvert --log-level 0 --to slides '
139 'notebook2.ipynb')
133 'notebook2.ipynb')
140 assert os.path.isfile('notebook2.slides.html')
134 assert os.path.isfile('notebook2.slides.html')
141 with open('notebook2.slides.html') as f:
135 with open('notebook2.slides.html') as f:
142 assert '/reveal.css' in f.read()
136 assert '/reveal.css' in f.read()
143
137
144
138
145 def test_glob_explicit(self):
139 def test_glob_explicit(self):
146 """
140 """
147 Can a search pattern be used along with matching explicit notebook names?
141 Can a search pattern be used along with matching explicit notebook names?
148 """
142 """
149 with self.create_temp_cwd(['notebook*.ipynb']):
143 with self.create_temp_cwd(['notebook*.ipynb']):
150 self.call('nbconvert --log-level 0 --to python '
144 self.call('nbconvert --log-level 0 --to python '
151 '*.ipynb notebook1.ipynb notebook2.ipynb')
145 '*.ipynb notebook1.ipynb notebook2.ipynb')
152 assert os.path.isfile('notebook1.py')
146 assert os.path.isfile('notebook1.py')
153 assert os.path.isfile('notebook2.py')
147 assert os.path.isfile('notebook2.py')
154
148
155
149
156 def test_explicit_glob(self):
150 def test_explicit_glob(self):
157 """
151 """
158 Can explicit notebook names be used and then a matching search pattern?
152 Can explicit notebook names be used and then a matching search pattern?
159 """
153 """
160 with self.create_temp_cwd(['notebook*.ipynb']):
154 with self.create_temp_cwd(['notebook*.ipynb']):
161 self.call('nbconvert --log-level 0 --to=python '
155 self.call('nbconvert --log-level 0 --to=python '
162 'notebook1.ipynb notebook2.ipynb *.ipynb')
156 'notebook1.ipynb notebook2.ipynb *.ipynb')
163 assert os.path.isfile('notebook1.py')
157 assert os.path.isfile('notebook1.py')
164 assert os.path.isfile('notebook2.py')
158 assert os.path.isfile('notebook2.py')
165
159
166
160
167 def test_default_config(self):
161 def test_default_config(self):
168 """
162 """
169 Does the default config work?
163 Does the default config work?
170 """
164 """
171 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
165 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
172 self.call('nbconvert --log-level 0')
166 self.call('nbconvert --log-level 0')
173 assert os.path.isfile('notebook1.py')
167 assert os.path.isfile('notebook1.py')
174 assert not os.path.isfile('notebook2.py')
168 assert not os.path.isfile('notebook2.py')
175
169
176
170
177 def test_override_config(self):
171 def test_override_config(self):
178 """
172 """
179 Can the default config be overriden?
173 Can the default config be overriden?
180 """
174 """
181 with self.create_temp_cwd(['notebook*.ipynb',
175 with self.create_temp_cwd(['notebook*.ipynb',
182 'ipython_nbconvert_config.py',
176 'ipython_nbconvert_config.py',
183 'override.py']):
177 'override.py']):
184 self.call('nbconvert --log-level 0 --config="override.py"')
178 self.call('nbconvert --log-level 0 --config="override.py"')
185 assert not os.path.isfile('notebook1.py')
179 assert not os.path.isfile('notebook1.py')
186 assert os.path.isfile('notebook2.py')
180 assert os.path.isfile('notebook2.py')
187
181
188 def test_accents_in_filename(self):
182 def test_accents_in_filename(self):
189 """
183 """
190 Can notebook names include accents?
184 Can notebook names include accents?
191 """
185 """
192 with self.create_temp_cwd():
186 with self.create_temp_cwd():
193 self.create_empty_notebook(u'nb1_anΓ‘lisis.ipynb')
187 self.create_empty_notebook(u'nb1_anΓ‘lisis.ipynb')
194 self.call('nbconvert --log-level 0 --to python nb1_*')
188 self.call('nbconvert --log-level 0 --to python nb1_*')
195 assert os.path.isfile(u'nb1_anΓ‘lisis.py')
189 assert os.path.isfile(u'nb1_anΓ‘lisis.py')
196
190
197 @dec.onlyif_cmds_exist('pdflatex')
191 @dec.onlyif_cmds_exist('pdflatex', 'pandoc')
198 @dec.onlyif_cmds_exist('pandoc')
192 def test_filename_accent_pdf(self):
199 def test_filename_accent(self):
200 """
193 """
201 Generate PDFs if notebooks have an accent in their name?
194 Generate PDFs if notebooks have an accent in their name?
202 """
195 """
203 with self.create_temp_cwd():
196 with self.create_temp_cwd():
204 self.create_empty_notebook(u'nb1_anΓ‘lisis.ipynb')
197 self.create_empty_notebook(u'nb1_anΓ‘lisis.ipynb')
205 o,e = self.call('nbconvert --log-level 0 --to latex '
198 self.call('nbconvert --log-level 0 --to latex '
206 '"nb1_*" --post PDF '
199 '"nb1_*" --post PDF '
207 '--PDFPostProcessor.verbose=True')
200 '--PDFPostProcessor.verbose=True')
208 assert os.path.isfile(u'nb1_anΓ‘lisis.tex')
201 assert os.path.isfile(u'nb1_anΓ‘lisis.tex')
209 assert os.path.isfile(u'nb1_anΓ‘lisis.pdf')
202 assert os.path.isfile(u'nb1_anΓ‘lisis.pdf')
@@ -1,62 +1,70 b''
1 """Test Pandoc module"""
1 """Test Pandoc module"""
2 #-----------------------------------------------------------------------------
2 #-----------------------------------------------------------------------------
3 # Copyright (C) 2014 The IPython Development Team
3 # Copyright (C) 2014 The IPython Development Team
4 #
4 #
5 # Distributed under the terms of the BSD License. The full license is in
5 # Distributed under the terms of the BSD License. The full license is in
6 # the file COPYING, distributed as part of this software.
6 # the file COPYING, distributed as part of this software.
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Imports
10 # Imports
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 import os
12 import os
13 import warnings
13
14
14 from IPython.testing import decorators as dec
15 from IPython.testing import decorators as dec
15
16
16 from IPython.nbconvert.tests.base import TestsBase
17 from IPython.nbconvert.tests.base import TestsBase
17 from .. import pandoc
18 from .. import pandoc
18
19
19 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
20 # Classes and functions
21 # Classes and functions
21 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
22 class TestPandoc(TestsBase):
23 class TestPandoc(TestsBase):
23 """Collection of Pandoc tests"""
24 """Collection of Pandoc tests"""
24
25
25 def __init__(self, *args, **kwargs):
26 def __init__(self, *args, **kwargs):
26 super(TestPandoc, self).__init__(*args, **kwargs)
27 super(TestPandoc, self).__init__(*args, **kwargs)
27 self.original_env = os.environ.copy()
28 self.original_env = os.environ.copy()
28
29
29 @dec.onlyif_cmds_exist('pandoc')
30 @dec.onlyif_cmds_exist('pandoc')
30 def test_pandoc_available(self):
31 def test_pandoc_available(self):
31 """ Test behaviour that pandoc functions raise PandocMissing as documented """
32 """ Test behaviour that pandoc functions raise PandocMissing as documented """
32 pandoc.clean_cache()
33 pandoc.clean_cache()
33
34
34 os.environ["PATH"] = ""
35 os.environ["PATH"] = ""
35 assert pandoc_function_raised_missing(pandoc.get_pandoc_version) == True
36 with self.assertRaises(pandoc.PandocMissing):
36 assert pandoc_function_raised_missing(pandoc.check_pandoc_version) == True
37 pandoc.get_pandoc_version()
37 assert pandoc_function_raised_missing(pandoc.pandoc, "", "markdown", "html") == True
38 with self.assertRaises(pandoc.PandocMissing):
39 pandoc.check_pandoc_version()
40 with self.assertRaises(pandoc.PandocMissing):
41 pandoc.pandoc("", "markdown", "html")
38
42
39 # original_env["PATH"] should contain pandoc
43 # original_env["PATH"] should contain pandoc
40 os.environ["PATH"] = self.original_env["PATH"]
44 os.environ["PATH"] = self.original_env["PATH"]
41 assert pandoc_function_raised_missing(pandoc.get_pandoc_version) == False
45 with warnings.catch_warnings(record=True) as w:
42 assert pandoc_function_raised_missing(pandoc.check_pandoc_version) == False
46 pandoc.get_pandoc_version()
43 assert pandoc_function_raised_missing(pandoc.pandoc, "", "markdown", "html") == False
47 pandoc.check_pandoc_version()
48 pandoc.pandoc("", "markdown", "html")
49 self.assertEqual(w, [])
44
50
45 @dec.onlyif_cmds_exist('pandoc')
51 @dec.onlyif_cmds_exist('pandoc')
46 def test_minimal_version(self):
52 def test_minimal_version(self):
47 original_minversion = pandoc._minimal_version
53 original_minversion = pandoc._minimal_version
48
54
49 pandoc._minimal_version = "120.0"
55 pandoc._minimal_version = "120.0"
50 assert not pandoc.check_pandoc_version()
56 with warnings.catch_warnings(record=True) as w:
57 assert not pandoc.check_pandoc_version()
58 self.assertEqual(len(w), 1)
51
59
52 pandoc._minimal_version = pandoc.get_pandoc_version()
60 pandoc._minimal_version = pandoc.get_pandoc_version()
53 assert pandoc.check_pandoc_version()
61 assert pandoc.check_pandoc_version()
54
62
55
63
56 def pandoc_function_raised_missing(f, *args, **kwargs):
64 def pandoc_function_raised_missing(f, *args, **kwargs):
57 try:
65 try:
58 f(*args, **kwargs)
66 f(*args, **kwargs)
59 except pandoc.PandocMissing:
67 except pandoc.PandocMissing:
60 return True
68 return True
61 else:
69 else:
62 return False
70 return False
@@ -1,383 +1,400 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Decorators for labeling test objects.
2 """Decorators for labeling test objects.
3
3
4 Decorators that merely return a modified version of the original function
4 Decorators that merely return a modified version of the original function
5 object are straightforward. Decorators that return a new function object need
5 object are straightforward. Decorators that return a new function object need
6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
7 decorator, in order to preserve metadata such as function name, setup and
7 decorator, in order to preserve metadata such as function name, setup and
8 teardown functions and so on - see nose.tools for more information.
8 teardown functions and so on - see nose.tools for more information.
9
9
10 This module provides a set of useful decorators meant to be ready to use in
10 This module provides a set of useful decorators meant to be ready to use in
11 your own tests. See the bottom of the file for the ready-made ones, and if you
11 your own tests. See the bottom of the file for the ready-made ones, and if you
12 find yourself writing a new one that may be of generic use, add it here.
12 find yourself writing a new one that may be of generic use, add it here.
13
13
14 Included decorators:
14 Included decorators:
15
15
16
16
17 Lightweight testing that remains unittest-compatible.
17 Lightweight testing that remains unittest-compatible.
18
18
19 - An @as_unittest decorator can be used to tag any normal parameter-less
19 - An @as_unittest decorator can be used to tag any normal parameter-less
20 function as a unittest TestCase. Then, both nose and normal unittest will
20 function as a unittest TestCase. Then, both nose and normal unittest will
21 recognize it as such. This will make it easier to migrate away from Nose if
21 recognize it as such. This will make it easier to migrate away from Nose if
22 we ever need/want to while maintaining very lightweight tests.
22 we ever need/want to while maintaining very lightweight tests.
23
23
24 NOTE: This file contains IPython-specific decorators. Using the machinery in
24 NOTE: This file contains IPython-specific decorators. Using the machinery in
25 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
25 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
26 available, OR use equivalent code in IPython.external._decorators, which
26 available, OR use equivalent code in IPython.external._decorators, which
27 we've copied verbatim from numpy.
27 we've copied verbatim from numpy.
28
28
29 Authors
29 Authors
30 -------
30 -------
31
31
32 - Fernando Perez <Fernando.Perez@berkeley.edu>
32 - Fernando Perez <Fernando.Perez@berkeley.edu>
33 """
33 """
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Copyright (C) 2009-2011 The IPython Development Team
36 # Copyright (C) 2009-2011 The IPython Development Team
37 #
37 #
38 # Distributed under the terms of the BSD License. The full license is in
38 # Distributed under the terms of the BSD License. The full license is in
39 # the file COPYING, distributed as part of this software.
39 # the file COPYING, distributed as part of this software.
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Imports
43 # Imports
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 # Stdlib imports
46 # Stdlib imports
47 import sys
47 import sys
48 import os
48 import os
49 import tempfile
49 import tempfile
50 import unittest
50 import unittest
51
51
52 # Third-party imports
52 # Third-party imports
53
53
54 # This is Michele Simionato's decorator module, kept verbatim.
54 # This is Michele Simionato's decorator module, kept verbatim.
55 from IPython.external.decorator import decorator
55 from IPython.external.decorator import decorator
56
56
57 # Expose the unittest-driven decorators
57 # Expose the unittest-driven decorators
58 from .ipunittest import ipdoctest, ipdocstring
58 from .ipunittest import ipdoctest, ipdocstring
59
59
60 # Grab the numpy-specific decorators which we keep in a file that we
60 # Grab the numpy-specific decorators which we keep in a file that we
61 # occasionally update from upstream: decorators.py is a copy of
61 # occasionally update from upstream: decorators.py is a copy of
62 # numpy.testing.decorators, we expose all of it here.
62 # numpy.testing.decorators, we expose all of it here.
63 from IPython.external.decorators import *
63 from IPython.external.decorators import *
64
64
65 # For onlyif_cmd_exists decorator
65 # For onlyif_cmd_exists decorator
66 from IPython.utils.process import is_cmd_found
66 from IPython.utils.process import is_cmd_found
67 from IPython.utils.py3compat import string_types
67 from IPython.utils.py3compat import string_types
68
68
69 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
70 # Classes and functions
70 # Classes and functions
71 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
72
72
73 # Simple example of the basic idea
73 # Simple example of the basic idea
74 def as_unittest(func):
74 def as_unittest(func):
75 """Decorator to make a simple function into a normal test via unittest."""
75 """Decorator to make a simple function into a normal test via unittest."""
76 class Tester(unittest.TestCase):
76 class Tester(unittest.TestCase):
77 def test(self):
77 def test(self):
78 func()
78 func()
79
79
80 Tester.__name__ = func.__name__
80 Tester.__name__ = func.__name__
81
81
82 return Tester
82 return Tester
83
83
84 # Utility functions
84 # Utility functions
85
85
86 def apply_wrapper(wrapper,func):
86 def apply_wrapper(wrapper,func):
87 """Apply a wrapper to a function for decoration.
87 """Apply a wrapper to a function for decoration.
88
88
89 This mixes Michele Simionato's decorator tool with nose's make_decorator,
89 This mixes Michele Simionato's decorator tool with nose's make_decorator,
90 to apply a wrapper in a decorator so that all nose attributes, as well as
90 to apply a wrapper in a decorator so that all nose attributes, as well as
91 function signature and other properties, survive the decoration cleanly.
91 function signature and other properties, survive the decoration cleanly.
92 This will ensure that wrapped functions can still be well introspected via
92 This will ensure that wrapped functions can still be well introspected via
93 IPython, for example.
93 IPython, for example.
94 """
94 """
95 import nose.tools
95 import nose.tools
96
96
97 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
97 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
98
98
99
99
100 def make_label_dec(label,ds=None):
100 def make_label_dec(label,ds=None):
101 """Factory function to create a decorator that applies one or more labels.
101 """Factory function to create a decorator that applies one or more labels.
102
102
103 Parameters
103 Parameters
104 ----------
104 ----------
105 label : string or sequence
105 label : string or sequence
106 One or more labels that will be applied by the decorator to the functions
106 One or more labels that will be applied by the decorator to the functions
107 it decorates. Labels are attributes of the decorated function with their
107 it decorates. Labels are attributes of the decorated function with their
108 value set to True.
108 value set to True.
109
109
110 ds : string
110 ds : string
111 An optional docstring for the resulting decorator. If not given, a
111 An optional docstring for the resulting decorator. If not given, a
112 default docstring is auto-generated.
112 default docstring is auto-generated.
113
113
114 Returns
114 Returns
115 -------
115 -------
116 A decorator.
116 A decorator.
117
117
118 Examples
118 Examples
119 --------
119 --------
120
120
121 A simple labeling decorator:
121 A simple labeling decorator:
122
122
123 >>> slow = make_label_dec('slow')
123 >>> slow = make_label_dec('slow')
124 >>> slow.__doc__
124 >>> slow.__doc__
125 "Labels a test as 'slow'."
125 "Labels a test as 'slow'."
126
126
127 And one that uses multiple labels and a custom docstring:
127 And one that uses multiple labels and a custom docstring:
128
128
129 >>> rare = make_label_dec(['slow','hard'],
129 >>> rare = make_label_dec(['slow','hard'],
130 ... "Mix labels 'slow' and 'hard' for rare tests.")
130 ... "Mix labels 'slow' and 'hard' for rare tests.")
131 >>> rare.__doc__
131 >>> rare.__doc__
132 "Mix labels 'slow' and 'hard' for rare tests."
132 "Mix labels 'slow' and 'hard' for rare tests."
133
133
134 Now, let's test using this one:
134 Now, let's test using this one:
135 >>> @rare
135 >>> @rare
136 ... def f(): pass
136 ... def f(): pass
137 ...
137 ...
138 >>>
138 >>>
139 >>> f.slow
139 >>> f.slow
140 True
140 True
141 >>> f.hard
141 >>> f.hard
142 True
142 True
143 """
143 """
144
144
145 if isinstance(label, string_types):
145 if isinstance(label, string_types):
146 labels = [label]
146 labels = [label]
147 else:
147 else:
148 labels = label
148 labels = label
149
149
150 # Validate that the given label(s) are OK for use in setattr() by doing a
150 # Validate that the given label(s) are OK for use in setattr() by doing a
151 # dry run on a dummy function.
151 # dry run on a dummy function.
152 tmp = lambda : None
152 tmp = lambda : None
153 for label in labels:
153 for label in labels:
154 setattr(tmp,label,True)
154 setattr(tmp,label,True)
155
155
156 # This is the actual decorator we'll return
156 # This is the actual decorator we'll return
157 def decor(f):
157 def decor(f):
158 for label in labels:
158 for label in labels:
159 setattr(f,label,True)
159 setattr(f,label,True)
160 return f
160 return f
161
161
162 # Apply the user's docstring, or autogenerate a basic one
162 # Apply the user's docstring, or autogenerate a basic one
163 if ds is None:
163 if ds is None:
164 ds = "Labels a test as %r." % label
164 ds = "Labels a test as %r." % label
165 decor.__doc__ = ds
165 decor.__doc__ = ds
166
166
167 return decor
167 return decor
168
168
169
169
170 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
170 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
171 # preserve function metadata better and allows the skip condition to be a
171 # preserve function metadata better and allows the skip condition to be a
172 # callable.
172 # callable.
173 def skipif(skip_condition, msg=None):
173 def skipif(skip_condition, msg=None):
174 ''' Make function raise SkipTest exception if skip_condition is true
174 ''' Make function raise SkipTest exception if skip_condition is true
175
175
176 Parameters
176 Parameters
177 ----------
177 ----------
178
178
179 skip_condition : bool or callable
179 skip_condition : bool or callable
180 Flag to determine whether to skip test. If the condition is a
180 Flag to determine whether to skip test. If the condition is a
181 callable, it is used at runtime to dynamically make the decision. This
181 callable, it is used at runtime to dynamically make the decision. This
182 is useful for tests that may require costly imports, to delay the cost
182 is useful for tests that may require costly imports, to delay the cost
183 until the test suite is actually executed.
183 until the test suite is actually executed.
184 msg : string
184 msg : string
185 Message to give on raising a SkipTest exception.
185 Message to give on raising a SkipTest exception.
186
186
187 Returns
187 Returns
188 -------
188 -------
189 decorator : function
189 decorator : function
190 Decorator, which, when applied to a function, causes SkipTest
190 Decorator, which, when applied to a function, causes SkipTest
191 to be raised when the skip_condition was True, and the function
191 to be raised when the skip_condition was True, and the function
192 to be called normally otherwise.
192 to be called normally otherwise.
193
193
194 Notes
194 Notes
195 -----
195 -----
196 You will see from the code that we had to further decorate the
196 You will see from the code that we had to further decorate the
197 decorator with the nose.tools.make_decorator function in order to
197 decorator with the nose.tools.make_decorator function in order to
198 transmit function name, and various other metadata.
198 transmit function name, and various other metadata.
199 '''
199 '''
200
200
201 def skip_decorator(f):
201 def skip_decorator(f):
202 # Local import to avoid a hard nose dependency and only incur the
202 # Local import to avoid a hard nose dependency and only incur the
203 # import time overhead at actual test-time.
203 # import time overhead at actual test-time.
204 import nose
204 import nose
205
205
206 # Allow for both boolean or callable skip conditions.
206 # Allow for both boolean or callable skip conditions.
207 if callable(skip_condition):
207 if callable(skip_condition):
208 skip_val = skip_condition
208 skip_val = skip_condition
209 else:
209 else:
210 skip_val = lambda : skip_condition
210 skip_val = lambda : skip_condition
211
211
212 def get_msg(func,msg=None):
212 def get_msg(func,msg=None):
213 """Skip message with information about function being skipped."""
213 """Skip message with information about function being skipped."""
214 if msg is None: out = 'Test skipped due to test condition.'
214 if msg is None: out = 'Test skipped due to test condition.'
215 else: out = msg
215 else: out = msg
216 return "Skipping test: %s. %s" % (func.__name__,out)
216 return "Skipping test: %s. %s" % (func.__name__,out)
217
217
218 # We need to define *two* skippers because Python doesn't allow both
218 # We need to define *two* skippers because Python doesn't allow both
219 # return with value and yield inside the same function.
219 # return with value and yield inside the same function.
220 def skipper_func(*args, **kwargs):
220 def skipper_func(*args, **kwargs):
221 """Skipper for normal test functions."""
221 """Skipper for normal test functions."""
222 if skip_val():
222 if skip_val():
223 raise nose.SkipTest(get_msg(f,msg))
223 raise nose.SkipTest(get_msg(f,msg))
224 else:
224 else:
225 return f(*args, **kwargs)
225 return f(*args, **kwargs)
226
226
227 def skipper_gen(*args, **kwargs):
227 def skipper_gen(*args, **kwargs):
228 """Skipper for test generators."""
228 """Skipper for test generators."""
229 if skip_val():
229 if skip_val():
230 raise nose.SkipTest(get_msg(f,msg))
230 raise nose.SkipTest(get_msg(f,msg))
231 else:
231 else:
232 for x in f(*args, **kwargs):
232 for x in f(*args, **kwargs):
233 yield x
233 yield x
234
234
235 # Choose the right skipper to use when building the actual generator.
235 # Choose the right skipper to use when building the actual generator.
236 if nose.util.isgenerator(f):
236 if nose.util.isgenerator(f):
237 skipper = skipper_gen
237 skipper = skipper_gen
238 else:
238 else:
239 skipper = skipper_func
239 skipper = skipper_func
240
240
241 return nose.tools.make_decorator(f)(skipper)
241 return nose.tools.make_decorator(f)(skipper)
242
242
243 return skip_decorator
243 return skip_decorator
244
244
245 # A version with the condition set to true, common case just to attach a message
245 # A version with the condition set to true, common case just to attach a message
246 # to a skip decorator
246 # to a skip decorator
247 def skip(msg=None):
247 def skip(msg=None):
248 """Decorator factory - mark a test function for skipping from test suite.
248 """Decorator factory - mark a test function for skipping from test suite.
249
249
250 Parameters
250 Parameters
251 ----------
251 ----------
252 msg : string
252 msg : string
253 Optional message to be added.
253 Optional message to be added.
254
254
255 Returns
255 Returns
256 -------
256 -------
257 decorator : function
257 decorator : function
258 Decorator, which, when applied to a function, causes SkipTest
258 Decorator, which, when applied to a function, causes SkipTest
259 to be raised, with the optional message added.
259 to be raised, with the optional message added.
260 """
260 """
261
261
262 return skipif(True,msg)
262 return skipif(True,msg)
263
263
264
264
265 def onlyif(condition, msg):
265 def onlyif(condition, msg):
266 """The reverse from skipif, see skipif for details."""
266 """The reverse from skipif, see skipif for details."""
267
267
268 if callable(condition):
268 if callable(condition):
269 skip_condition = lambda : not condition()
269 skip_condition = lambda : not condition()
270 else:
270 else:
271 skip_condition = lambda : not condition
271 skip_condition = lambda : not condition
272
272
273 return skipif(skip_condition, msg)
273 return skipif(skip_condition, msg)
274
274
275 #-----------------------------------------------------------------------------
275 #-----------------------------------------------------------------------------
276 # Utility functions for decorators
276 # Utility functions for decorators
277 def module_not_available(module):
277 def module_not_available(module):
278 """Can module be imported? Returns true if module does NOT import.
278 """Can module be imported? Returns true if module does NOT import.
279
279
280 This is used to make a decorator to skip tests that require module to be
280 This is used to make a decorator to skip tests that require module to be
281 available, but delay the 'import numpy' to test execution time.
281 available, but delay the 'import numpy' to test execution time.
282 """
282 """
283 try:
283 try:
284 mod = __import__(module)
284 mod = __import__(module)
285 mod_not_avail = False
285 mod_not_avail = False
286 except ImportError:
286 except ImportError:
287 mod_not_avail = True
287 mod_not_avail = True
288
288
289 return mod_not_avail
289 return mod_not_avail
290
290
291
291
292 def decorated_dummy(dec, name):
292 def decorated_dummy(dec, name):
293 """Return a dummy function decorated with dec, with the given name.
293 """Return a dummy function decorated with dec, with the given name.
294
294
295 Examples
295 Examples
296 --------
296 --------
297 import IPython.testing.decorators as dec
297 import IPython.testing.decorators as dec
298 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
298 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
299 """
299 """
300 dummy = lambda: None
300 dummy = lambda: None
301 dummy.__name__ = name
301 dummy.__name__ = name
302 return dec(dummy)
302 return dec(dummy)
303
303
304 #-----------------------------------------------------------------------------
304 #-----------------------------------------------------------------------------
305 # Decorators for public use
305 # Decorators for public use
306
306
307 # Decorators to skip certain tests on specific platforms.
307 # Decorators to skip certain tests on specific platforms.
308 skip_win32 = skipif(sys.platform == 'win32',
308 skip_win32 = skipif(sys.platform == 'win32',
309 "This test does not run under Windows")
309 "This test does not run under Windows")
310 skip_linux = skipif(sys.platform.startswith('linux'),
310 skip_linux = skipif(sys.platform.startswith('linux'),
311 "This test does not run under Linux")
311 "This test does not run under Linux")
312 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
312 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
313
313
314
314
315 # Decorators to skip tests if not on specific platforms.
315 # Decorators to skip tests if not on specific platforms.
316 skip_if_not_win32 = skipif(sys.platform != 'win32',
316 skip_if_not_win32 = skipif(sys.platform != 'win32',
317 "This test only runs under Windows")
317 "This test only runs under Windows")
318 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
318 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
319 "This test only runs under Linux")
319 "This test only runs under Linux")
320 skip_if_not_osx = skipif(sys.platform != 'darwin',
320 skip_if_not_osx = skipif(sys.platform != 'darwin',
321 "This test only runs under OSX")
321 "This test only runs under OSX")
322
322
323
323
324 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
324 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
325 os.environ.get('DISPLAY', '') == '')
325 os.environ.get('DISPLAY', '') == '')
326 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
326 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
327
327
328 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
328 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
329
329
330 # not a decorator itself, returns a dummy function to be used as setup
330 # not a decorator itself, returns a dummy function to be used as setup
331 def skip_file_no_x11(name):
331 def skip_file_no_x11(name):
332 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
332 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
333
333
334 # Other skip decorators
334 # Other skip decorators
335
335
336 # generic skip without module
336 # generic skip without module
337 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
337 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
338
338
339 skipif_not_numpy = skip_without('numpy')
339 skipif_not_numpy = skip_without('numpy')
340
340
341 skipif_not_matplotlib = skip_without('matplotlib')
341 skipif_not_matplotlib = skip_without('matplotlib')
342
342
343 skipif_not_sympy = skip_without('sympy')
343 skipif_not_sympy = skip_without('sympy')
344
344
345 skip_known_failure = knownfailureif(True,'This test is known to fail')
345 skip_known_failure = knownfailureif(True,'This test is known to fail')
346
346
347 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
347 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
348 'This test is known to fail on Python 3.')
348 'This test is known to fail on Python 3.')
349
349
350 # A null 'decorator', useful to make more readable code that needs to pick
350 # A null 'decorator', useful to make more readable code that needs to pick
351 # between different decorators based on OS or other conditions
351 # between different decorators based on OS or other conditions
352 null_deco = lambda f: f
352 null_deco = lambda f: f
353
353
354 # Some tests only run where we can use unicode paths. Note that we can't just
354 # Some tests only run where we can use unicode paths. Note that we can't just
355 # check os.path.supports_unicode_filenames, which is always False on Linux.
355 # check os.path.supports_unicode_filenames, which is always False on Linux.
356 try:
356 try:
357 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
357 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
358 except UnicodeEncodeError:
358 except UnicodeEncodeError:
359 unicode_paths = False
359 unicode_paths = False
360 else:
360 else:
361 unicode_paths = True
361 unicode_paths = True
362 f.close()
362 f.close()
363
363
364 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
364 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
365 "where we can use unicode in filenames."))
365 "where we can use unicode in filenames."))
366
366
367
367
368 def onlyif_cmds_exist(*commands):
368 def onlyif_cmds_exist(*commands):
369 """
369 """
370 Decorator to skip test when at least one of `commands` is not found.
370 Decorator to skip test when at least one of `commands` is not found.
371 """
371 """
372 for cmd in commands:
372 for cmd in commands:
373 try:
373 try:
374 if not is_cmd_found(cmd):
374 if not is_cmd_found(cmd):
375 return skip("This test runs only if command '{0}' "
375 return skip("This test runs only if command '{0}' "
376 "is installed".format(cmd))
376 "is installed".format(cmd))
377 except ImportError as e:
377 except ImportError as e:
378 # is_cmd_found uses pywin32 on windows, which might not be available
378 # is_cmd_found uses pywin32 on windows, which might not be available
379 if sys.platform == 'win32' and 'pywin32' in str(e):
379 if sys.platform == 'win32' and 'pywin32' in str(e):
380 return skip("This test runs only if pywin32 and command '{0}' "
380 return skip("This test runs only if pywin32 and command '{0}' "
381 "is installed".format(cmd))
381 "is installed".format(cmd))
382 raise e
382 raise e
383 return null_deco
383 return null_deco
384
385 def onlyif_any_cmd_exists(*commands):
386 """
387 Decorator to skip test unless at least one of `commands` is found.
388 """
389 for cmd in commands:
390 try:
391 if is_cmd_found(cmd):
392 return null_deco
393 except ImportError as e:
394 # is_cmd_found uses pywin32 on windows, which might not be available
395 if sys.platform == 'win32' and 'pywin32' in str(e):
396 return skip("This test runs only if pywin32 and commands '{0}' "
397 "are installed".format(commands))
398 raise e
399 return skip("This test runs only if one of the commands {0} "
400 "is installed".format(commands))
General Comments 0
You need to be logged in to leave comments. Login now