Merge branch 'pull-50'
diff --git a/.travis.yml b/.travis.yml
index f872f70..b277b4b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,25 +1,46 @@
 language: python
 sudo: false
-env:
-  - TOXENV=py26-nose
-  - TOXENV=py26-nose2
-  - TOXENV=py26-pytest
-  - TOXENV=py26-unit
-  - TOXENV=py26-unit2
-  - TOXENV=py27-nose
-  - TOXENV=py27-nose2
-  - TOXENV=py27-pytest
-  - TOXENV=py27-unit
-  - TOXENV=py27-unit2
-  - TOXENV=py33-nose
-  - TOXENV=py33-nose2
-  - TOXENV=py33-pytest
-  - TOXENV=py33-unit
-  - TOXENV=py33-unit2
-  - TOXENV=pypy-nose
-  - TOXENV=pypy-nose2
-  - TOXENV=pypy-pytest
-  - TOXENV=pypy-unit
-  - TOXENV=pypy-unit2
+matrix:
+  include:
+    - env: "TOXENV=py27-nose"
+      python: "2.7"
+    - env: "TOXENV=py27-nose2"
+      python: "2.7"
+    - env: "TOXENV=py27-pytest"
+      python: "2.7"
+    - env: "TOXENV=py27-unit"
+      python: "2.7"
+    - env: "TOXENV=py27-unit2"
+      python: "2.7"
+    - env: "TOXENV=py35-nose"
+      python: "3.5"
+    - env: "TOXENV=py35-nose2"
+      python: "3.5"
+    - env: "TOXENV=py35-pytest"
+      python: "3.5"
+    - env: "TOXENV=py35-unit"
+      python: "3.5"
+    - env: "TOXENV=py35-unit2"
+      python: "3.5"
+    - env: "TOXENV=py36-nose"
+      python: "3.6"
+    - env: "TOXENV=py36-nose2"
+      python: "3.6"
+    - env: "TOXENV=py36-pytest"
+      python: "3.6"
+    - env: "TOXENV=py36-unit"
+      python: "3.6"
+    - env: "TOXENV=py36-unit2"
+      python: "3.6"
+    - env: "TOXENV=pypy-nose"
+      python: "pypy"
+    - env: "TOXENV=pypy-nose2"
+      python: "pypy"
+    - env: "TOXENV=pypy-pytest"
+      python: "pypy"
+    - env: "TOXENV=pypy-unit"
+      python: "pypy"
+    - env: "TOXENV=pypy-unit2"
+      python: "pypy"
 install: pip install tox
 script: tox
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index b4fc834..c4628f1 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,9 +1,13 @@
-0.6.3 (2017-11-18)
-    * Added parameterized_class feature. Now it supports
-    parameterized for an entire test class setting new attributes
-    and values.
-0.6.2 (???)
+0.7.0 (2018-12-30)
+    * Added parameterized_class feature, for parameterizing entire test
+      classes.
+
+0.6.2 (2018-03-11)
     * Make sure that `setUp` and `tearDown` methods work correctly (#40)
+    * Raise a ValueError when input is empty (thanks @danielbradburn;
+      https://github.com/wolever/parameterized/pull/48)
+    * Fix the order when number of cases exceeds 10 (thanks @ntflc;
+      https://github.com/wolever/parameterized/pull/49)
 
 0.6.1 (2017-03-21)
     * Rename package from nose-parameterized to parameterized. A
diff --git a/parameterized/parameterized.py b/parameterized/parameterized.py
index a4383a7..fae361b 100644
--- a/parameterized/parameterized.py
+++ b/parameterized/parameterized.py
@@ -13,6 +13,12 @@
 
 from unittest import TestCase
 
+try:
+    from unittest import SkipTest
+except ImportError:
+    class SkipTest(Exception):
+        pass
+
 PY3 = sys.version_info[0] == 3
 PY2 = sys.version_info[0] == 2
 
@@ -40,6 +46,30 @@
 
 _param = namedtuple("param", "args kwargs")
 
+def skip_on_empty_helper(*a, **kw):
+    raise SkipTest("parameterized input is empty")
+
+def reapply_patches_if_need(func):
+
+    def dummy_wrapper(orgfunc):
+        @wraps(orgfunc)
+        def dummy_func(*args, **kwargs):
+            return orgfunc(*args, **kwargs)
+        return dummy_func
+
+    if hasattr(func, 'patchings'):
+        func = dummy_wrapper(func)
+        tmp_patchings = func.patchings
+        delattr(func, 'patchings')
+        for patch_obj in tmp_patchings:
+            func = patch_obj.decorate_callable(func)
+    return func
+
+def delete_patches_if_need(func):
+    if hasattr(func, 'patchings'):
+        func.patchings[:] = []
+
+
 class param(_param):
     """ Represents a single parameter to a test case.
 
@@ -283,9 +313,10 @@
                 assert_equal(a + b, expected)
         """
 
-    def __init__(self, input, doc_func=None):
+    def __init__(self, input, doc_func=None, skip_on_empty=False):
         self.get_input = self.input_as_callable(input)
         self.doc_func = doc_func or default_doc_func
+        self.skip_on_empty = skip_on_empty
 
     def __call__(self, test_func):
         self.assert_not_in_testcase_subclass()
@@ -321,9 +352,21 @@
                     if test_self is not None:
                         delattr(test_cls, test_func.__name__)
                     wrapper.__doc__ = original_doc
-        wrapper.parameterized_input = self.get_input()
+
+        input = self.get_input()
+        if not input:
+            if not self.skip_on_empty:
+                raise ValueError(
+                    "Parameters iterable is empty (hint: use "
+                    "`parameterized([], skip_on_empty=True)` to skip "
+                    "this test when the input is empty)"
+                )
+            wrapper = wraps(test_func)(lambda: skip_on_empty_helper())
+
+        wrapper.parameterized_input = input
         wrapper.parameterized_func = test_func
         test_func.__name__ = "_parameterized_original_%s" %(test_func.__name__, )
+
         return wrapper
 
     def param_as_nose_tuple(self, test_self, func, num, p):
@@ -388,7 +431,8 @@
         return [ param.from_decorator(p) for p in input_values ]
 
     @classmethod
-    def expand(cls, input, name_func=None, doc_func=None, **legacy):
+    def expand(cls, input, name_func=None, doc_func=None, skip_on_empty=False,
+               **legacy):
         """ A "brute force" method of parameterizing test cases. Creates new
             test cases and injects them into the namespace that the wrapped
             function is being defined in. Useful for parameterizing tests in
@@ -425,11 +469,31 @@
             frame_locals = frame[0].f_locals
 
             paramters = cls.input_as_callable(input)()
+
+            if not paramters:
+                if not skip_on_empty:
+                    raise ValueError(
+                        "Parameters iterable is empty (hint: use "
+                        "`parameterized.expand([], skip_on_empty=True)` to skip "
+                        "this test when the input is empty)"
+                    )
+                return wraps(f)(lambda: skip_on_empty_helper())
+
+            digits = len(str(len(paramters) - 1))
             for num, p in enumerate(paramters):
-                name = name_func(f, num, p)
-                frame_locals[name] = cls.param_as_standalone_func(p, f, name)
+                name = name_func(f, "{num:0>{digits}}".format(digits=digits, num=num), p)
+                # If the original function has patches applied by 'mock.patch',
+                # re-construct all patches on the just former decoration layer
+                # of param_as_standalone_func so as not to share
+                # patch objects between new functions
+                nf = reapply_patches_if_need(f)
+                frame_locals[name] = cls.param_as_standalone_func(p, nf, name)
                 frame_locals[name].__doc__ = doc_func(f, num, p)
 
+            # Delete original patches to prevent new function from evaluating
+            # original patching object as well as re-constructed patches.
+            delete_patches_if_need(f)
+
             f.__test__ = False
         return parameterized_expand_wrapper
 
diff --git a/parameterized/test.py b/parameterized/test.py
index a3b5e2c..7bc6b49 100644
--- a/parameterized/test.py
+++ b/parameterized/test.py
@@ -1,13 +1,13 @@
 # coding=utf-8
 
 import inspect
+import mock
 from unittest import TestCase
-from nose.tools import assert_equal
-from nose.plugins.skip import SkipTest
+from nose.tools import assert_equal, assert_raises
 
 from .parameterized import (
     PY3, PY2, parameterized, param, parameterized_argument_value_pairs,
-    short_repr, detect_runner, parameterized_class,
+    short_repr, detect_runner, parameterized_class, SkipTest,
 )
 
 def assert_contains(haystack, needle):
@@ -101,6 +101,74 @@
     return custom_naming_func
 
 
+@mock.patch("os.getpid")
+class TestParameterizedExpandWithMockPatchForClass(TestCase):
+    expect([
+        "test_one_function_patch_decorator('foo1', 'umask', 'getpid')",
+        "test_one_function_patch_decorator('foo0', 'umask', 'getpid')",
+        "test_one_function_patch_decorator(42, 'umask', 'getpid')",
+    ])
+
+    @parameterized.expand([(42, ), "foo0", param("foo1")])
+    @mock.patch("os.umask")
+    def test_one_function_patch_decorator(self, foo, mock_umask, mock_getpid):
+        missing_tests.remove("test_one_function_patch_decorator(%r, %r, %r)" %
+                             (foo, mock_umask._mock_name,
+                              mock_getpid._mock_name))
+
+    expect([
+        "test_multiple_function_patch_decorator"
+        "(42, 51, 'umask', 'fdopen', 'getpid')",
+        "test_multiple_function_patch_decorator"
+        "('foo0', 'bar0', 'umask', 'fdopen', 'getpid')",
+        "test_multiple_function_patch_decorator"
+        "('foo1', 'bar1', 'umask', 'fdopen', 'getpid')",
+    ])
+
+    @parameterized.expand([(42, 51), ("foo0", "bar0"), param("foo1", "bar1")])
+    @mock.patch("os.fdopen")
+    @mock.patch("os.umask")
+    def test_multiple_function_patch_decorator(self, foo, bar, mock_umask,
+                                               mock_fdopen, mock_getpid):
+        missing_tests.remove("test_multiple_function_patch_decorator"
+                             "(%r, %r, %r, %r, %r)" %
+                             (foo, bar, mock_umask._mock_name,
+                              mock_fdopen._mock_name, mock_getpid._mock_name))
+
+
+class TestParameterizedExpandWithNoMockPatchForClass(TestCase):
+    expect([
+        "test_one_function_patch_decorator('foo1', 'umask')",
+        "test_one_function_patch_decorator('foo0', 'umask')",
+        "test_one_function_patch_decorator(42, 'umask')",
+    ])
+
+    @parameterized.expand([(42, ), "foo0", param("foo1")])
+    @mock.patch("os.umask")
+    def test_one_function_patch_decorator(self, foo, mock_umask):
+        missing_tests.remove("test_one_function_patch_decorator(%r, %r)" %
+                             (foo, mock_umask._mock_name))
+
+    expect([
+        "test_multiple_function_patch_decorator"
+        "(42, 51, 'umask', 'fdopen')",
+        "test_multiple_function_patch_decorator"
+        "('foo0', 'bar0', 'umask', 'fdopen')",
+        "test_multiple_function_patch_decorator"
+        "('foo1', 'bar1', 'umask', 'fdopen')",
+    ])
+
+    @parameterized.expand([(42, 51), ("foo0", "bar0"), param("foo1", "bar1")])
+    @mock.patch("os.fdopen")
+    @mock.patch("os.umask")
+    def test_multiple_function_patch_decorator(self, foo, bar, mock_umask,
+                                               mock_fdopen):
+        missing_tests.remove("test_multiple_function_patch_decorator"
+                             "(%r, %r, %r, %r)" %
+                             (foo, bar, mock_umask._mock_name,
+                              mock_fdopen._mock_name))
+
+
 class TestParamerizedOnTestCase(TestCase):
     expect([
         "test_on_TestCase('foo0', bar=None)",
@@ -214,6 +282,32 @@
     else:
         raise AssertionError("Expected exception not raised")
 
+
+def test_helpful_error_on_empty_iterable_input():
+    try:
+        parameterized([])(lambda: None)
+    except ValueError as e:
+        assert_contains(str(e), "iterable is empty")
+    else:
+        raise AssertionError("Expected exception not raised")
+
+def test_skip_test_on_empty_iterable():
+    func = parameterized([], skip_on_empty=True)(lambda: None)
+    assert_raises(SkipTest, func)
+
+
+def test_helpful_error_on_empty_iterable_input_expand():
+    try:
+        class ExpectErrorOnEmptyInput(TestCase):
+            @parameterized.expand([])
+            def test_expect_error(self):
+                pass
+    except ValueError as e:
+        assert_contains(str(e), "iterable is empty")
+    else:
+        raise AssertionError("Expected exception not raised")
+
+
 expect("generator", [
     "test_wrapped_iterable_input('foo')",
 ])
@@ -302,6 +396,13 @@
     pass
 
 
+cases_over_10 = [(i, i+1) for i in range(11)]
+
+@parameterized(cases_over_10)
+def test_cases_over_10(input, expected):
+    assert_equal(input, expected-1)
+
+
 @parameterized_class(("a", "b", "c"), [
     ("foo", 1, 2),
     ("bar", 3, 0),
@@ -354,4 +455,3 @@
             self.foo,
             self.bar,
         ))
-
diff --git a/tox.ini b/tox.ini
index fade720..8bceac2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,9 +1,9 @@
 [tox]
-envlist=py{26,27,35,36,37,py}-{nose,nose2,pytest,unit,unit2}
-
+envlist=py{27,35,36,37,py}-{nose,nose2,pytest,unit,unit2} 
 [testenv]
 deps=
     nose
+    mock
     nose2: nose2
     pytest: pytest>=2
     unit2: unittest2