• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / Products.GenericSetup / 9927778353

12 Jun 2024 06:30AM UTC coverage: 93.296% (+0.06%) from 93.233%
9927778353

push

github

web-flow
- Add support for Python 3.12.  - Drop support for Python 3.7. (#130)

1806 of 2123 branches covered (85.07%)

Branch coverage included in aggregate %.

127 of 134 new or added lines in 4 files covered. (94.78%)

2 existing lines in 2 files now uncovered.

9285 of 9765 relevant lines covered (95.08%)

0.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.65
/src/Products/GenericSetup/tests/test_utils.py
1
##############################################################################
2
#
3
# Copyright (c) 2004 Zope Foundation and Contributors.
4
#
5
# This software is subject to the provisions of the Zope Public License,
6
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
7
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10
# FOR A PARTICULAR PURPOSE.
11
#
12
##############################################################################
13
""" GenericSetup.utils unit tests
14
"""
15

16
import unittest
1✔
17

18
from DateTime.DateTime import DateTime
1✔
19
from Testing.ZopeTestCase import ZopeTestCase
1✔
20
from Testing.ZopeTestCase import installProduct
1✔
21

22

23
installProduct('GenericSetup')
1✔
24

25
_TESTED_PROPERTIES = (
1✔
26
    {
27
        'id': 'foo_boolean',
28
        'type': 'boolean',
29
        'mode': 'wd'
30
    },
31
    {
32
        'id': 'foo_date',
33
        'type': 'date',
34
        'mode': 'wd'
35
    },
36
    {
37
        'id': 'foo_float',
38
        'type': 'float',
39
        'mode': 'wd'
40
    },
41
    {
42
        'id': 'foo_int',
43
        'type': 'int',
44
        'mode': 'wd'
45
    },
46
    {
47
        'id': 'foo_lines',
48
        'type': 'lines',
49
        'mode': 'wd'
50
    },
51
    {
52
        'id': 'foo_long',
53
        'type': 'long',
54
        'mode': 'wd'
55
    },
56
    {
57
        'id': 'foo_string',
58
        'type': 'string',
59
        'mode': 'wd'
60
    },
61
    {
62
        'id': 'foo_text',
63
        'type': 'text',
64
        'mode': 'wd'
65
    },
66
    {
67
        'id': 'foo_tokens',
68
        'type': 'tokens',
69
        'mode': 'wd'
70
    },
71
    {
72
        'id': 'foo_selection',
73
        'type': 'selection',
74
        'select_variable': 'foobarbaz',
75
        'mode': 'wd'
76
    },
77
    {
78
        'id': 'foo_mselection',
79
        'type': 'multiple selection',
80
        'select_variable': 'foobarbaz',
81
        'mode': 'wd'
82
    },
83
    {
84
        'id': 'foo_boolean0',
85
        'type': 'boolean',
86
        'mode': 'wd'
87
    },
88
    {
89
        'id': 'foo_date_naive',
90
        'type': 'date',
91
        'mode': 'wd'
92
    },
93
    {
94
        'id': 'foo_ro',
95
        'type': 'string',
96
        'mode': ''
97
    },
98
    {
99
        'id': 'foo_boolean_nodel',
100
        'type': 'boolean',
101
        'mode': 'w'
102
    },
103
    {
104
        'id': 'foo_date_nodel',
105
        'type': 'date',
106
        'mode': 'w'
107
    },
108
    {
109
        'id': 'foo_float_nodel',
110
        'type': 'float',
111
        'mode': 'w'
112
    },
113
    {
114
        'id': 'foo_int_nodel',
115
        'type': 'int',
116
        'mode': 'w'
117
    },
118
)
119

120
_EMPTY_PROPERTY_EXPORT = b"""\
1✔
121
<?xml version="1.0" encoding="utf-8"?>
122
<dummy>
123
 <property name="foo_boolean" type="boolean">False</property>
124
 <property name="foo_date" type="date">1970/01/01 00:00:00 UTC</property>
125
 <property name="foo_float" type="float">0.0</property>
126
 <property name="foo_int" type="int">0</property>
127
 <property name="foo_lines" type="lines"/>
128
 <property name="foo_long" type="long">0</property>
129
 <property name="foo_string" type="string"></property>
130
 <property name="foo_text" type="text"></property>
131
 <property name="foo_tokens" type="tokens"/>
132
 <property name="foo_selection" select_variable="foobarbaz"
133
    type="selection"></property>
134
 <property name="foo_mselection" select_variable="foobarbaz"
135
    type="multiple selection"/>
136
 <property name="foo_boolean0" type="boolean">False</property>
137
 <property name="foo_date_naive" type="date">1970/01/01 00:00:00</property>
138
 <property name="foo_boolean_nodel">False</property>
139
 <property name="foo_date_nodel">1970/01/01 00:00:00 UTC</property>
140
 <property name="foo_float_nodel">0.0</property>
141
 <property name="foo_int_nodel">0</property>
142
</dummy>
143
"""
144

145
_NONE_PROPERTY_EXPORT = b"""\
1✔
146
<?xml version="1.0" encoding="utf-8"?>
147
<dummy>
148
 <property name="foo_boolean" type="boolean">False</property>
149
 <property name="foo_date" type="date">1970/01/01 00:00:00 UTC</property>
150
 <property name="foo_float" type="float">0.0</property>
151
 <property name="foo_int" type="int">0</property>
152
 <property name="foo_lines" type="lines"/>
153
 <property name="foo_long" type="long">0</property>
154
 <property name="foo_string" type="string"></property>
155
 <property name="foo_tokens" type="tokens"/>
156
 <property name="foo_selection" select_variable="foobarbaz"
157
    type="selection"></property>
158
 <property name="foo_mselection" select_variable="foobarbaz"
159
    type="multiple selection"/>
160
 <property name="foo_boolean0" type="boolean">False</property>
161
 <property name="foo_date_naive" type="date">1970/01/01 00:00:00</property>
162
 <property name="foo_boolean_nodel">False</property>
163
 <property name="foo_date_nodel">1970/01/01 00:00:00 UTC</property>
164
 <property name="foo_float_nodel">0.0</property>
165
 <property name="foo_int_nodel">0</property>
166
</dummy>
167
"""
168

169
_NORMAL_PROPERTY_EXPORT = """\
1✔
170
<?xml version="1.0" encoding="utf-8"?>
171
<dummy>
172
 <property name="foo_boolean" type="boolean">True</property>
173
 <property name="foo_date" type="date">2000/01/01 00:00:00 UTC</property>
174
 <property name="foo_float" type="float">1.1</property>
175
 <property name="foo_int" type="int">1</property>
176
 <property name="foo_lines" type="lines">
177
  <element value="Foo"/>
178
  <element value="Lines"/>
179
  <element value="\xfcbrigens"/>
180
 </property>
181
 <property name="foo_long" type="long">1</property>
182
 <property name="foo_string" type="string">Foo String</property>
183
 <property name="foo_text" type="text">Foo
184
  Text</property>
185
 <property name="foo_tokens" type="tokens">
186
  <element value="Foo"/>
187
  <element value="Tokens"/>
188
 </property>
189
 <property name="foo_selection" select_variable="foobarbaz"
190
    type="selection">Foo</property>
191
 <property name="foo_mselection" select_variable="foobarbaz"
192
    type="multiple selection">
193
  <element value="Foo"/>
194
  <element value="Baz"/>
195
 </property>
196
 <property name="foo_boolean0" type="boolean">False</property>
197
 <property name="foo_date_naive" type="date">2000/01/01 00:00:00</property>
198
 <property name="foo_boolean_nodel">True</property>
199
 <property name="foo_date_nodel">2000/01/01 00:00:00 UTC</property>
200
 <property name="foo_float_nodel">3.1415</property>
201
 <property name="foo_int_nodel">1789</property>
202
</dummy>
203
""".encode()
204

205
_NORMAL_PROPERTY_EXPORT_ISO_8859_1 = b"""\
1✔
206
<?xml version="1.0" encoding="iso-8859-1"?>
207
<dummy>
208
 <property name="foo_boolean" type="boolean">True</property>
209
 <property name="foo_date" type="date">2000/01/01 00:00:00 UTC</property>
210
 <property name="foo_float" type="float">1.1</property>
211
 <property name="foo_int" type="int">1</property>
212
 <property name="foo_lines" type="lines">
213
  <element value="Foo"/>
214
  <element value="Lines"/>
215
  <element value="\xfcbrigens"/>
216
 </property>
217
 <property name="foo_long" type="long">1</property>
218
 <property name="foo_string" type="string">Foo String</property>
219
 <property name="foo_text" type="text">Foo
220
  Text</property>
221
 <property name="foo_tokens" type="tokens">
222
  <element value="Foo"/>
223
  <element value="Tokens"/>
224
 </property>
225
 <property name="foo_selection" select_variable="foobarbaz"
226
    type="selection">Foo</property>
227
 <property name="foo_mselection" select_variable="foobarbaz"
228
    type="multiple selection">
229
  <element value="Foo"/>
230
  <element value="Baz"/>
231
 </property>
232
 <property name="foo_boolean0" type="boolean">False</property>
233
 <property name="foo_date_naive" type="date">2000/01/01 00:00:00</property>
234
 <property name="foo_boolean_nodel">True</property>
235
 <property name="foo_date_nodel">2000/01/01 00:00:00 UTC</property>
236
 <property name="foo_float_nodel">3.1415</property>
237
 <property name="foo_int_nodel">1789</property>
238
</dummy>
239
"""
240

241
_NORMAL_PROPERTY_EXPORT_OLD = b"""\
1✔
242
<?xml version="1.0"?>
243
<dummy>
244
 <property name="foo_boolean" type="boolean">True</property>
245
 <property name="foo_date" type="date">2000/01/01 00:00:00 UTC</property>
246
 <property name="foo_float" type="float">1.1</property>
247
 <property name="foo_int" type="int">1</property>
248
 <property name="foo_lines" type="lines">
249
  <element value="Foo"/>
250
  <element value="Lines"/>
251
  <element value="\xc3\xbcbrigens"/>
252
 </property>
253
 <property name="foo_long" type="long">1</property>
254
 <property name="foo_string" type="string">Foo String</property>
255
 <property name="foo_text" type="text">Foo
256
  Text</property>
257
 <property name="foo_tokens" type="tokens">
258
  <element value="Foo"/>
259
  <element value="Tokens"/>
260
 </property>
261
 <property name="foo_selection" select_variable="foobarbaz"
262
    type="selection">Foo</property>
263
 <property name="foo_mselection" select_variable="foobarbaz"
264
    type="multiple selection">
265
  <element value="Foo"/>
266
  <element value="Baz"/>
267
 </property>
268
 <property name="foo_boolean0" type="boolean">False</property>
269
 <property name="foo_date_naive" type="date">2000/01/01 00:00:00</property>
270
 <property name="foo_boolean_nodel">True</property>
271
 <property name="foo_date_nodel">2000/01/01 00:00:00 UTC</property>
272
 <property name="foo_float_nodel">3.1415</property>
273
 <property name="foo_int_nodel">1789</property>
274
</dummy>
275
"""
276

277
_FIXED_PROPERTY_EXPORT = """\
1✔
278
<?xml version="1.0" encoding="utf-8"?>
279
<dummy>
280
 <property name="foo_boolean">True</property>
281
 <property name="foo_date">2000/01/01 00:00:00 UTC</property>
282
 <property name="foo_float">1.1</property>
283
 <property name="foo_int">1</property>
284
 <property name="foo_lines">
285
  <element value="Foo"/>
286
  <element value="Lines"/>
287
 <element value="\xfcbrigens"/>
288
 </property>
289
 <property name="foo_long">1</property>
290
 <property name="foo_string">Foo String</property>
291
 <property name="foo_text">Foo
292
  Text</property>
293
 <property name="foo_tokens">
294
  <element value="Foo"/>
295
  <element value="Tokens"/></property>
296
 <property name="foo_selection" type="selection"
297
    select_variable="foobarbaz">Foo</property>
298
 <property name="foo_mselection">
299
  <element value="Foo"/>
300
  <element value="Baz"/>
301
 </property>
302
 <property name="foo_boolean0">False</property>
303
 <property name="foo_date_naive">2000/01/01 00:00:00</property>
304
 <property name="foo_boolean_nodel">True</property>
305
 <property name="foo_date_nodel">2000/01/01 00:00:00 UTC</property>
306
 <property name="foo_float_nodel">3.1415</property>
307
 <property name="foo_int_nodel">1789</property>
308
</dummy>
309
""".encode()
310

311
_SPECIAL_IMPORT = b"""\
1✔
312
<?xml version="1.0" encoding="utf-8"?>
313
<dummy>
314
 <!-- ignore comment, import 0 as False -->
315
 <property name="foo_boolean0" type="boolean">0</property>
316
</dummy>
317
"""
318

319
_I18N_IMPORT = b"""\
1✔
320
<?xml version="1.0" encoding="utf-8"?>
321
<dummy xmlns:i18n="http://xml.zope.org/namespaces/i18n"
322
   i18n:domain="dummy_domain">
323
 <property name="foo_string" i18n:translate="">Foo String</property>
324
</dummy>
325
"""
326

327
_NOPURGE_IMPORT = b"""\
1✔
328
<?xml version="1.0" encoding="utf-8"?>
329
<dummy>
330
 <property name="lines1">
331
  <element value="Foo"/>
332
  <element value="Bar"/>
333
 </property>
334
 <property name="lines2" purge="True">
335
  <element value="Foo"/>
336
  <element value="Bar"/>
337
 </property>
338
 <property name="lines3" purge="False">
339
  <element value="Foo"/>
340
  <element value="Bar"/>
341
 </property>
342
</dummy>
343
"""
344

345
_REMOVE_ELEMENT_IMPORT = b"""\
1✔
346
<?xml version="1.0" encoding="utf-8"?>
347
<dummy>
348
 <property name="lines1" purge="False">
349
   <element value="Foo" remove="True" />
350
   <element value="Bar" remove="False" />
351
  </property>
352
 <property name="lines2" purge="False">
353
   <element value="Foo" remove="True" />
354
  </property>
355
</dummy>
356
"""
357

358
_NORMAL_MARKER_EXPORT = b"""\
1✔
359
<?xml version="1.0" encoding="utf-8"?>
360
<dummy>
361
 <marker name="Products.GenericSetup.testing.IDummyMarker"/>
362
</dummy>
363
"""
364

365
_ADD_IMPORT = b"""\
1✔
366
<?xml version="1.0" encoding="utf-8"?>
367
<dummy>
368
 <object name="history" meta_type="Generic Setup Tool"/>
369
 <object name="future" meta_type="Generic Setup Tool"/>
370
</dummy>
371
"""
372

373
_REMOVE_IMPORT = b"""\
1✔
374
<?xml version="1.0" encoding="utf-8"?>
375
<dummy>
376
 <object name="history" remove="True"/>
377
 <object name="future" remove="False"/>
378
</dummy>
379
"""
380

381
_ADD_PROPERTY_IMPORT = b"""\
1✔
382
<?xml version="1.0" encoding="utf-8"?>
383
<dummy>
384
 <property name="line1" type="string">Line 1</property>
385
 <property name="line2" type="string">Line 2</property>
386
</dummy>
387
"""
388

389
_REMOVE_PROPERTY_IMPORT = b"""\
1✔
390
<?xml version="1.0" encoding="utf-8"?>
391
<dummy>
392
 <property name="line1" remove="True"/>
393
 <property name="line2" type="string" remove="False"/>
394
</dummy>
395
"""
396

397

398
def _getDocumentElement(text):
1✔
399
    from xml.dom.minidom import parseString
1✔
400
    return parseString(text).documentElement
1✔
401

402

403
def _testFunc(*args, **kw):
1✔
404
    """ This is a test.
405

406
    This is only a test.
407
    """
408

409

410
_TEST_FUNC_NAME = 'Products.GenericSetup.tests.test_utils._testFunc'
1✔
411

412

413
class Whatever:
1✔
414
    pass
1✔
415

416

417
_WHATEVER_NAME = 'Products.GenericSetup.tests.test_utils.Whatever'
1✔
418

419
whatever_inst = Whatever()
1✔
420
whatever_inst.__name__ = 'whatever_inst'
1✔
421

422
_WHATEVER_INST_NAME = 'Products.GenericSetup.tests.test_utils.whatever_inst'
1✔
423

424

425
class UtilsTests(unittest.TestCase):
1✔
426

427
    def test__getDottedName_simple(self):
1✔
428

429
        from ..utils import _getDottedName
1✔
430

431
        self.assertEqual(_getDottedName(_testFunc), _TEST_FUNC_NAME)
1✔
432

433
    def test__getDottedName_string(self):
1✔
434

435
        from ..utils import _getDottedName
1✔
436

437
        self.assertEqual(_getDottedName(_TEST_FUNC_NAME), _TEST_FUNC_NAME)
1✔
438

439
    def test__getDottedName_unicode(self):
1✔
440

441
        from ..utils import _getDottedName
1✔
442

443
        dotted = '%s' % _TEST_FUNC_NAME
1✔
444
        self.assertEqual(_getDottedName(dotted), _TEST_FUNC_NAME)
1✔
445
        self.assertEqual(type(_getDottedName(dotted)), str)
1✔
446

447
    def test__getDottedName_class(self):
1✔
448

449
        from ..utils import _getDottedName
1✔
450

451
        self.assertEqual(_getDottedName(Whatever), _WHATEVER_NAME)
1✔
452

453
    def test__getDottedName_inst(self):
1✔
454

455
        from ..utils import _getDottedName
1✔
456

457
        self.assertEqual(_getDottedName(whatever_inst), _WHATEVER_INST_NAME)
1✔
458

459
    def test__getDottedName_noname(self):
1✔
460

461
        from ..utils import _getDottedName
1✔
462

463
        class Doh:
1✔
464
            pass
1✔
465

466
        doh = Doh()
1✔
467
        self.assertRaises(ValueError, _getDottedName, doh)
1✔
468

469
    def test__version_for_print(self):
1✔
470
        from ..utils import _version_for_print as vfp
1✔
471

472
        self.assertEqual(vfp('1000'), '1000')
1✔
473
        self.assertEqual(vfp(('1000', )), '1000')
1✔
474
        self.assertEqual(vfp(('1', '2', '3')), '1.2.3')
1✔
475
        self.assertEqual(vfp(42), '42')
1✔
476
        self.assertEqual(vfp('unknown'), 'unknown')
1✔
477
        self.assertEqual(vfp(None), 'None')
1✔
478

479

480
class PropertyManagerHelpersTests(unittest.TestCase):
1✔
481

482
    def _getTargetClass(self):
1✔
483
        from ..utils import PropertyManagerHelpers
1✔
484

485
        return PropertyManagerHelpers
1✔
486

487
    def _makeOne(self, context=None, environ=None):
1✔
488
        from ..testing import DummySetupEnviron
1✔
489
        from ..utils import NodeAdapterBase
1✔
490

491
        class Foo(self._getTargetClass(), NodeAdapterBase):
1✔
492
            pass
1✔
493

494
        if context is None:
1✔
495
            context = self._makeContext()
1✔
496

497
        if environ is None:
1!
498
            environ = DummySetupEnviron()
1✔
499

500
        return Foo(context, environ)
1✔
501

502
    def _getContextClass(self):
1✔
503
        from OFS.PropertyManager import PropertyManager
1✔
504

505
        class DummyContext(PropertyManager):
1✔
506
            _properties = _TESTED_PROPERTIES
1✔
507

508
        return DummyContext
1✔
509

510
    def _makeContext(self):
1✔
511
        obj = self._getContextClass()()
1✔
512
        obj.foobarbaz = ('Foo', 'Bar', 'Baz')
1✔
513
        obj.foo_boolean = False
1✔
514
        obj.foo_date = DateTime('1970/01/01 00:00:00 UTC')
1✔
515
        obj.foo_float = 0.0
1✔
516
        obj.foo_int = 0
1✔
517
        obj.foo_lines = []
1✔
518
        obj.foo_long = 0
1✔
519
        obj.foo_string = ''
1✔
520
        obj.foo_text = ''
1✔
521
        obj.foo_tokens = ()
1✔
522
        obj.foo_selection = ''
1✔
523
        obj.foo_mselection = ()
1✔
524
        obj.foo_boolean0 = 0
1✔
525
        obj.foo_date_naive = DateTime('1970/01/01 00:00:00')
1✔
526
        obj.foo_ro = ''
1✔
527
        obj.foo_boolean_nodel = False
1✔
528
        obj.foo_date_nodel = DateTime('1970/01/01 00:00:00 UTC')
1✔
529
        obj.foo_float_nodel = 0.0
1✔
530
        obj.foo_int_nodel = 0
1✔
531
        return obj
1✔
532

533
    def _getReal(self, obj):
1✔
534
        return obj
1✔
535

536
    def _populate(self, obj):
1✔
537
        obj._updateProperty('foo_boolean', 'True')
1✔
538
        obj._updateProperty('foo_date', '2000/01/01 00:00:00 UTC')
1✔
539
        obj._updateProperty('foo_float', '1.1')
1✔
540
        obj._updateProperty('foo_int', '1')
1✔
541
        obj._updateProperty('foo_lines', 'Foo\nLines\n\xfcbrigens')
1✔
542
        obj._updateProperty('foo_long', '1')
1✔
543
        obj._updateProperty('foo_string', 'Foo String')
1✔
544
        obj._updateProperty('foo_text', 'Foo\nText')
1✔
545
        obj._updateProperty('foo_tokens', ('Foo', 'Tokens'))
1✔
546
        obj._updateProperty('foo_selection', 'Foo')
1✔
547
        obj._updateProperty('foo_mselection', ('Foo', 'Baz'))
1✔
548
        obj.foo_boolean0 = 0
1✔
549
        obj._updateProperty('foo_date_naive', '2000/01/01 00:00:00')
1✔
550
        obj._updateProperty('foo_ro', 'RO')
1✔
551
        obj._updateProperty('foo_boolean_nodel', 'True')
1✔
552
        obj._updateProperty('foo_date_nodel', '2000/01/01 00:00:00 UTC')
1✔
553
        obj._updateProperty('foo_float_nodel', '3.1415')
1✔
554
        obj._updateProperty('foo_int_nodel', '1789')
1✔
555

556
    def test__extractProperties_empty(self):
1✔
557
        from ..utils import PrettyDocument
1✔
558
        helpers = self._makeOne()
1✔
559
        doc = helpers._doc = PrettyDocument()
1✔
560
        node = doc.createElement('dummy')
1✔
561
        node.appendChild(helpers._extractProperties())
1✔
562
        doc.appendChild(node)
1✔
563

564
        self.assertEqual(doc.toprettyxml(' '), _EMPTY_PROPERTY_EXPORT)
1✔
565

566
    def test__extractProperties_none(self):
1✔
567
        from ..utils import PrettyDocument
1✔
568
        context = self._makeContext()
1✔
569
        # When a text property is None, in 1.10.0 you get an AttributeError:
570
        # 'NoneType' object has no attribute 'decode'
571
        context.foo_text = None
1✔
572
        helpers = self._makeOne(context=context)
1✔
573
        doc = helpers._doc = PrettyDocument()
1✔
574
        node = doc.createElement('dummy')
1✔
575
        node.appendChild(helpers._extractProperties())
1✔
576
        doc.appendChild(node)
1✔
577

578
        self.assertEqual(doc.toprettyxml(' '), _NONE_PROPERTY_EXPORT)
1✔
579

580
    def test__extractProperties_normal(self):
1✔
581
        from ..utils import PrettyDocument
1✔
582
        helpers = self._makeOne()
1✔
583
        obj = self._getReal(helpers.context)
1✔
584
        self._populate(obj)
1✔
585
        doc = helpers._doc = PrettyDocument()
1✔
586
        node = doc.createElement('dummy')
1✔
587

588
        # The extraction process wants to decode text properties
589
        # to unicode using the default ZPublisher encoding, which
590
        # defaults to iso-8859-15. We force UTF-8 here because we
591
        # forced our properties to be UTF-8 encoded.
592
        helpers._encoding = 'utf-8'
1✔
593
        node.appendChild(helpers._extractProperties())
1✔
594
        doc.appendChild(node)
1✔
595

596
        self.assertEqual(doc.toprettyxml(' '), _NORMAL_PROPERTY_EXPORT)
1✔
597

598
    def test__purgeProperties(self):
1✔
599
        helpers = self._makeOne()
1✔
600
        obj = self._getReal(helpers.context)
1✔
601
        self._populate(obj)
1✔
602
        helpers._purgeProperties()
1✔
603

604
        self.assertEqual(getattr(obj, 'foo_boolean', None), None)
1✔
605
        self.assertEqual(getattr(obj, 'foo_date', None), None)
1✔
606
        self.assertEqual(getattr(obj, 'foo_float', None), None)
1✔
607
        self.assertEqual(getattr(obj, 'foo_int', None), None)
1✔
608
        self.assertEqual(getattr(obj, 'foo_lines', None), None)
1✔
609
        self.assertEqual(getattr(obj, 'foo_long', None), None)
1✔
610
        self.assertEqual(getattr(obj, 'foo_string', None), None)
1✔
611
        self.assertEqual(getattr(obj, 'foo_text', None), None)
1✔
612
        self.assertEqual(getattr(obj, 'foo_tokens', None), None)
1✔
613
        self.assertEqual(getattr(obj, 'foo_selection', None), None)
1✔
614
        self.assertEqual(getattr(obj, 'foo_mselection', None), None)
1✔
615
        self.assertEqual(getattr(obj, 'foo_boolean0', None), None)
1✔
616
        self.assertEqual(getattr(obj, 'foo_date_naive', None), None)
1✔
617
        self.assertEqual(obj.foo_ro, 'RO')
1✔
618
        self.assertEqual(obj.foo_boolean_nodel, False)
1✔
619
        self.assertEqual(obj.foo_date_nodel,
1✔
620
                         DateTime('1970/01/01 00:00:00 UTC'))
621
        self.assertEqual(obj.foo_float_nodel, 0.0)
1✔
622
        self.assertEqual(obj.foo_int_nodel, 0)
1✔
623

624
    def test__initProperties_normal(self):
1✔
625
        from ..utils import PrettyDocument
1✔
626
        helpers = self._makeOne()
1✔
627
        obj = self._getReal(helpers.context)
1✔
628
        node = _getDocumentElement(_NORMAL_PROPERTY_EXPORT)
1✔
629
        helpers._initProperties(node)
1✔
630
        self.assertEqual(type(obj.foo_int), int)
1✔
631
        self.assertEqual(type(obj.foo_string), str)
1✔
632
        self.assertEqual(type(obj.foo_tokens), tuple)
1✔
633
        self.assertEqual(type(obj.foo_tokens[0]), bytes)
1✔
634

635
        doc = helpers._doc = PrettyDocument()
1✔
636
        node = doc.createElement('dummy')
1✔
637
        node.appendChild(helpers._extractProperties())
1✔
638
        doc.appendChild(node)
1✔
639

640
        self.assertEqual(doc.toprettyxml(' '), _NORMAL_PROPERTY_EXPORT)
1✔
641

642
    def test__initProperties_normal_oldxml(self):
1✔
643
        from ..utils import PrettyDocument
1✔
644
        helpers = self._makeOne()
1✔
645
        obj = self._getReal(helpers.context)
1✔
646
        node = _getDocumentElement(_NORMAL_PROPERTY_EXPORT_OLD)
1✔
647
        helpers._initProperties(node)
1✔
648
        self.assertEqual(type(obj.foo_int), int)
1✔
649
        self.assertEqual(type(obj.foo_string), str)
1✔
650
        self.assertEqual(type(obj.foo_tokens), tuple)
1✔
651
        self.assertEqual(type(obj.foo_tokens[0]), bytes)
1✔
652

653
        doc = helpers._doc = PrettyDocument()
1✔
654
        node = doc.createElement('dummy')
1✔
655
        node.appendChild(helpers._extractProperties())
1✔
656
        doc.appendChild(node)
1✔
657

658
        self.assertEqual(doc.toprettyxml(' '), _NORMAL_PROPERTY_EXPORT)
1✔
659

660
    def test__initProperties_normal_iso_8859_1(self):
1✔
661
        from ..utils import PrettyDocument
1✔
662
        helpers = self._makeOne()
1✔
663
        obj = self._getReal(helpers.context)
1✔
664
        node = _getDocumentElement(_NORMAL_PROPERTY_EXPORT_ISO_8859_1)
1✔
665
        # *sigh* The base class does not respect the encoding specified in the
666
        # xml, so we have to be explicit here. In the real world it is in
667
        # responsibility of the subclass.
668
        helpers._encoding = 'iso-8859-1'
1✔
669
        helpers._initProperties(node)
1✔
670
        self.assertEqual(type(obj.foo_int), int)
1✔
671
        self.assertEqual(type(obj.foo_string), str)
1✔
672
        self.assertEqual(type(obj.foo_tokens), tuple)
1✔
673
        self.assertEqual(type(obj.foo_tokens[0]), bytes)
1✔
674

675
        doc = helpers._doc = PrettyDocument()
1✔
676
        node = doc.createElement('dummy')
1✔
677
        node.appendChild(helpers._extractProperties())
1✔
678
        doc.appendChild(node)
1✔
679

680
        self.assertEqual(doc.toprettyxml(' '), _NORMAL_PROPERTY_EXPORT)
1✔
681

682
    def test__initProperties_fixed(self):
1✔
683
        from ..utils import PrettyDocument
1✔
684
        helpers = self._makeOne()
1✔
685
        node = _getDocumentElement(_FIXED_PROPERTY_EXPORT)
1✔
686
        helpers._initProperties(node)
1✔
687

688
        doc = helpers._doc = PrettyDocument()
1✔
689
        node = doc.createElement('dummy')
1✔
690
        node.appendChild(helpers._extractProperties())
1✔
691
        doc.appendChild(node)
1✔
692

693
        self.assertEqual(doc.toprettyxml(' '), _NORMAL_PROPERTY_EXPORT)
1✔
694

695
    def test__initProperties_special(self):
1✔
696
        from ..utils import PrettyDocument
1✔
697
        helpers = self._makeOne()
1✔
698
        node = _getDocumentElement(_SPECIAL_IMPORT)
1✔
699
        helpers._initProperties(node)
1✔
700

701
        doc = helpers._doc = PrettyDocument()
1✔
702
        node = doc.createElement('dummy')
1✔
703
        node.appendChild(helpers._extractProperties())
1✔
704
        doc.appendChild(node)
1✔
705

706
        self.assertEqual(doc.toprettyxml(' '), _EMPTY_PROPERTY_EXPORT)
1✔
707

708
    def test__initProperties_i18n(self):
1✔
709
        helpers = self._makeOne()
1✔
710
        helpers.context.manage_addProperty('i18n_domain', '', 'string')
1✔
711
        node = _getDocumentElement(_I18N_IMPORT)
1✔
712
        helpers._initProperties(node)
1✔
713

714
        self.assertEqual(helpers.context.getProperty('i18n_domain'),
1✔
715
                         'dummy_domain')
716

717
    def test__initProperties_nopurge_base(self):
1✔
718
        helpers = self._makeOne()
1✔
719
        node = _getDocumentElement(_NOPURGE_IMPORT)
1✔
720
        helpers.environ._should_purge = True  # base profile
1✔
721
        obj = helpers.context
1✔
722
        obj._properties = ()
1✔
723
        obj.manage_addProperty('lines1', ('Foo', 'Gee'), 'lines')
1✔
724
        obj.manage_addProperty('lines2', ('Foo', 'Gee'), 'lines')
1✔
725
        obj.manage_addProperty('lines3', ('Foo', 'Gee'), 'lines')
1✔
726
        # Check that the type is what we expect.
727
        # Beware of bytes versus text.
728
        # Since Zope 5.3, lines should contain text.
729
        # See zopefoundation/Products.GenericSetup/issues/109
730
        # An import should not result in bytes instead of text.
731
        is_bytes = isinstance(obj.getProperty('lines1')[0], bytes)
1✔
732
        helpers._initProperties(node)
1✔
733
        if is_bytes:
1!
734
            self.assertEqual(obj.getProperty('lines1'), (b'Foo', b'Bar'))
×
735
            self.assertEqual(obj.getProperty('lines2'), (b'Foo', b'Bar'))
×
NEW
736
            self.assertEqual(obj.getProperty('lines3'),
×
737
                             (b'Gee', b'Foo', b'Bar'))
738
        else:
739
            self.assertEqual(obj.getProperty('lines1'), ('Foo', 'Bar'))
1✔
740
            self.assertEqual(obj.getProperty('lines2'), ('Foo', 'Bar'))
1✔
741
            self.assertEqual(obj.getProperty('lines3'), ('Gee', 'Foo', 'Bar'))
1✔
742

743
    def test__initProperties_nopurge_extension(self):
1✔
744
        helpers = self._makeOne()
1✔
745
        node = _getDocumentElement(_NOPURGE_IMPORT)
1✔
746
        helpers.environ._should_purge = False  # extension profile
1✔
747
        obj = helpers.context
1✔
748
        obj._properties = ()
1✔
749
        obj.manage_addProperty('lines1', ('Foo', 'Gee'), 'lines')
1✔
750
        obj.manage_addProperty('lines2', ('Foo', 'Gee'), 'lines')
1✔
751
        obj.manage_addProperty('lines3', ('Foo', 'Gee'), 'lines')
1✔
752
        is_bytes = isinstance(obj.getProperty('lines1')[0], bytes)
1✔
753
        helpers._initProperties(node)
1✔
754

755
        if is_bytes:
1!
756
            self.assertEqual(obj.getProperty('lines1'), (b'Foo', b'Bar'))
×
757
            self.assertEqual(obj.getProperty('lines2'), (b'Foo', b'Bar'))
×
NEW
758
            self.assertEqual(obj.getProperty('lines3'),
×
759
                             (b'Gee', b'Foo', b'Bar'))
760
        else:
761
            self.assertEqual(obj.getProperty('lines1'), ('Foo', 'Bar'))
1✔
762
            self.assertEqual(obj.getProperty('lines2'), ('Foo', 'Bar'))
1✔
763
            self.assertEqual(obj.getProperty('lines3'), ('Gee', 'Foo', 'Bar'))
1✔
764

765
    def test_initProperties_remove_elements(self):
1✔
766
        helpers = self._makeOne()
1✔
767
        node = _getDocumentElement(_REMOVE_ELEMENT_IMPORT)
1✔
768
        helpers.environ._should_purge = False  # extension profile
1✔
769
        obj = helpers.context
1✔
770
        obj._properties = ()
1✔
771
        obj.manage_addProperty('lines1', ('Foo', 'Gee'), 'lines')
1✔
772
        obj.manage_addProperty('lines2', ('Foo', 'Gee'), 'lines')
1✔
773
        is_bytes = isinstance(obj.getProperty('lines1')[0], bytes)
1✔
774
        helpers._initProperties(node)
1✔
775

776
        if is_bytes:
1!
777
            self.assertEqual(obj.getProperty('lines1'), (b'Gee', b'Bar'))
×
NEW
778
            self.assertEqual(obj.getProperty('lines2'), (b'Gee', ))
×
779
        else:
780
            self.assertEqual(obj.getProperty('lines1'), ('Gee', 'Bar'))
1✔
781
            self.assertEqual(obj.getProperty('lines2'), ('Gee', ))
1✔
782

783
    def test_initProperties_remove_properties(self):
1✔
784
        helpers = self._makeOne()
1✔
785
        helpers.environ._should_purge = False  # extension profile
1✔
786
        obj = helpers.context
1✔
787
        obj._properties = ()
1✔
788

789
        # Add two properties
790
        node = _getDocumentElement(_ADD_PROPERTY_IMPORT)
1✔
791
        helpers._initProperties(node)
1✔
792
        self.assertTrue(obj.hasProperty('line1'))
1✔
793
        self.assertEqual(obj.getProperty('line1'), 'Line 1')
1✔
794
        self.assertTrue(obj.hasProperty('line2'))
1✔
795

796
        # Remove one.
797
        node = _getDocumentElement(_REMOVE_PROPERTY_IMPORT)
1✔
798
        helpers._initProperties(node)
1✔
799
        self.assertFalse(obj.hasProperty('line1'))
1✔
800
        self.assertTrue(obj.hasProperty('line2'))
1✔
801

802
        # Removing it a second time should not throw an
803
        # AttributeError.
804
        node = _getDocumentElement(_REMOVE_PROPERTY_IMPORT)
1✔
805
        helpers._initProperties(node)
1✔
806
        self.assertFalse(obj.hasProperty('line1'))
1✔
807
        self.assertTrue(obj.hasProperty('line2'))
1✔
808

809

810
class PropertyManagerHelpersNonPMContextTests(PropertyManagerHelpersTests):
1✔
811

812
    def _makeOne(self, context=None, environ=None):
1✔
813
        from ..testing import DummySetupEnviron
1✔
814
        from ..utils import NodeAdapterBase
1✔
815

816
        class Foo(self._getTargetClass(), NodeAdapterBase):
1✔
817
            _PROPERTIES = _TESTED_PROPERTIES
1✔
818

819
        if context is None:
1✔
820
            context = self._makeContext()
1✔
821

822
        if environ is None:
1!
823
            environ = DummySetupEnviron()
1✔
824

825
        return Foo(context, environ)
1✔
826

827
    def _getContextClass(self):
1✔
828

829
        class NonPropertyManager:
1✔
830
            pass
1✔
831

832
        return NonPropertyManager
1✔
833

834
    def _getReal(self, obj):
1✔
835
        return obj._real
1✔
836

837
    def _populate(self, obj):
1✔
838
        obj.foo_boolean = True
1✔
839
        obj.foo_date = DateTime('2000/01/01 00:00:00 UTC')
1✔
840
        obj.foo_float = 1.1
1✔
841
        obj.foo_int = 1
1✔
842
        obj.foo_lines = ['Foo', 'Lines', '\xfcbrigens'.encode()]
1✔
843
        obj.foo_long = 1
1✔
844
        obj.foo_string = 'Foo String'
1✔
845
        obj.foo_text = 'Foo\nText'
1✔
846
        obj.foo_tokens = ('Foo', 'Tokens')
1✔
847
        obj.foo_selection = 'Foo'
1✔
848
        obj.foo_mselection = ('Foo', 'Baz')
1✔
849
        obj.foo_boolean0 = 0
1✔
850
        obj.foo_date_naive = DateTime('2000/01/01 00:00:00')
1✔
851
        obj.foo_ro = 'RO'
1✔
852
        obj.foo_boolean_nodel = True
1✔
853
        obj.foo_date_nodel = DateTime('2000/01/01 00:00:00 UTC')
1✔
854
        obj.foo_float_nodel = 3.1415
1✔
855
        obj.foo_int_nodel = 1789
1✔
856

857

858
class MarkerInterfaceHelpersTests(unittest.TestCase):
1✔
859

860
    def _getTargetClass(self):
1✔
861
        from ..utils import MarkerInterfaceHelpers
1✔
862

863
        return MarkerInterfaceHelpers
1✔
864

865
    def _makeOne(self, context=None, environ=None):
1✔
866
        from ..testing import DummySetupEnviron
1✔
867
        from ..utils import NodeAdapterBase
1✔
868

869
        class Foo(self._getTargetClass(), NodeAdapterBase):
1✔
870
            pass
1✔
871

872
        if context is None:
1!
873
            context = self._makeContext()
1✔
874

875
        if environ is None:
1!
876
            environ = DummySetupEnviron()
1✔
877

878
        return Foo(context, environ)
1✔
879

880
    def _makeContext(self):
1✔
881
        from OFS.SimpleItem import Item
1✔
882
        return Item('obj')
1✔
883

884
    def _populate(self, obj):
1✔
885
        from zope.interface import directlyProvides
1✔
886

887
        from ..testing import IDummyMarker
1✔
888
        directlyProvides(obj, IDummyMarker)
1✔
889

890
    def setUp(self):
1✔
891
        from OFS.interfaces import IItem
1✔
892
        from Products.Five.utilities.marker import MarkerInterfacesAdapter
1✔
893
        from zope.component import provideAdapter
1✔
894
        from zope.component.interface import provideInterface
1✔
895

896
        from ..testing import IDummyMarker
1✔
897
        provideAdapter(MarkerInterfacesAdapter, (IItem, ))
1✔
898
        provideInterface('', IDummyMarker)
1✔
899

900
    def tearDown(self):
1✔
901
        from zope.testing.cleanup import cleanUp
1✔
902
        cleanUp()
1✔
903

904
    def test__extractMarkers(self):
1✔
905
        from ..utils import PrettyDocument
1✔
906
        helpers = self._makeOne()
1✔
907
        self._populate(helpers.context)
1✔
908
        doc = helpers._doc = PrettyDocument()
1✔
909
        node = doc.createElement('dummy')
1✔
910
        node.appendChild(helpers._extractMarkers())
1✔
911
        doc.appendChild(node)
1✔
912

913
        self.assertEqual(doc.toprettyxml(' '), _NORMAL_MARKER_EXPORT)
1✔
914

915
    def test__purgeMarkers(self):
1✔
916
        from ..testing import IDummyMarker
1✔
917
        helpers = self._makeOne()
1✔
918
        obj = helpers.context
1✔
919
        self._populate(obj)
1✔
920
        self.assertTrue(IDummyMarker.providedBy(obj))
1✔
921

922
        helpers._purgeMarkers()
1✔
923
        self.assertFalse(IDummyMarker.providedBy(obj))
1✔
924

925
    def test__initMarkers(self):
1✔
926
        from ..testing import IDummyMarker
1✔
927
        from ..utils import PrettyDocument
1✔
928
        helpers = self._makeOne()
1✔
929
        node = _getDocumentElement(_NORMAL_MARKER_EXPORT)
1✔
930
        helpers._initMarkers(node)
1✔
931
        self.assertTrue(IDummyMarker.providedBy(helpers.context))
1✔
932

933
        doc = helpers._doc = PrettyDocument()
1✔
934
        node = doc.createElement('dummy')
1✔
935
        node.appendChild(helpers._extractMarkers())
1✔
936
        doc.appendChild(node)
1✔
937

938
        self.assertEqual(doc.toprettyxml(' '), _NORMAL_MARKER_EXPORT)
1✔
939

940

941
class ObjectManagerHelpersTests(ZopeTestCase):
1✔
942

943
    def _getTargetClass(self):
1✔
944
        from ..utils import ObjectManagerHelpers
1✔
945

946
        return ObjectManagerHelpers
1✔
947

948
    def _makeOne(self, context=None, environ=None):
1✔
949
        from ..testing import DummySetupEnviron
1✔
950
        from ..utils import NodeAdapterBase
1✔
951

952
        class Foo(self._getTargetClass(), NodeAdapterBase):
1✔
953
            pass
1✔
954

955
        if context is None:
1!
956
            context = self._makeContext()
1✔
957

958
        if environ is None:
1!
959
            environ = DummySetupEnviron()
1✔
960

961
        return Foo(context, environ)
1✔
962

963
    def _makeContext(self):
1✔
964
        from OFS.ObjectManager import ObjectManager
1✔
965
        return ObjectManager('obj')
1✔
966

967
    def test__initObjects(self):
1✔
968
        helpers = self._makeOne()
1✔
969
        obj = helpers.context
1✔
970
        self.assertFalse('history' in obj.objectIds())
1✔
971

972
        # Add two objects
973
        node = _getDocumentElement(_ADD_IMPORT)
1✔
974
        helpers._initObjects(node)
1✔
975
        self.assertTrue('history' in obj.objectIds())
1✔
976
        self.assertTrue('future' in obj.objectIds())
1✔
977

978
        # Remove one
979
        node = _getDocumentElement(_REMOVE_IMPORT)
1✔
980
        helpers._initObjects(node)
1✔
981
        self.assertFalse('history' in obj.objectIds())
1✔
982
        self.assertTrue('future' in obj.objectIds())
1✔
983

984
        # Removing it a second time should not throw an
985
        # AttributeError.
986
        node = _getDocumentElement(_REMOVE_IMPORT)
1✔
987
        helpers._initObjects(node)
1✔
988
        self.assertFalse('history' in obj.objectIds())
1✔
989
        self.assertTrue('future' in obj.objectIds())
1✔
990

991

992
class PrettyDocumentTests(unittest.TestCase):
1✔
993

994
    def test_attr_quoting(self):
1✔
995
        from ..utils import PrettyDocument
1✔
996
        original = 'baz &nbsp;<bar>&"\''
1✔
997
        html_escaped = (b'<?xml version="1.0" encoding="utf-8"?>\n'
1✔
998
                        b'<doc bar="" foo="baz '
999
                        b'&amp;nbsp;&lt;bar&gt;&amp;&quot;&#x27;"/>\n')
1000
        cgi_escaped = (b'<?xml version="1.0" encoding="utf-8"?>\n'
1✔
1001
                       b'<doc bar="" foo="baz '
1002
                       b'&amp;nbsp;&lt;bar&gt;&amp;&quot;\'"/>\n')
1003

1004
        doc = PrettyDocument()
1✔
1005
        node = doc.createElement('doc')
1✔
1006
        node.setAttribute('foo', original)
1✔
1007
        node.setAttribute('bar', None)
1✔
1008
        doc.appendChild(node)
1✔
1009

1010
        # Output depends on the presence of html.escape
1011
        xml_output = doc.toprettyxml(' ')
1✔
1012
        self.assertEqual(xml_output, html_escaped)
1✔
1013

1014
        # Reparse from cgi.escape representation should always work
1015
        e = _getDocumentElement(cgi_escaped)
1✔
1016
        self.assertEqual(e.getAttribute('foo'), original)
1✔
1017

1018
        # Reparse from html.escape representation should always work, even
1019
        # without html.escape
1020
        e = _getDocumentElement(html_escaped)
1✔
1021
        self.assertEqual(e.getAttribute('foo'), original)
1✔
1022

1023
    def test_text_quoting(self):
1✔
1024
        from ..utils import PrettyDocument
1✔
1025
        original = 'goo &nbsp;<hmm>&"\''
1✔
1026
        html_escaped = (b'<?xml version="1.0" encoding="utf-8"?>\n'
1✔
1027
                        b'<doc>goo &amp;nbsp;&lt;hmm&gt;'
1028
                        b'&amp;&quot;&#x27;</doc>\n')
1029
        cgi_escaped = (b'<?xml version="1.0" encoding="utf-8"?>\n'
1✔
1030
                       b'<doc>goo &amp;nbsp;&lt;hmm&gt;'
1031
                       b'&amp;"\'</doc>\n')
1032

1033
        doc = PrettyDocument()
1✔
1034
        node = doc.createElement('doc')
1✔
1035
        child = doc.createTextNode(original)
1✔
1036
        node.appendChild(child)
1✔
1037
        doc.appendChild(node)
1✔
1038

1039
        # Output depends on the presence of html.escape
1040
        xml_output = doc.toprettyxml(' ')
1✔
1041
        self.assertEqual(xml_output, html_escaped)
1✔
1042

1043
        # Reparse from cgi.escape representation (Python 2 only)
1044
        # should always work
1045
        e = _getDocumentElement(cgi_escaped)
1✔
1046
        self.assertEqual(e.childNodes[0].nodeValue, original)
1✔
1047

1048
        # Reparse from html.escape representation (Python 3 only)
1049
        # should always work, even without html.escape
1050
        e = _getDocumentElement(html_escaped)
1✔
1051
        self.assertEqual(e.childNodes[0].nodeValue, original)
1✔
1052

1053

1054
def test_suite():
1✔
1055
    # reimport to make sure tests are run from Products
1056
    from .test_utils import UtilsTests
1✔
1057

1058
    loader = unittest.defaultTestLoader
1✔
1059
    return unittest.TestSuite((
1✔
1060
        loader.loadTestsFromTestCase(UtilsTests),
1061
        loader.loadTestsFromTestCase(PropertyManagerHelpersTests),
1062
        loader.loadTestsFromTestCase(PropertyManagerHelpersNonPMContextTests),
1063
        loader.loadTestsFromTestCase(MarkerInterfaceHelpersTests),
1064
        loader.loadTestsFromTestCase(ObjectManagerHelpersTests),
1065
        loader.loadTestsFromTestCase(PrettyDocumentTests),
1066
    ))
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc