# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See LICENSE in the project root # for license information. from __future__ import print_function, with_statement, absolute_import import numbers import re from ptvsd.compat import unicode from tests.helpers.pathutils import compare_path class BasePattern(object): def __repr__(self): raise NotImplementedError() def __eq__(self, value): raise NotImplementedError() def such_that(self, condition): return Maybe(self, condition) class Any(BasePattern): """Represents a wildcard in a pattern as used by json_matches(), and matches any single object in the same place in input data. """ def __init__(self): pass def __repr__(self): return 'ANY' def __eq__(self, other): return True @staticmethod def dict_with(items): """A pattern that matches any dict that contains the specified key-value pairs. d1 = {'a': 1, 'b': 2, 'c': 3} d2 = {'a': 1, 'b': 2} d1 == Pattern(d2) # False (need exact match) d1 == ANY.dict_with(d2) # True (subset matches) """ class AnyDictWith(dict): def __repr__(self): return repr(items)[:-1] + ', ...}' def __eq__(self, other): if not isinstance(other, dict): return NotImplemented d = {key: ANY for key in other} d.update(self) return d == other def __ne__(self, other): return not (self == other) items = dict(items) return AnyDictWith(items) class Maybe(BasePattern): """A pattern that matches if condition is True. """ name = None def __init__(self, pattern, condition): self.pattern = pattern self.condition = condition def __repr__(self): return self.name or 'Maybe(%r)' % self.pattern def __eq__(self, value): return self.condition(value) and value == self.pattern class Success(BasePattern): """A pattern that matches a response body depending on whether the request succeeded or failed. """ def __init__(self, success): self.success = success def __repr__(self): return 'SUCCESS' if self.success else 'FAILURE' def __eq__(self, response_body): return self.success != isinstance(response_body, Exception) class Is(BasePattern): """A pattern that matches a specific object only (i.e. uses operator 'is' rather than '=='). """ def __init__(self, obj): self.obj = obj def __repr__(self): return 'Is(%r)' % self.obj def __eq__(self, value): return self.obj is value class Path(object): """A pattern that matches strings as path, using os.path.normcase before comparison, and sys.getfilesystemencoding() to compare Unicode and non-Unicode strings. """ def __init__(self, s): self.s = s def __repr__(self): return 'Path(%r)' % (self.s,) def __eq__(self, other): if not (isinstance(other, bytes) or isinstance(other, unicode)): return NotImplemented return compare_path(self.s, other, show=False) def __ne__(self, other): return not (self == other) class Regex(object): """A pattern that matches strings against regex, as if with re.match(). """ def __init__(self, regex): self.regex = regex def __repr__(self): return '/%s/' % (self.regex,) def __eq__(self, other): if not (isinstance(other, bytes) or isinstance(other, unicode)): return NotImplemented return re.match(self.regex, other) def __ne__(self, other): return not (self == other) SUCCESS = Success(True) FAILURE = Success(False) ANY = Any() ANY.bool = ANY.such_that(lambda x: x is True or x is False) ANY.bool.name = 'ANY.bool' ANY.str = ANY.such_that(lambda x: isinstance(x, unicode)) ANY.str.name = 'ANY.str' ANY.num = ANY.such_that(lambda x: isinstance(x, numbers.Real)) ANY.num.name = 'ANY.num' ANY.int = ANY.such_that(lambda x: isinstance(x, numbers.Integral)) ANY.int.name = 'ANY.int' # Note: in practice it could be any int32, but as in those cases we expect the number to be # incremented sequentially, this should be reasonable for tests. ANY.dap_id = ANY.such_that(lambda x: isinstance(x, numbers.Integral) and 0 <= x < 10000) ANY.dap_id.name = 'ANY.dap_id'