Commits

Anonymous committed bf80d08 Merge

Merged test-testid

Comments (0)

Files changed (6)

 \.noseids
 .*\.egg-info$
+dist
+\..*\.sw.
+.*\~
+.+\.py[co]

nose2/plugins/testid.py

         self._loaded = False
 
     def nextId(self):
+        """Increment ID and return it.
+        
+        XXX: Make private?
+        """
         self.id += 1
         return self.id
 
     def startTest(self, event):
+        """Implement hook"""
         testid = event.test.id().split('\n')[0]
         if testid not in self.tests:
             id_ = self.nextId()
         event.message('#%s ' % id_, (2,))
 
     def loadTestsFromName(self, event):
-        self.loadIds()
-        id_ = self.argToId(event.name)
-        if id_ and id_ in self.ids:
-            event.name = self.ids[id_]
+        """Implement hook.
+        
+        If the name is a number, it might be an ID assigned by us. If we can
+        find a test to which we have assigned that ID, event.name is changed to
+        the test's real ID. In this way, tests can be referred to via sequential
+        numbers.
+        """
+        testid = self._testNameFromId(event.name)
+        if testid is not None:
+            event.name = testid
 
     def loadTestsFromNames(self, event):
-        self.loadIds()
-        new_names = []
-        for name in event.names:
-            if self.argToId(name) in self.ids:
-                new_names.append(self.ids[name])
-            else:
-                new_names.append(name)
-        event.names[:] = new_names
+        """Implement hook."""
+        for i, name in enumerate(event.names[:]):
+            testid = self._testNameFromId(name)
+            if testid is not None:
+                event.names[i] = testid
 
     def stopTestRun(self, event):
+        """Implement hook."""
         fh = open(self.idfile, 'w')
         pickle.dump({'ids': self.ids, 'tests': self.tests}, fh)
 
-    def argToId(self, name):
-        m = self.idpat.match(name)
-        if m:
-            return int(m.groups()[0])
-
     def loadIds(self):
+        """Load previously pickled 'ids' and 'tests' attributes.
+        
+        XXX: Make private?
+        """
         if self._loaded:
             return
-        fh = open(self.idfile, 'r')
-        data = pickle.load(fh)
+
+        try:
+            fh = open(self.idfile, 'r')
+        except EnvironmentError:
+            self._loaded = True
+            return
+        try:
+            data = pickle.load(fh)
+        finally:
+            fh.close()
+
         if 'ids' in data:
             self.ids = data['ids']
         if 'tests' in data:
             self.tests = data['tests']
         self.id = max(self.ids.keys())
         self._loaded = True
+
+
+    def _testNameFromId(self, name):
+        """Try to translate one of our IDs to real test ID."""
+        m = self.idpat.match(name)
+        if m is None:
+            return None
+
+        id_ = int(m.groups()[0])
+
+        self.loadIds()
+        # Translate to test's real ID
+        try:
+            return self.ids[id_]
+        except KeyError:
+            return None

nose2/tests/__init__.py

-import os
-import subprocess
-import unittest2
-
-HERE = os.path.dirname(__file__)
-ROOT = os.path.join(HERE, '..', '..')
-SUPPORT = os.path.join(ROOT, 'support')
-
-
-class FunctionalTestCase(unittest2.TestCase):
-
-    tags = ['functional']
-
-    def assertTestRunOutputMatches(self, proc, stdout=None, stderr=None):
-        cmd_stdout, cmd_stderr = proc.communicate()
-        if stdout:
-            self.assertRegexpMatches(cmd_stdout, stdout)
-        if stderr:
-            self.assertRegexpMatches(cmd_stderr, stderr)
-
-    def runIn(self, testdir, *args):
-        cmd = ['nose2'] + list(args)
-        proc = subprocess.Popen(cmd, 
-                                cwd=os.path.join(SUPPORT, testdir),
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE)
-        return proc
+"""Unit and functional tests."""

