1##############################################################################
2#
3# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4# All Rights Reserved.
5#
6# This software is subject to the provisions of the Zope Public License,
7# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11# FOR A PARTICULAR PURPOSE.
12#
13##############################################################################
14"""Field Properties tests
15"""
16
17import unittest
18
19
20class _Base(unittest.TestCase):
21
22    def _makeOne(self, field=None, name=None):
23        from zope.schema import Text
24        if field is None:
25            field = Text(__name__='testing')
26        if name is None:
27            return self._getTargetClass()(field)
28        return self._getTargetClass()(field, name)
29
30
31class _Integration(object):
32
33    def _makeImplementer(self):
34        schema = _getSchema()
35        class _Implementer(object):
36            title = self._makeOne(schema['title'])
37            weight = self._makeOne(schema['weight'])
38            code = self._makeOne(schema['code'])
39            date = self._makeOne(schema['date'])
40        return _Implementer()
41
42    def test_basic(self):
43        from zope.schema._compat import b
44        from zope.schema._compat import u
45        from zope.schema.interfaces import ValidationError
46        c = self._makeImplementer()
47        self.assertEqual(c.title, u('say something'))
48        self.assertEqual(c.weight, None)
49        self.assertEqual(c.code, b('xxxxxx'))
50        self.assertRaises(ValidationError, setattr, c, 'title', b('foo'))
51        self.assertRaises(ValidationError, setattr, c, 'weight', b('foo'))
52        self.assertRaises(ValidationError, setattr, c, 'weight', -1.0)
53        self.assertRaises(ValidationError, setattr, c, 'weight', 2)
54        self.assertRaises(ValidationError, setattr, c, 'code', -1)
55        self.assertRaises(ValidationError, setattr, c, 'code', b('xxxx'))
56        self.assertRaises(ValidationError, setattr, c, 'code', u('xxxxxx'))
57
58        c.title = u('c is good')
59        c.weight = 10.0
60        c.code = b('abcdef')
61
62        self.assertEqual(c.title, u('c is good'))
63        self.assertEqual(c.weight, 10)
64        self.assertEqual(c.code, b('abcdef'))
65
66    def test_readonly(self):
67        c = self._makeImplementer()
68        # The date should be only settable once
69        c.date = 0.0
70        # Setting the value a second time should fail.
71        self.assertRaises(ValueError, setattr, c, 'date', 1.0)
72
73
74class FieldPropertyTests(_Base, _Integration):
75
76    def _getTargetClass(self):
77        from zope.schema.fieldproperty import FieldProperty
78        return FieldProperty
79
80    def test_ctor_defaults(self):
81        from zope.schema import Text
82        field = Text(__name__='testing')
83        cname = self._getTargetClass().__name__
84        prop = self._makeOne(field)
85        self.assertTrue(getattr(prop, '_%s__field' % cname) is field)
86        self.assertEqual(getattr(prop, '_%s__name' % cname), 'testing')
87        self.assertEqual(prop.__name__, 'testing')
88        self.assertEqual(prop.description, field.description)
89        self.assertEqual(prop.default, field.default)
90        self.assertEqual(prop.readonly, field.readonly)
91        self.assertEqual(prop.required, field.required)
92
93    def test_ctor_explicit(self):
94        from zope.schema import Text
95        from zope.schema._compat import u
96        field = Text(__name__='testing',
97                     description=u('DESCRIPTION'),
98                     default=u('DEFAULT'),
99                     readonly=True,
100                     required=True,
101                    )
102        cname = self._getTargetClass().__name__
103        prop = self._makeOne(field, name='override')
104        self.assertTrue(getattr(prop, '_%s__field' % cname) is field)
105        self.assertEqual(getattr(prop, '_%s__name' % cname), 'override')
106        self.assertEqual(prop.description, field.description)
107        self.assertEqual(prop.default, field.default)
108        self.assertEqual(prop.readonly, field.readonly)
109        self.assertEqual(prop.required, field.required)
110
111    def test___get___from_class(self):
112        prop = self._makeOne()
113        class Foo(object):
114            testing = prop
115        self.assertTrue(Foo.testing is prop)
116
117    def test___get___from_instance_pseudo_field_wo_default(self):
118        class _Faux(object):
119            def bind(self, other):
120                return self
121        prop = self._makeOne(_Faux(), 'nonesuch')
122        class Foo(object):
123            testing = prop
124        foo = Foo()
125        self.assertRaises(AttributeError, getattr, foo, 'testing')
126
127    def test___get___from_instance_miss_uses_field_default(self):
128        prop = self._makeOne()
129        class Foo(object):
130            testing = prop
131        foo = Foo()
132        self.assertEqual(foo.testing, None)
133
134    def test___get___from_instance_hit(self):
135        prop = self._makeOne(name='other')
136        class Foo(object):
137            testing = prop
138        foo = Foo()
139        foo.other = '123'
140        self.assertEqual(foo.testing, '123')
141
142    def test___get___from_instance_hit_after_bind(self):
143        class _Faux(object):
144            default = '456'
145            def bind(self, other):
146                return self
147        prop = self._makeOne(_Faux(), 'testing')
148        class Foo(object):
149            testing = prop
150        foo = Foo()
151        self.assertEqual(foo.testing, '456')
152
153    def test___set___not_readonly(self):
154        class _Faux(object):
155            readonly = False
156            default = '456'
157            def bind(self, other):
158                return self
159        faux = _Faux()
160        _validated = []
161        faux.validate = _validated.append
162        prop = self._makeOne(faux, 'testing')
163        class Foo(object):
164            testing = prop
165        foo = Foo()
166        foo.testing = '123'
167        self.assertEqual(foo.__dict__['testing'], '123')
168
169    def test___set___w_readonly_not_already_set(self):
170        class _Faux(object):
171            readonly = True
172            default = '456'
173            def bind(self, other):
174                return self
175        faux = _Faux()
176        _validated = []
177        faux.validate = _validated.append
178        prop = self._makeOne(faux, 'testing')
179        class Foo(object):
180            testing = prop
181        foo = Foo()
182        foo.testing = '123'
183        self.assertEqual(foo.__dict__['testing'], '123')
184        self.assertEqual(_validated, ['123'])
185
186    def test___set___w_readonly_and_already_set(self):
187        class _Faux(object):
188            readonly = True
189            default = '456'
190            def bind(self, other):
191                return self
192        faux = _Faux()
193        _validated = []
194        faux.validate = _validated.append
195        prop = self._makeOne(faux, 'testing')
196        class Foo(object):
197            testing = prop
198        foo = Foo()
199        foo.__dict__['testing'] = '789'
200        self.assertRaises(ValueError, setattr, foo, 'testing', '123')
201        self.assertEqual(_validated, ['123'])
202
203
204class FieldPropertyStoredThroughFieldTests(_Base, _Integration):
205
206    def _getTargetClass(self):
207        from zope.schema.fieldproperty import FieldPropertyStoredThroughField
208        return FieldPropertyStoredThroughField
209
210    def test_ctor_defaults(self):
211        from zope.schema import Text
212        field = Text(__name__='testing')
213        cname = self._getTargetClass().__name__
214        prop = self._makeOne(field)
215        self.assertTrue(isinstance(prop.field, field.__class__))
216        self.assertFalse(prop.field is field)
217        self.assertEqual(prop.field.__name__, '__st_testing_st')
218        self.assertEqual(prop.__name__, '__st_testing_st')
219        self.assertEqual(getattr(prop, '_%s__name' % cname), 'testing')
220        self.assertEqual(prop.description, field.description)
221        self.assertEqual(prop.default, field.default)
222        self.assertEqual(prop.readonly, field.readonly)
223        self.assertEqual(prop.required, field.required)
224
225    def test_ctor_explicit(self):
226        from zope.schema import Text
227        from zope.schema._compat import u
228        field = Text(__name__='testing',
229                     description=u('DESCRIPTION'),
230                     default=u('DEFAULT'),
231                     readonly=True,
232                     required=True,
233                    )
234        cname = self._getTargetClass().__name__
235        prop = self._makeOne(field, name='override')
236        self.assertTrue(isinstance(prop.field, field.__class__))
237        self.assertFalse(prop.field is field)
238        self.assertEqual(prop.field.__name__, '__st_testing_st')
239        self.assertEqual(prop.__name__, '__st_testing_st')
240        self.assertEqual(getattr(prop, '_%s__name' % cname), 'override')
241        self.assertEqual(prop.description, field.description)
242        self.assertEqual(prop.default, field.default)
243        self.assertEqual(prop.readonly, field.readonly)
244        self.assertEqual(prop.required, field.required)
245
246    def test_setValue(self):
247        from zope.schema import Text
248        class Foo(object):
249            pass
250        foo = Foo()
251        prop = self._makeOne()
252        field = Text(__name__='testing')
253        prop.setValue(foo, field, '123')
254        self.assertEqual(foo.testing, '123')
255
256    def test_getValue_miss(self):
257        from zope.schema import Text
258        from zope.schema.fieldproperty import _marker
259        class Foo(object):
260            pass
261        foo = Foo()
262        prop = self._makeOne()
263        field = Text(__name__='testing')
264        value = prop.getValue(foo, field)
265        self.assertTrue(value is _marker)
266
267    def test_getValue_hit(self):
268        from zope.schema import Text
269        class Foo(object):
270            pass
271        foo = Foo()
272        foo.testing = '123'
273        prop = self._makeOne()
274        field = Text(__name__='testing')
275        value = prop.getValue(foo, field)
276        self.assertEqual(value, '123')
277
278    def test_queryValue_miss(self):
279        from zope.schema import Text
280        class Foo(object):
281            pass
282        foo = Foo()
283        prop = self._makeOne()
284        field = Text(__name__='testing')
285        default = object()
286        value = prop.queryValue(foo, field, default)
287        self.assertTrue(value is default)
288
289    def test_queryValue_hit(self):
290        from zope.schema import Text
291        class Foo(object):
292            pass
293        foo = Foo()
294        foo.testing = '123'
295        prop = self._makeOne()
296        field = Text(__name__='testing')
297        default = object()
298        value = prop.queryValue(foo, field, default)
299        self.assertEqual(value, '123')
300
301    def test___get___from_class(self):
302        prop = self._makeOne()
303        class Foo(object):
304            testing = prop
305        self.assertTrue(Foo.testing is prop)
306
307    def test___get___from_instance_pseudo_field_wo_default(self):
308        class _Faux(object):
309            __name__ = 'Faux'
310            def bind(self, other):
311                return self
312            def query(self, inst, default):
313                return default
314        prop = self._makeOne(_Faux(), 'nonesuch')
315        class Foo(object):
316            testing = prop
317        foo = Foo()
318        self.assertRaises(AttributeError, getattr, foo, 'testing')
319
320    def test___get___from_instance_miss_uses_field_default(self):
321        prop = self._makeOne()
322        class Foo(object):
323            testing = prop
324        foo = Foo()
325        self.assertEqual(foo.testing, None)
326
327    def test___get___from_instance_hit(self):
328        from zope.schema import Text
329        field = Text(__name__='testing')
330        prop = self._makeOne(field, name='other')
331        class Foo(object):
332            testing = prop
333        foo = Foo()
334        foo.__dict__['__st_testing_st'] = '456'
335        foo.other = '123'
336        self.assertEqual(foo.testing, '456')
337
338    def test___set___not_readonly(self):
339        class _Faux(object):
340            __name__ = 'Faux'
341            readonly = False
342            default = '456'
343            def bind(self, other):
344                return self
345            def set(self, inst, value):
346                setattr(inst, 'faux', value)
347        faux = _Faux()
348        _validated = []
349        faux.validate = _validated.append
350        prop = self._makeOne(faux, 'testing')
351        class Foo(object):
352            testing = prop
353        foo = Foo()
354        foo.testing = '123'
355        self.assertEqual(foo.__dict__['faux'], '123')
356        self.assertEqual(_validated, ['123'])
357
358    def test___set___w_readonly_not_already_set(self):
359        class _Faux(object):
360            __name__ = 'Faux'
361            readonly = True
362            default = '456'
363            def bind(self, other):
364                return self
365            def query(self, inst, default):
366                return default
367            def set(self, inst, value):
368                if self.readonly:
369                    raise ValueError
370                setattr(inst, 'faux', value)
371        faux = _Faux()
372        _validated = []
373        faux.validate = _validated.append
374        prop = self._makeOne(faux, 'testing')
375        class Foo(object):
376            testing = prop
377        foo = Foo()
378        foo.testing = '123'
379        self.assertEqual(foo.__dict__['faux'], '123')
380        self.assertEqual(_validated, ['123'])
381
382    def test___set___w_readonly_and_already_set(self):
383        class _Faux(object):
384            __name__ = 'Faux'
385            readonly = True
386            default = '456'
387            def bind(self, other):
388                return self
389            def query(self, inst, default):
390                return '789'
391        faux = _Faux()
392        _validated = []
393        faux.validate = _validated.append
394        prop = self._makeOne(faux, 'testing')
395        class Foo(object):
396            testing = prop
397        foo = Foo()
398        foo.__dict__['testing'] = '789'
399        self.assertRaises(ValueError, setattr, foo, 'testing', '123')
400
401
402def _getSchema():
403    from zope.interface import Interface
404    from zope.schema import Bytes
405    from zope.schema import Float
406    from zope.schema import Text
407    from zope.schema._compat import b
408    from zope.schema._compat import u
409
410    class Schema(Interface):
411        title = Text(description=u("Short summary"),
412                     default=u('say something'))
413        weight = Float(min=0.0)
414        code = Bytes(min_length=6, max_length=6, default=b('xxxxxx'))
415        date = Float(title=u('Date'), readonly=True)
416
417    return Schema
418
419
420def test_suite():
421    return unittest.TestSuite((
422        unittest.makeSuite(FieldPropertyTests),
423        unittest.makeSuite(FieldPropertyStoredThroughFieldTests),
424    ))
425