# -*- coding: utf-8 -*- """ tests.datastructures ~~~~~~~~~~~~~~~~~~~~ Tests the functionality of the provided Werkzeug datastructures. Classes prefixed with an underscore are mixins and are not discovered by the test runner. TODO: - FileMultiDict - Immutable types undertested - Split up dict tests :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from __future__ import with_statement import pytest from tests import strict_eq import pickle from contextlib import contextmanager from copy import copy, deepcopy from werkzeug import datastructures from werkzeug._compat import iterkeys, itervalues, iteritems, iterlists, \ iterlistvalues, text_type, PY2 from werkzeug.exceptions import BadRequestKeyError class TestNativeItermethods(object): def test_basic(self): @datastructures.native_itermethods(['keys', 'values', 'items']) class StupidDict(object): def keys(self, multi=1): return iter(['a', 'b', 'c'] * multi) def values(self, multi=1): return iter([1, 2, 3] * multi) def items(self, multi=1): return iter(zip(iterkeys(self, multi=multi), itervalues(self, multi=multi))) d = StupidDict() expected_keys = ['a', 'b', 'c'] expected_values = [1, 2, 3] expected_items = list(zip(expected_keys, expected_values)) assert list(iterkeys(d)) == expected_keys assert list(itervalues(d)) == expected_values assert list(iteritems(d)) == expected_items assert list(iterkeys(d, 2)) == expected_keys * 2 assert list(itervalues(d, 2)) == expected_values * 2 assert list(iteritems(d, 2)) == expected_items * 2 class _MutableMultiDictTests(object): storage_class = None def test_pickle(self): cls = self.storage_class def create_instance(module=None): if module is None: d = cls() else: old = cls.__module__ cls.__module__ = module d = cls() cls.__module__ = old d.setlist(b'foo', [1, 2, 3, 4]) d.setlist(b'bar', b'foo bar baz'.split()) return d for protocol in range(pickle.HIGHEST_PROTOCOL + 1): d = create_instance() s = pickle.dumps(d, protocol) ud = pickle.loads(s) assert type(ud) == type(d) assert ud == d alternative = pickle.dumps(create_instance('werkzeug'), protocol) assert pickle.loads(alternative) == d ud[b'newkey'] = b'bla' assert ud != d def test_basic_interface(self): md = self.storage_class() assert isinstance(md, dict) mapping = [('a', 1), ('b', 2), ('a', 2), ('d', 3), ('a', 1), ('a', 3), ('d', 4), ('c', 3)] md = self.storage_class(mapping) # simple getitem gives the first value assert md['a'] == 1 assert md['c'] == 3 with pytest.raises(KeyError): md['e'] assert md.get('a') == 1 # list getitem assert md.getlist('a') == [1, 2, 1, 3] assert md.getlist('d') == [3, 4] # do not raise if key not found assert md.getlist('x') == [] # simple setitem overwrites all values md['a'] = 42 assert md.getlist('a') == [42] # list setitem md.setlist('a', [1, 2, 3]) assert md['a'] == 1 assert md.getlist('a') == [1, 2, 3] # verify that it does not change original lists l1 = [1, 2, 3] md.setlist('a', l1) del l1[:] assert md['a'] == 1 # setdefault, setlistdefault assert md.setdefault('u', 23) == 23 assert md.getlist('u') == [23] del md['u'] md.setlist('u', [-1, -2]) # delitem del md['u'] with pytest.raises(KeyError): md['u'] del md['d'] assert md.getlist('d') == [] # keys, values, items, lists assert list(sorted(md.keys())) == ['a', 'b', 'c'] assert list(sorted(iterkeys(md))) == ['a', 'b', 'c'] assert list(sorted(itervalues(md))) == [1, 2, 3] assert list(sorted(itervalues(md))) == [1, 2, 3] assert list(sorted(md.items())) == [('a', 1), ('b', 2), ('c', 3)] assert list(sorted(md.items(multi=True))) == \ [('a', 1), ('a', 2), ('a', 3), ('b', 2), ('c', 3)] assert list(sorted(iteritems(md))) == [('a', 1), ('b', 2), ('c', 3)] assert list(sorted(iteritems(md, multi=True))) == \ [('a', 1), ('a', 2), ('a', 3), ('b', 2), ('c', 3)] assert list(sorted(md.lists())) == \ [('a', [1, 2, 3]), ('b', [2]), ('c', [3])] assert list(sorted(iterlists(md))) == \ [('a', [1, 2, 3]), ('b', [2]), ('c', [3])] # copy method c = md.copy() assert c['a'] == 1 assert c.getlist('a') == [1, 2, 3] # copy method 2 c = copy(md) assert c['a'] == 1 assert c.getlist('a') == [1, 2, 3] # deepcopy method c = md.deepcopy() assert c['a'] == 1 assert c.getlist('a') == [1, 2, 3] # deepcopy method 2 c = deepcopy(md) assert c['a'] == 1 assert c.getlist('a') == [1, 2, 3] # update with a multidict od = self.storage_class([('a', 4), ('a', 5), ('y', 0)]) md.update(od) assert md.getlist('a') == [1, 2, 3, 4, 5] assert md.getlist('y') == [0] # update with a regular dict md = c od = {'a': 4, 'y': 0} md.update(od) assert md.getlist('a') == [1, 2, 3, 4] assert md.getlist('y') == [0] # pop, poplist, popitem, popitemlist assert md.pop('y') == 0 assert 'y' not in md assert md.poplist('a') == [1, 2, 3, 4] assert 'a' not in md assert md.poplist('missing') == [] # remaining: b=2, c=3 popped = md.popitem() assert popped in [('b', 2), ('c', 3)] popped = md.popitemlist() assert popped in [('b', [2]), ('c', [3])] # type conversion md = self.storage_class({'a': '4', 'b': ['2', '3']}) assert md.get('a', type=int) == 4 assert md.getlist('b', type=int) == [2, 3] # repr md = self.storage_class([('a', 1), ('a', 2), ('b', 3)]) assert "('a', 1)" in repr(md) assert "('a', 2)" in repr(md) assert "('b', 3)" in repr(md) # add and getlist md.add('c', '42') md.add('c', '23') assert md.getlist('c') == ['42', '23'] md.add('c', 'blah') assert md.getlist('c', type=int) == [42, 23] # setdefault md = self.storage_class() md.setdefault('x', []).append(42) md.setdefault('x', []).append(23) assert md['x'] == [42, 23] # to dict md = self.storage_class() md['foo'] = 42 md.add('bar', 1) md.add('bar', 2) assert md.to_dict() == {'foo': 42, 'bar': 1} assert md.to_dict(flat=False) == {'foo': [42], 'bar': [1, 2]} # popitem from empty dict with pytest.raises(KeyError): self.storage_class().popitem() with pytest.raises(KeyError): self.storage_class().popitemlist() # key errors are of a special type with pytest.raises(BadRequestKeyError): self.storage_class()[42] # setlist works md = self.storage_class() md['foo'] = 42 md.setlist('foo', [1, 2]) assert md.getlist('foo') == [1, 2] class _ImmutableDictTests(object): storage_class = None def test_follows_dict_interface(self): cls = self.storage_class data = {'foo': 1, 'bar': 2, 'baz': 3} d = cls(data) assert d['foo'] == 1 assert d['bar'] == 2 assert d['baz'] == 3 assert sorted(d.keys()) == ['bar', 'baz', 'foo'] assert 'foo' in d assert 'foox' not in d assert len(d) == 3 def test_copies_are_mutable(self): cls = self.storage_class immutable = cls({'a': 1}) with pytest.raises(TypeError): immutable.pop('a') mutable = immutable.copy() mutable.pop('a') assert 'a' in immutable assert mutable is not immutable assert copy(immutable) is immutable def test_dict_is_hashable(self): cls = self.storage_class immutable = cls({'a': 1, 'b': 2}) immutable2 = cls({'a': 2, 'b': 2}) x = set([immutable]) assert immutable in x assert immutable2 not in x x.discard(immutable) assert immutable not in x assert immutable2 not in x x.add(immutable2) assert immutable not in x assert immutable2 in x x.add(immutable) assert immutable in x assert immutable2 in x class TestImmutableTypeConversionDict(_ImmutableDictTests): storage_class = datastructures.ImmutableTypeConversionDict class TestImmutableMultiDict(_ImmutableDictTests): storage_class = datastructures.ImmutableMultiDict def test_multidict_is_hashable(self): cls = self.storage_class immutable = cls({'a': [1, 2], 'b': 2}) immutable2 = cls({'a': [1], 'b': 2}) x = set([immutable]) assert immutable in x assert immutable2 not in x x.discard(immutable) assert immutable not in x assert immutable2 not in x x.add(immutable2) assert immutable not in x assert immutable2 in x x.add(immutable) assert immutable in x assert immutable2 in x class TestImmutableDict(_ImmutableDictTests): storage_class = datastructures.ImmutableDict class TestImmutableOrderedMultiDict(_ImmutableDictTests): storage_class = datastructures.ImmutableOrderedMultiDict def test_ordered_multidict_is_hashable(self): a = self.storage_class([('a', 1), ('b', 1), ('a', 2)]) b = self.storage_class([('a', 1), ('a', 2), ('b', 1)]) assert hash(a) != hash(b) class TestMultiDict(_MutableMultiDictTests): storage_class = datastructures.MultiDict def test_multidict_pop(self): make_d = lambda: self.storage_class({'foo': [1, 2, 3, 4]}) d = make_d() assert d.pop('foo') == 1 assert not d d = make_d() assert d.pop('foo', 32) == 1 assert not d d = make_d() assert d.pop('foos', 32) == 32 assert d with pytest.raises(KeyError): d.pop('foos') def test_multidict_pop_raise_badrequestkeyerror_for_empty_list_value(self): mapping = [('a', 'b'), ('a', 'c')] md = self.storage_class(mapping) md.setlistdefault('empty', []) with pytest.raises(KeyError): md.pop('empty') def test_multidict_popitem_raise_badrequestkeyerror_for_empty_list_value(self): mapping = [] md = self.storage_class(mapping) md.setlistdefault('empty', []) with pytest.raises(KeyError): md.popitem() def test_setlistdefault(self): md = self.storage_class() assert md.setlistdefault('u', [-1, -2]) == [-1, -2] assert md.getlist('u') == [-1, -2] assert md['u'] == -1 def test_iter_interfaces(self): mapping = [('a', 1), ('b', 2), ('a', 2), ('d', 3), ('a', 1), ('a', 3), ('d', 4), ('c', 3)] md = self.storage_class(mapping) assert list(zip(md.keys(), md.listvalues())) == list(md.lists()) assert list(zip(md, iterlistvalues(md))) == list(iterlists(md)) assert list(zip(iterkeys(md), iterlistvalues(md))) == \ list(iterlists(md)) @pytest.mark.skipif(not PY2, reason='viewmethods work only for the 2-nd version.') def test_view_methods(self): mapping = [('a', 'b'), ('a', 'c')] md = self.storage_class(mapping) vi = md.viewitems() vk = md.viewkeys() vv = md.viewvalues() assert list(vi) == list(md.items()) assert list(vk) == list(md.keys()) assert list(vv) == list(md.values()) md['k'] = 'n' assert list(vi) == list(md.items()) assert list(vk) == list(md.keys()) assert list(vv) == list(md.values()) @pytest.mark.skipif(not PY2, reason='viewmethods work only for the 2-nd version.') def test_viewitems_with_multi(self): mapping = [('a', 'b'), ('a', 'c')] md = self.storage_class(mapping) vi = md.viewitems(multi=True) assert list(vi) == list(md.items(multi=True)) md['k'] = 'n' assert list(vi) == list(md.items(multi=True)) def test_getitem_raise_badrequestkeyerror_for_empty_list_value(self): mapping = [('a', 'b'), ('a', 'c')] md = self.storage_class(mapping) md.setlistdefault('empty', []) with pytest.raises(KeyError): md['empty'] class TestOrderedMultiDict(_MutableMultiDictTests): storage_class = datastructures.OrderedMultiDict def test_ordered_interface(self): cls = self.storage_class d = cls() assert not d d.add('foo', 'bar') assert len(d) == 1 d.add('foo', 'baz') assert len(d) == 1 assert list(iteritems(d)) == [('foo', 'bar')] assert list(d) == ['foo'] assert list(iteritems(d, multi=True)) == \ [('foo', 'bar'), ('foo', 'baz')] del d['foo'] assert not d assert len(d) == 0 assert list(d) == [] d.update([('foo', 1), ('foo', 2), ('bar', 42)]) d.add('foo', 3) assert d.getlist('foo') == [1, 2, 3] assert d.getlist('bar') == [42] assert list(iteritems(d)) == [('foo', 1), ('bar', 42)] expected = ['foo', 'bar'] assert list(d.keys()) == expected assert list(d) == expected assert list(iterkeys(d)) == expected assert list(iteritems(d, multi=True)) == \ [('foo', 1), ('foo', 2), ('bar', 42), ('foo', 3)] assert len(d) == 2 assert d.pop('foo') == 1 assert d.pop('blafasel', None) is None assert d.pop('blafasel', 42) == 42 assert len(d) == 1 assert d.poplist('bar') == [42] assert not d d.get('missingkey') is None d.add('foo', 42) d.add('foo', 23) d.add('bar', 2) d.add('foo', 42) assert d == datastructures.MultiDict(d) id = self.storage_class(d) assert d == id d.add('foo', 2) assert d != id d.update({'blah': [1, 2, 3]}) assert d['blah'] == 1 assert d.getlist('blah') == [1, 2, 3] # setlist works d = self.storage_class() d['foo'] = 42 d.setlist('foo', [1, 2]) assert d.getlist('foo') == [1, 2] with pytest.raises(BadRequestKeyError): d.pop('missing') with pytest.raises(BadRequestKeyError): d['missing'] # popping d = self.storage_class() d.add('foo', 23) d.add('foo', 42) d.add('foo', 1) assert d.popitem() == ('foo', 23) with pytest.raises(BadRequestKeyError): d.popitem() assert not d d.add('foo', 23) d.add('foo', 42) d.add('foo', 1) assert d.popitemlist() == ('foo', [23, 42, 1]) with pytest.raises(BadRequestKeyError): d.popitemlist() # Unhashable d = self.storage_class() d.add('foo', 23) pytest.raises(TypeError, hash, d) def test_iterables(self): a = datastructures.MultiDict((("key_a", "value_a"),)) b = datastructures.MultiDict((("key_b", "value_b"),)) ab = datastructures.CombinedMultiDict((a, b)) assert sorted(ab.lists()) == [('key_a', ['value_a']), ('key_b', ['value_b'])] assert sorted(ab.listvalues()) == [['value_a'], ['value_b']] assert sorted(ab.keys()) == ["key_a", "key_b"] assert sorted(iterlists(ab)) == [('key_a', ['value_a']), ('key_b', ['value_b'])] assert sorted(iterlistvalues(ab)) == [['value_a'], ['value_b']] assert sorted(iterkeys(ab)) == ["key_a", "key_b"] class TestCombinedMultiDict(object): storage_class = datastructures.CombinedMultiDict def test_basic_interface(self): d1 = datastructures.MultiDict([('foo', '1')]) d2 = datastructures.MultiDict([('bar', '2'), ('bar', '3')]) d = self.storage_class([d1, d2]) # lookup assert d['foo'] == '1' assert d['bar'] == '2' assert d.getlist('bar') == ['2', '3'] assert sorted(d.items()) == [('bar', '2'), ('foo', '1')] assert sorted(d.items(multi=True)) == \ [('bar', '2'), ('bar', '3'), ('foo', '1')] assert 'missingkey' not in d assert 'foo' in d # type lookup assert d.get('foo', type=int) == 1 assert d.getlist('bar', type=int) == [2, 3] # get key errors for missing stuff with pytest.raises(KeyError): d['missing'] # make sure that they are immutable with pytest.raises(TypeError): d['foo'] = 'blub' # copies are immutable d = d.copy() with pytest.raises(TypeError): d['foo'] = 'blub' # make sure lists merges md1 = datastructures.MultiDict((("foo", "bar"),)) md2 = datastructures.MultiDict((("foo", "blafasel"),)) x = self.storage_class((md1, md2)) assert list(iterlists(x)) == [('foo', ['bar', 'blafasel'])] def test_length(self): d1 = datastructures.MultiDict([('foo', '1')]) d2 = datastructures.MultiDict([('bar', '2')]) assert len(d1) == len(d2) == 1 d = self.storage_class([d1, d2]) assert len(d) == 2 d1.clear() assert len(d1) == 0 assert len(d) == 1 class TestHeaders(object): storage_class = datastructures.Headers def test_basic_interface(self): headers = self.storage_class() headers.add('Content-Type', 'text/plain') headers.add('X-Foo', 'bar') assert 'x-Foo' in headers assert 'Content-type' in headers headers['Content-Type'] = 'foo/bar' assert headers['Content-Type'] == 'foo/bar' assert len(headers.getlist('Content-Type')) == 1 # list conversion assert headers.to_wsgi_list() == [ ('Content-Type', 'foo/bar'), ('X-Foo', 'bar') ] assert str(headers) == ( "Content-Type: foo/bar\r\n" "X-Foo: bar\r\n" "\r\n" ) assert str(self.storage_class()) == "\r\n" # extended add headers.add('Content-Disposition', 'attachment', filename='foo') assert headers['Content-Disposition'] == 'attachment; filename=foo' headers.add('x', 'y', z='"') assert headers['x'] == r'y; z="\""' def test_defaults_and_conversion(self): # defaults headers = self.storage_class([ ('Content-Type', 'text/plain'), ('X-Foo', 'bar'), ('X-Bar', '1'), ('X-Bar', '2') ]) assert headers.getlist('x-bar') == ['1', '2'] assert headers.get('x-Bar') == '1' assert headers.get('Content-Type') == 'text/plain' assert headers.setdefault('X-Foo', 'nope') == 'bar' assert headers.setdefault('X-Bar', 'nope') == '1' assert headers.setdefault('X-Baz', 'quux') == 'quux' assert headers.setdefault('X-Baz', 'nope') == 'quux' headers.pop('X-Baz') # type conversion assert headers.get('x-bar', type=int) == 1 assert headers.getlist('x-bar', type=int) == [1, 2] # list like operations assert headers[0] == ('Content-Type', 'text/plain') assert headers[:1] == self.storage_class([('Content-Type', 'text/plain')]) del headers[:2] del headers[-1] assert headers == self.storage_class([('X-Bar', '1')]) def test_copying(self): a = self.storage_class([('foo', 'bar')]) b = a.copy() a.add('foo', 'baz') assert a.getlist('foo') == ['bar', 'baz'] assert b.getlist('foo') == ['bar'] def test_popping(self): headers = self.storage_class([('a', 1)]) assert headers.pop('a') == 1 assert headers.pop('b', 2) == 2 with pytest.raises(KeyError): headers.pop('c') def test_set_arguments(self): a = self.storage_class() a.set('Content-Disposition', 'useless') a.set('Content-Disposition', 'attachment', filename='foo') assert a['Content-Disposition'] == 'attachment; filename=foo' def test_reject_newlines(self): h = self.storage_class() for variation in 'foo\nbar', 'foo\r\nbar', 'foo\rbar': with pytest.raises(ValueError): h['foo'] = variation with pytest.raises(ValueError): h.add('foo', variation) with pytest.raises(ValueError): h.add('foo', 'test', option=variation) with pytest.raises(ValueError): h.set('foo', variation) with pytest.raises(ValueError): h.set('foo', 'test', option=variation) def test_slicing(self): # there's nothing wrong with these being native strings # Headers doesn't care about the data types h = self.storage_class() h.set('X-Foo-Poo', 'bleh') h.set('Content-Type', 'application/whocares') h.set('X-Forwarded-For', '192.168.0.123') h[:] = [(k, v) for k, v in h if k.startswith(u'X-')] assert list(h) == [ ('X-Foo-Poo', 'bleh'), ('X-Forwarded-For', '192.168.0.123') ] def test_bytes_operations(self): h = self.storage_class() h.set('X-Foo-Poo', 'bleh') h.set('X-Whoops', b'\xff') assert h.get('x-foo-poo', as_bytes=True) == b'bleh' assert h.get('x-whoops', as_bytes=True) == b'\xff' def test_to_wsgi_list(self): h = self.storage_class() h.set(u'Key', u'Value') for key, value in h.to_wsgi_list(): if PY2: strict_eq(key, b'Key') strict_eq(value, b'Value') else: strict_eq(key, u'Key') strict_eq(value, u'Value') class TestEnvironHeaders(object): storage_class = datastructures.EnvironHeaders def test_basic_interface(self): # this happens in multiple WSGI servers because they # use a vary naive way to convert the headers; broken_env = { 'HTTP_CONTENT_TYPE': 'text/html', 'CONTENT_TYPE': 'text/html', 'HTTP_CONTENT_LENGTH': '0', 'CONTENT_LENGTH': '0', 'HTTP_ACCEPT': '*', 'wsgi.version': (1, 0) } headers = self.storage_class(broken_env) assert headers assert len(headers) == 3 assert sorted(headers) == [ ('Accept', '*'), ('Content-Length', '0'), ('Content-Type', 'text/html') ] assert not self.storage_class({'wsgi.version': (1, 0)}) assert len(self.storage_class({'wsgi.version': (1, 0)})) == 0 def test_return_type_is_unicode(self): # environ contains native strings; we return unicode headers = self.storage_class({ 'HTTP_FOO': '\xe2\x9c\x93', 'CONTENT_TYPE': 'text/plain', }) assert headers['Foo'] == u"\xe2\x9c\x93" assert isinstance(headers['Foo'], text_type) assert isinstance(headers['Content-Type'], text_type) iter_output = dict(iter(headers)) assert iter_output['Foo'] == u"\xe2\x9c\x93" assert isinstance(iter_output['Foo'], text_type) assert isinstance(iter_output['Content-Type'], text_type) def test_bytes_operations(self): foo_val = '\xff' h = self.storage_class({ 'HTTP_X_FOO': foo_val }) assert h.get('x-foo', as_bytes=True) == b'\xff' assert h.get('x-foo') == u'\xff' class TestHeaderSet(object): storage_class = datastructures.HeaderSet def test_basic_interface(self): hs = self.storage_class() hs.add('foo') hs.add('bar') assert 'Bar' in hs assert hs.find('foo') == 0 assert hs.find('BAR') == 1 assert hs.find('baz') < 0 hs.discard('missing') hs.discard('foo') assert hs.find('foo') < 0 assert hs.find('bar') == 0 with pytest.raises(IndexError): hs.index('missing') assert hs.index('bar') == 0 assert hs hs.clear() assert not hs class TestImmutableList(object): storage_class = datastructures.ImmutableList def test_list_hashable(self): t = (1, 2, 3, 4) l = self.storage_class(t) assert hash(t) == hash(l) assert t != l def make_call_asserter(func=None): """Utility to assert a certain number of function calls. :param func: Additional callback for each function call. >>> assert_calls, func = make_call_asserter() >>> with assert_calls(2): func() func() """ calls = [0] @contextmanager def asserter(count, msg=None): calls[0] = 0 yield assert calls[0] == count def wrapped(*args, **kwargs): calls[0] += 1 if func is not None: return func(*args, **kwargs) return asserter, wrapped class TestCallbackDict(object): storage_class = datastructures.CallbackDict def test_callback_dict_reads(self): assert_calls, func = make_call_asserter() initial = {'a': 'foo', 'b': 'bar'} dct = self.storage_class(initial=initial, on_update=func) with assert_calls(0, 'callback triggered by read-only method'): # read-only methods dct['a'] dct.get('a') pytest.raises(KeyError, lambda: dct['x']) 'a' in dct list(iter(dct)) dct.copy() with assert_calls(0, 'callback triggered without modification'): # methods that may write but don't dct.pop('z', None) dct.setdefault('a') def test_callback_dict_writes(self): assert_calls, func = make_call_asserter() initial = {'a': 'foo', 'b': 'bar'} dct = self.storage_class(initial=initial, on_update=func) with assert_calls(8, 'callback not triggered by write method'): # always-write methods dct['z'] = 123 dct['z'] = 123 # must trigger again del dct['z'] dct.pop('b', None) dct.setdefault('x') dct.popitem() dct.update([]) dct.clear() with assert_calls(0, 'callback triggered by failed del'): pytest.raises(KeyError, lambda: dct.__delitem__('x')) with assert_calls(0, 'callback triggered by failed pop'): pytest.raises(KeyError, lambda: dct.pop('x')) class TestCacheControl(object): def test_repr(self): cc = datastructures.RequestCacheControl( [("max-age", "0"), ("private", "True")], ) assert repr(cc) == "" class TestAccept(object): storage_class = datastructures.Accept def test_accept_basic(self): accept = self.storage_class([('tinker', 0), ('tailor', 0.333), ('soldier', 0.667), ('sailor', 1)]) # check __getitem__ on indices assert accept[3] == ('tinker', 0) assert accept[2] == ('tailor', 0.333) assert accept[1] == ('soldier', 0.667) assert accept[0], ('sailor', 1) # check __getitem__ on string assert accept['tinker'] == 0 assert accept['tailor'] == 0.333 assert accept['soldier'] == 0.667 assert accept['sailor'] == 1 assert accept['spy'] == 0 # check quality method assert accept.quality('tinker') == 0 assert accept.quality('tailor') == 0.333 assert accept.quality('soldier') == 0.667 assert accept.quality('sailor') == 1 assert accept.quality('spy') == 0 # check __contains__ assert 'sailor' in accept assert 'spy' not in accept # check index method assert accept.index('tinker') == 3 assert accept.index('tailor') == 2 assert accept.index('soldier') == 1 assert accept.index('sailor') == 0 with pytest.raises(ValueError): accept.index('spy') # check find method assert accept.find('tinker') == 3 assert accept.find('tailor') == 2 assert accept.find('soldier') == 1 assert accept.find('sailor') == 0 assert accept.find('spy') == -1 # check to_header method assert accept.to_header() == \ 'sailor,soldier;q=0.667,tailor;q=0.333,tinker;q=0' # check best_match method assert accept.best_match(['tinker', 'tailor', 'soldier', 'sailor'], default=None) == 'sailor' assert accept.best_match(['tinker', 'tailor', 'soldier'], default=None) == 'soldier' assert accept.best_match(['tinker', 'tailor'], default=None) == \ 'tailor' assert accept.best_match(['tinker'], default=None) is None assert accept.best_match(['tinker'], default='x') == 'x' def test_accept_wildcard(self): accept = self.storage_class([('*', 0), ('asterisk', 1)]) assert '*' in accept assert accept.best_match(['asterisk', 'star'], default=None) == \ 'asterisk' assert accept.best_match(['star'], default=None) is None @pytest.mark.skipif(True, reason='Werkzeug doesn\'t respect specificity.') def test_accept_wildcard_specificity(self): accept = self.storage_class([('asterisk', 0), ('star', 0.5), ('*', 1)]) assert accept.best_match(['star', 'asterisk'], default=None) == 'star' assert accept.best_match(['asterisk', 'star'], default=None) == 'star' assert accept.best_match(['asterisk', 'times'], default=None) == \ 'times' assert accept.best_match(['asterisk'], default=None) is None class TestFileStorage(object): storage_class = datastructures.FileStorage def test_mimetype_always_lowercase(self): file_storage = self.storage_class(content_type='APPLICATION/JSON') assert file_storage.mimetype == 'application/json' def test_bytes_proper_sentinel(self): # ensure we iterate over new lines and don't enter into an infinite loop import io unicode_storage = self.storage_class(io.StringIO(u"one\ntwo")) for idx, line in enumerate(unicode_storage): assert idx < 2 assert idx == 1 binary_storage = self.storage_class(io.BytesIO(b"one\ntwo")) for idx, line in enumerate(binary_storage): assert idx < 2 assert idx == 1