nose2/tests/_common.py

+"""Common functionality."""
+import os.path
+import tempfile
+import shutil
+import sys
+import subprocess
+import unittest2
+
+HERE = os.path.dirname(__file__)
+ROOT = os.path.join(HERE, '..', '..')
+SUPPORT = os.path.join(ROOT, 'support')
+
+
+class TestCase(unittest2.TestCase):
+    """TestCase extension.
+    
+    If the class variable _RUN_IN_TEMP is True (default: False), tests will be
+    performed in a temporary directory, which is deleted afterwards.
+    """
+    _RUN_IN_TEMP = False
+
+    def setUp(self):
+        super(TestCase, self).setUp()
+        
+        if self._RUN_IN_TEMP:
+            self._orig_dir = os.getcwd()
+            work_dir = self._work_dir = tempfile.mkdtemp()
+            os.chdir(self._work_dir)
+            # Make sure it's possible to import modules from current directory
+            sys.path.insert(0, work_dir)
+
+    def tearDown(self):
+        super(TestCase, self).tearDown()
+
+        if self._RUN_IN_TEMP:
+            os.chdir(self._orig_dir)
+            shutil.rmtree(self._work_dir, ignore_errors=True)
+
+
+class FunctionalTestCase(unittest2.TestCase):
+
+    tags = ['functional']
+
+    def assertTestRunOutputMatches(self, proc, stdout=None, stderr=None):
+        cmd_stdout, cmd_stderr = proc.communicate()
+        if stdout:
+            self.assertRegexpMatches(cmd_stdout, stdout)
+        if stderr:
+            self.assertRegexpMatches(cmd_stderr, stderr)
+
+    def runIn(self, testdir, *args):
+        cmd = ['nose2'] + list(args)
+        proc = subprocess.Popen(cmd, 
+                                cwd=os.path.join(SUPPORT, testdir),
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE)
+        return proc
+
+
+class _FakeEventBase(object):
+    """Baseclass for fake Events."""
+    def __init__(self):
+        self._fake_messages = []
+
+    def message(self, msg, verbosity=(1, 2)):
+        self._fake_messages.append((msg, verbosity))
+
+class FakeStartTestEvent(_FakeEventBase):
+    """Fake StartTestEvent."""
+    def __init__(self, test):
+        super(FakeStartTestEvent, self).__init__()
+        self.test = test
+        self.result = test.defaultTestResult()
+        import time
+        self.startTime = time.time()
+
+class FakeLoadFromNameEvent(_FakeEventBase):
+    """Fake LoadFromNameEvent."""
+    def __init__(self, name):
+        super(FakeLoadFromNameEvent, self).__init__()
+        self.name = name
+
+class FakeLoadFromNamesEvent(_FakeEventBase):
+    """Fake LoadFromNamesEvent."""
+    def __init__(self, names):
+        super(FakeLoadFromNamesEvent, self).__init__()
+        self.names = names

nose2/tests/test_logcapture_plugin.py

 import re
 import unittest2
 
-from . import FunctionalTestCase
+from ._common import FunctionalTestCase
 from ..plugins.logcapture import LogCapture
 
 

nose2/tests/test_testid_plugin.py

