Skip to content

Commit 7b485a7

Browse files
committed
Skipping by capability/feature
This change provide a way to skip one or more tests using features names to make it easier to understand why the test is being skipped, specially in the case it is testing new features by avoid the if `list of language` approach. It does not replace the skipped lists implemented for each driver testkit-backend since the skip reason could be different. The usage of the RunTest message to fetch capabilities was done to keep a simple and backwards compatible way to use the fetch this information.
1 parent e13c397 commit 7b485a7

File tree

6 files changed

+86
-0
lines changed

6 files changed

+86
-0
lines changed

nutkit/protocol/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from .requests import *
22
from .cypher import *
33
from .responses import *
4+
from .feature import *

nutkit/protocol/feature.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""
2+
Enumerate all the capabilities in the drivers
3+
"""
4+
from enum import Enum
5+
6+
7+
class Feature(Enum):
8+
AUTHORIZATION_EXPIRED_TREATMENT = 'AutorizationExpiredTreament'

nutkit/protocol/requests.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ def __init__(self, test_name):
2727
self.testName = test_name
2828

2929

30+
class GetFeatures:
31+
""" Request the backend to the list of features supported by the driver.
32+
The backend should respond with FeatureList.
33+
"""
34+
pass
35+
36+
3037
class NewDriver:
3138
""" Request to create a new driver instance on the backend.
3239
Backend should respond with a Driver response or an Error response.

nutkit/protocol/responses.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ class RunTest:
2626
pass
2727

2828

29+
class FeatureList:
30+
""" Response to GetFeatures indication the features supported
31+
by the driver"""
32+
def __init__(self, features=None):
33+
if features is None:
34+
features = []
35+
self.features = features
36+
37+
2938
class SkipTest:
3039
""" Response to StartTest indicating that the test should be skipped"""
3140

tests/shared.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from nutkit import protocol
1717
from nutkit.backend import Backend
18+
import warnings
1819

1920

2021
def get_backend_host_and_port():
@@ -30,6 +31,62 @@ def new_backend():
3031
return Backend(host, port)
3132

3233

34+
def driver_feature(feature):
35+
if not isinstance(feature, protocol.Feature):
36+
raise Exception('The argument should be instance of Feature')
37+
38+
def get_valid_test_case(*args, **kwargs):
39+
if not args or not isinstance(args[0], TestkitTestCase):
40+
raise Exception('Should only decorate TestkitTestCase methods')
41+
return args[0]
42+
43+
def driver_feature_decorator(func):
44+
def wrapper(*args, **kwargs):
45+
test_case = get_valid_test_case(*args, **kwargs)
46+
if (feature.value not in test_case._driver_features):
47+
test_case.skipTest("Needs support for %s" % feature.value)
48+
return func(*args, **kwargs)
49+
return wrapper
50+
return driver_feature_decorator
51+
52+
53+
class MemoizedSupplier:
54+
""" Momoize the function it annotates.
55+
This way the decorated function will always return the
56+
same value of the first interaction independent of the
57+
supplied params.
58+
"""
59+
60+
def __init__(self, func):
61+
self._func = func
62+
self._memo = None
63+
64+
def __call__(self, *args, **kwargs):
65+
if self._memo is None:
66+
self._memo = self._func(*args, **kwargs)
67+
return self._memo
68+
69+
70+
@MemoizedSupplier
71+
def get_driver_features(backend):
72+
# TODO Remove when dotnet implements the GetFeature message
73+
if get_driver_name() in ['dotnet']:
74+
warnings.warn("Driver does not implements GetFeatures.")
75+
features = ()
76+
return features
77+
78+
try:
79+
response = backend.sendAndReceive(protocol.GetFeatures())
80+
if not isinstance(response, protocol.FeatureList):
81+
raise Exception("Response is not instance of FeatureList")
82+
features = tuple(response.features)
83+
except Exception as e:
84+
warnings.warn("Could not fetch FeatureList: %s" % e)
85+
features = ()
86+
87+
return features
88+
89+
3390
def get_driver_name():
3491
return os.environ['TEST_DRIVER_NAME']
3592

@@ -40,6 +97,7 @@ def setUp(self):
4097
id_ = re.sub(r"^([^\.]+\.)*?tests\.", "", self.id())
4198
self._backend = new_backend()
4299
self.addCleanup(self._backend.close)
100+
self._driver_features = get_driver_features(self._backend)
43101
response = self._backend.sendAndReceive(protocol.StartTest(id_))
44102
if isinstance(response, protocol.SkipTest):
45103
self.skipTest(response.reason)

tests/stub/authorization.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from tests.shared import (
44
get_driver_name,
55
TestkitTestCase,
6+
driver_feature
67
)
78
from tests.stub.shared import StubServer
89

@@ -698,6 +699,7 @@ def read_return_1_failure_return_2_succeed_script(self):
698699
+}
699700
"""
700701

702+
@driver_feature(types.Feature.AUTHORIZATION_EXPIRED_TREATMENT)
701703
def test_should_drop_connection_after_AuthorizationExpired(self):
702704
self._server.start(
703705
script=self.read_return_1_failure_return_2_succeed_script()
@@ -732,6 +734,7 @@ def test_should_drop_connection_after_AuthorizationExpired(self):
732734

733735
driver.close()
734736

737+
@driver_feature(types.Feature.AUTHORIZATION_EXPIRED_TREATMENT)
735738
def test_should_be_able_to_use_current_sessions_after_AuthorizationExpired(self):
736739
self._server.start(
737740
script=self.read_return_1_failure_return_2_succeed_script()

0 commit comments

Comments
 (0)