+"""Test testid plugin."""
+from unittest2 import TestCase
+import re
+import os.path
+import pickle
+import shutil
+import tempfile
+
+from ..plugins import testid
+from ._common import (FakeStartTestEvent, FakeLoadFromNameEvent,
+        FakeLoadFromNamesEvent)
+
+class UnitTestTestId(TestCase):
+    """Test class TestId.
+    
+    Tests are carried out in a temporary directory, since TestId stores state
+    to file. The temporary directory is removed after testing.
+    """
+    tags = ['unit']
+
+    def setUp(self):
+        super(UnitTestTestId, self).setUp()
+        self._orig_dir = os.getcwd()
+        # XXX: Use deterministic temporary directory, which can be inspected
+        # after test?
+        self._temp_dir = tempfile.mkdtemp()
+        os.chdir(self._temp_dir)
+
+    def tearDown(self):
+        super(UnitTestTestId, self).tearDown()
+        os.chdir(self._orig_dir)
+        shutil.rmtree(self._temp_dir, ignore_errors=True)
+
+
+    def test___init__(self):
+        """Test the __init__ method."""
+        plug = self._create()
+        # Test attributes
+        for name, exp_val in [('configSection', 'testid'), ('commandLineSwitch',
+            ('I', 'with-id', 'Add test ids to output')), ('idfile',
+                os.path.abspath('.noseids')), ('ids', {}), ('tests', {}),
+                ('id', 0)]:
+            try:
+                val = getattr(plug, name)
+            except AttributeError:
+                self.fail('TestId instance doesn\'t have attribute %s' % (name,))
+            self.assertEqual(val, exp_val, 'Attribute %s should have value '
+                '\'%s\', but has value %s' % (name, exp_val, val))
+
+
+    def test_start_test(self):
+        """Test startTest method."""
+        event = FakeStartTestEvent(self)
+        plug = self._create()
+        plug.startTest(event)
+
+        self.assertEqual(plug.id, 1)
+        test_id = self.id()
+        self.assertEqual(plug.ids, {1: test_id})
+        self.assertEqual(plug.tests, {test_id: 1})
+        self.assertEqual(event._fake_messages, [('#1 ', (2,))])
+
+    def test_start_test_twice(self):
+        """Test calling startTest twice."""
+        event = FakeStartTestEvent(self)
+        plug = self._create()
+        plug.startTest(event)
+        plug.startTest(event)
+
+        self.assertEqual(plug.id, 1)
+        test_id = self.id()
+        self.assertEqual(plug.ids, {1: test_id})
+        self.assertEqual(plug.tests, {test_id: 1})
+        msg = ('#1 ', (2,))
+        self.assertEqual(event._fake_messages, [msg, msg])
+
+
+    def test_stop_test_run(self):
+        """Test stopTestRun method."""
+        plug = self._create()
+        plug.startTest(FakeStartTestEvent(self))
+        plug.stopTestRun(None)
+
+        fh = open(plug.idfile, 'r')
+        try:
+            data = pickle.load(fh)
+        finally:
+            fh.close()
+        self.assertEqual(data, {'ids': plug.ids, 'tests': plug.tests})
+
+
+    def test_load_tests_from_name(self):
+        """Test loadTestsFromName method."""
+        plug = self._create()
+        # By first starting/stopping a test, an ID is assigned by the plugin
+        plug.startTest(FakeStartTestEvent(self))
+        plug.stopTestRun(None)
+        event = FakeLoadFromNameEvent('1')
+        plug.loadTestsFromName(event)
+
+        # The numeric ID should be translated to this test's ID
+        self.assertEqual(event.name, self.id())
+
+    def test_load_tests_from_name_no_ids(self):
+        """Test calling loadTestsFromName when no IDs have been saved."""
+        plug = self._create()
+        event = FakeLoadFromNameEvent('1')
+        plug.loadTestsFromName(event)
+
+        # The event's name should be unchanged, since no IDs should be mapped
+        self.assertEqual(event.name, '1')
+
+
+    def test_load_tests_from_names(self):
+        """Test loadTestsFromNames method."""
+        plug = self._create()
+        # By first starting/stopping a test, an ID is assigned by the plugin
+        plug.startTest(FakeStartTestEvent(self))
+        plug.stopTestRun(None)
+        event = FakeLoadFromNamesEvent(['1', '2'])
+        plug.loadTestsFromNames(event)
+
+        name1, name2 = event.names
+        # The first numeric ID should be translated to this test's ID
+        self.assertEqual(name1, self.id())
+        # The second one should not have a match
+        self.assertEqual(name2, '2')
+
+
+    def _create(self):
+        """Create a TestId instance."""
+        return testid.TestId()