~ubuntu-branches/ubuntu/raring/software-center/raring-proposed

« back to all changes in this revision

Viewing changes to tests/test_database.py

  • Committer: Package Import Robot
  • Author(s): Michael Vogt
  • Date: 2012-10-11 15:33:05 UTC
  • mfrom: (195.1.18 quantal)
  • Revision ID: package-import@ubuntu.com-20121011153305-fm5ln7if3rpzts4n
Tags: 5.4.1.1
* lp:~mvo/software-center/reinstall-previous-purchase-token-fix:
  - fix reinstall previous purchases that have a system-wide
    license key LP: #1065481
* lp:~mvo/software-center/lp1060106:
  - Add missing gettext init for utils/update-software-center-agent
    (LP: #1060106)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
import apt
 
2
import platform
2
3
import os
3
4
import re
4
5
import tempfile
13
14
from tests.utils import (
14
15
    DATA_DIR,
15
16
    get_test_db,
 
17
    get_test_db_from_app_install_data,
16
18
    get_test_pkg_info,
17
19
    do_events,
18
20
    make_software_center_agent_subscription_dict,
30
32
from softwarecenter.db.database import parse_axi_values_file
31
33
from softwarecenter.db.pkginfo import get_pkg_info, _Version
32
34
from softwarecenter.db.update import (
33
 
    make_doc_from_parser,
34
35
    update_from_app_install_data,
35
36
    update_from_var_lib_apt_lists,
36
37
    update_from_appstream_xml,
44
45
    get_installed_apps_list,
45
46
)
46
47
from softwarecenter.enums import (
 
48
    NonAppVisibility,
 
49
    PkgStates,
47
50
    XapianValues,
48
 
    PkgStates,
49
 
    )
50
 
from softwarecenter.region import REGION_BLACKLIST_TAG, REGIONTAG
 
51
    )
 
52
from softwarecenter.region import (
 
53
    REGION_BLACKLIST_TAG,
 
54
    REGION_WHITELIST_TAG,
 
55
    REGIONTAG,
 
56
    )
51
57
 
52
58
PKD_DIR = os.path.join(DATA_DIR, 'appdetails', 'var', 'lib', 'dpkg', 'status')
53
59
TEST_DB = os.path.join(DATA_DIR, "test.db")
78
84
        self.assertEqual(res, [ v2, v1, v0 ])
79
85
 
80
86
 
 
87
    def _get_db_from_test_app_install_data(self):
 
88
        db = xapian.inmemory_open()
 
89
        res = update_from_app_install_data(db, self.cache,
 
90
            datadir=os.path.join(DATA_DIR, "desktop"))
 
91
        self.assertTrue(res)
 
92
        self.assertEqual(db.get_doccount(), 5)
 
93
        return db
 
94
 
81
95
    def test_update_from_desktop_file(self):
82
96
        # ensure we index with german locales to test i18n
83
97
        os.environ["LANGUAGE"] = "de"
84
 
        db = xapian.WritableDatabase(TEST_DB,
85
 
                                     xapian.DB_CREATE_OR_OVERWRITE)
86
 
        res = update_from_app_install_data(db, self.cache,
87
 
            datadir=os.path.join(DATA_DIR, "desktop"))
88
 
        self.assertTrue(res)
89
 
        self.assertEqual(db.get_doccount(), 5)
 
98
        datadir = os.path.join(DATA_DIR, "desktop")
 
99
        db = get_test_db_from_app_install_data(datadir)
90
100
        # test if Name[de] was picked up
91
101
        i=0
92
102
        for it in db.postlist("AAUbuntu Software Zentrum"):
93
103
            i+=1
94
 
        self.assertEqual(i, 1)
 
104
 
 
105
    def test_regression_index_terms(self):
 
106
        """ this tests for a regression that we had in the term indexer
 
107
            that would index hundrets of size 1 terms due to a bug
 
108
            in AppInfoParserBase._set_doc_from_key
 
109
        """
 
110
        db = xapian.WritableDatabase(TEST_DB,
 
111
                                     xapian.DB_CREATE_OR_OVERWRITE)
 
112
        update_from_app_install_data(db, self.cache,
 
113
            datadir=os.path.join(DATA_DIR, "desktop"))
 
114
        for it in db.postlist("APsoftware-center"):
 
115
            docid = it.docid
 
116
            break
 
117
        # this is the important part, ensure no signle char terms
 
118
        for t in db.termlist(docid):
 
119
            self.assertFalse(len(t.term) == 1)
95
120
 
96
121
    def test_update_from_appstream_xml(self):
97
 
        db = xapian.WritableDatabase(TEST_DB,
98
 
                                     xapian.DB_CREATE_OR_OVERWRITE)
 
122
        db = xapian.inmemory_open()
99
123
        res = update_from_appstream_xml(db, self.cache,
100
124
            os.path.join(DATA_DIR, "app-info"))
101
125
        self.assertTrue(res)
111
135
                self.assertIsInstance(value.num, long)
112
136
                self.assertIsInstance(value.value, basestring)
113
137
 
 
138
    @unittest.skipIf(platform.dist()[2] == "precise", "needs quantal or later")
114
139
    def test_update_from_var_lib_apt_lists(self):
115
140
        # ensure we index with german locales to test i18n
116
141
        os.environ["LANGUAGE"] = "de"
117
 
        db = xapian.WritableDatabase(TEST_DB,
118
 
                                     xapian.DB_CREATE_OR_OVERWRITE)
 
142
        db = xapian.inmemory_open()
119
143
        res = update_from_var_lib_apt_lists(db, self.cache,
120
144
            listsdir=os.path.join(DATA_DIR, "app-info"))
121
145
        self.assertTrue(res)
122
146
        self.assertEqual(db.get_doccount(), 1)
123
147
        # test if Name-de was picked up
124
148
        i=0
125
 
        for it in db.postlist("AAFestplatten Ueberpruefer"):
 
149
        for it in db.postlist("AAFestplattenbelegung analysieren"):
126
150
            i+=1
127
151
        self.assertEqual(i, 1)
128
152
        # test if gettext worked
129
153
        found_gettext_translation = False
130
 
        for it in db.postlist("AAFestplatten Ueberpruefer"):
 
154
        for it in db.postlist("AAFestplattenbelegung analysieren"):
131
155
            doc = db.get_document(it.docid)
132
156
            for term_iter in doc.termlist():
133
157
                # a german term from the app-info file to ensure that
134
158
                # it got indexed in german
135
 
                if term_iter.term == "platzes":
 
159
                if term_iter.term == "festplattenbelegung":
136
160
                    found_gettext_translation = True
137
161
                    break
138
162
        self.assertTrue(found_gettext_translation)
139
163
 
140
164
    def test_update_from_json_string(self):
141
 
        db = xapian.WritableDatabase(TEST_DB,
142
 
                                     xapian.DB_CREATE_OR_OVERWRITE)
 
165
        db = xapian.inmemory_open()
143
166
        cache = apt.Cache()
144
167
        p = os.path.join(DATA_DIR, "app-info-json", "apps.json")
145
168
        res = update_from_json_string(db, cache, open(p).read(), origin=p)
146
169
        self.assertTrue(res)
147
170
        self.assertEqual(db.get_doccount(), 1)
148
171
 
149
 
    def test_build_from_software_center_agent(self):
150
 
        db = xapian.WritableDatabase(TEST_DB,
151
 
                                     xapian.DB_CREATE_OR_OVERWRITE)
 
172
    @patch("softwarecenter.backend.ubuntusso.UbuntuSSO"
 
173
           ".find_oauth_token_sync")
 
174
    def test_build_from_software_center_agent(self, mock_find_oauth):
 
175
        # pretend we have no token
 
176
        mock_find_oauth.return_value = None
 
177
        db = xapian.inmemory_open()
152
178
        cache = apt.Cache()
153
179
        # monkey patch distro to ensure we get data
154
180
        distro = softwarecenter.distro.get_distro()
209
235
        app = Application("Ubuntu Software Center Test", "software-center")
210
236
        details = app.get_details(db)
211
237
        self.assertNotEqual(details, None)
212
 
        self.assertEqual(details.component, "main")
 
238
        # mvo: disabled, we can reenable this once there is a static
 
239
        #      apt rootdir and we do not rely on the test system to
 
240
        #      have software-center from the main archive and not from
 
241
        #      e.g. a custom repo like the ADT environment
 
242
        #self.assertEqual(details.component, "main")
213
243
        self.assertEqual(details.pkgname, "software-center")
214
244
        # get the first document
215
245
        for doc in db:
226
256
        #        and monkey-patch/modify the APP_INSTALL_CHANNELS_PATH
227
257
        self.assertEqual(appdetails.channelname, None)
228
258
        self.assertEqual(appdetails.channelfile, None)
229
 
        self.assertEqual(appdetails.component, "main")
230
259
        self.assertNotEqual(appdetails.pkg, None)
231
260
        # from the fake test/data/appdetails/var/lib/dpkg/status
232
261
        self.assertEqual(appdetails.pkg.is_installed, True)
247
276
                "http://screenshots.ubuntu.com/thumbnail-with-version/software-center/[\d.]+",
248
277
                appdetails.thumbnail))
249
278
        # FIXME: add document that has a price
250
 
        self.assertEqual(appdetails.price, '')
251
 
        self.assertEqual(appdetails.license, "Open source")
 
279
        self.assertEqual(appdetails.price, "Free")
 
280
        self.assertEqual(appdetails.raw_price, "")
 
281
        # mvo: disabled, we can reenable this once there is a static
 
282
        #      apt rootdir and we do not rely on the test system to
 
283
        #      have software-center from the main archive and not from
 
284
        #      e.g. a custom repo like the ADT environment
 
285
        #self.assertEqual(appdetails.license, "Open source")
252
286
        # test lazy history loading for installation date
253
287
        self.ensure_installation_date_and_lazy_history_loading(appdetails)
254
288
        # test apturl replacements
302
336
        app = Application("The expensive gem", "expensive-gem")
303
337
        appdetails = app.get_details(db)
304
338
        self.assertEqual(appdetails.pkg_state, PkgStates.NEEDS_PURCHASE)
305
 
        self.assertEqual(appdetails.icon_url, "http://www.google.com/favicon.ico")
 
339
        self.assertEqual(appdetails.icon_url,
 
340
                         "http://www.google.com/favicon.ico")
306
341
        self.assertEqual(appdetails.icon, "expensive-gem-icon-favicon")
307
342
        # test PkgStates.PURCHASED_BUT_REPO_MUST_BE_ENABLED
308
343
        # test PkgStates.UNKNOWN
309
344
        app = Application("Scintillant Orange", "scintillant-orange")
310
345
        appdetails = app.get_details(db)
311
346
        self.assertEqual(appdetails.pkg_state, PkgStates.NOT_FOUND)
312
 
        self.assertEqual(
313
 
            appdetails.tags,
314
 
            set(['use::converting', 'role::program', 'implemented-in::perl']))
 
347
        expected = ['use::converting', 'role::program', 'implemented-in::perl']
 
348
        self.assertEqual(appdetails.tags, set(expected))
315
349
 
316
350
    def test_packagename_is_application(self):
317
351
        db = StoreDatabase("/var/cache/software-center/xapian", self.cache)
321
355
        # but software-center has
322
356
        self.assertEqual(len(db.get_apps_for_pkgname("software-center")), 1)
323
357
 
 
358
    @unittest.skipIf(not os.path.exists("/var/lib/apt-xapian-index/index"),
 
359
                     "Need populated apt-xapian-index for this test")
324
360
    def test_whats_new(self):
325
361
        db = StoreDatabase("/var/cache/software-center/xapian", self.cache)
326
362
        db.open()
336
372
            doc.get_value(value_time) >= last_time
337
373
            last_time = doc.get_value(value_time)
338
374
 
339
 
    def test_for_purchase_apps_date_published(self):
 
375
    @patch("softwarecenter.backend.ubuntusso.UbuntuSSO"
 
376
           ".find_oauth_token_sync")
 
377
    def test_for_purchase_apps_date_published(self, mock_find_oauth):
 
378
        # pretend we have no token
 
379
        mock_find_oauth.return_value = None
340
380
        #os.environ["SOFTWARE_CENTER_DEBUG_HTTP"] = "1"
341
381
        #os.environ["SOFTWARE_CENTER_AGENT_HOST"] = "http://sc.staging.ubuntu.com/"
342
382
        # staging does not have a valid cert
343
383
        os.environ["PISTON_MINI_CLIENT_DISABLE_SSL_VALIDATION"] = "1"
344
384
        cache = get_test_pkg_info()
345
 
        db = xapian.WritableDatabase(TEST_DB,
346
 
                                     xapian.DB_CREATE_OR_OVERWRITE)
 
385
        db = xapian.inmemory_open()
347
386
        res = update_from_software_center_agent(db, cache, ignore_cache=True)
348
387
        self.assertTrue(res)
349
388
 
429
468
        self.assertNotEqual(axi_values, {})
430
469
        #print axi_values
431
470
 
 
471
    @unittest.skipIf(not os.path.exists("/var/lib/apt-xapian-index/index"), 
 
472
                     "Need populated apt-xapian-index for this test")
432
473
    def test_appdetails(self):
433
474
        db = get_test_db()
434
475
        # see "apt-cache show casper|grep ^Tag"
489
530
    if db is None:
490
531
        db = get_test_db()
491
532
 
492
 
    doc = make_doc_from_parser(parser, db._aptcache)
 
533
    doc = parser.make_doc(db._aptcache)
493
534
    app_details = AppDetails(db, doc)
494
535
    return app_details
495
536
 
499
540
    def setUp(self):
500
541
        self.db = get_test_db()
501
542
 
 
543
    def _get_app_details_from_override(self, override_dict):
 
544
        app_dict = make_software_center_agent_app_dict()
 
545
        app_dict.update(override_dict)
 
546
        app_details =  self._get_app_details_from_app_dict(app_dict)
 
547
        return app_details
 
548
 
502
549
    def _get_app_details_from_app_dict(self, app_dict):
503
550
        item = PistonResponseObject.from_dict(app_dict)
504
551
        parser = SCAApplicationParser(item)
505
 
        doc = make_doc_from_parser(parser, self.db._aptcache)
 
552
        doc = parser.make_doc(self.db._aptcache)
506
553
        app_details = AppDetails(self.db, doc)
507
554
        return app_details
508
555
 
 
556
    def test_currency(self):
 
557
        app_details = self._get_app_details_from_override({
 
558
                "price": "24.95"})
 
559
        self.assertEqual("US$", app_details.currency)
 
560
        self.assertEqual("24.95", app_details.raw_price)
 
561
 
509
562
    @patch('os.path.exists')
510
563
    def test_channel_detection_partner(self, mock):
511
564
        # we need to patch os.path.exists as "AppDetails.channelname" will
512
565
        # check if there is a matching channel description file on disk
513
566
        os.path.exists.return_value = True
514
 
        # setup dict
515
 
        app_dict = make_software_center_agent_app_dict()
516
 
        app_dict["archive_root"] = "http://archive.canonical.com/"
517
 
        app_details = self._get_app_details_from_app_dict(app_dict)
 
567
        app_details = self._get_app_details_from_override({
 
568
                "archive_root": "http://archive.canonical.com/"})
518
569
        # ensure that archive.canonical.com archive roots are detected
519
570
        # as the partner channel
520
571
        dist = softwarecenter.distro.get_distro().get_codename()
526
577
        # check if there is a matching channel description file on disk
527
578
        os.path.exists.return_value = True
528
579
        # setup dict
529
 
        app_dict = make_software_center_agent_app_dict()
530
 
        app_dict["archive_root"] = "http://extras.ubuntu.com/"
531
 
        app_details = self._get_app_details_from_app_dict(app_dict)
 
580
        app_details = self._get_app_details_from_override({
 
581
                "archive_root": "http://extras.ubuntu.com/"})
532
582
        # ensure that archive.canonical.com archive roots are detected
533
583
        # as the partner channel
534
584
        self.assertEqual(app_details.channelname, "ubuntu-extras")
535
585
 
536
586
    def test_date_no_published(self):
537
 
        app_dict = make_software_center_agent_app_dict()
538
 
        app_dict["date_published"] = "None"
539
 
        app_details = self._get_app_details_from_app_dict(app_dict)
 
587
        app_details = self._get_app_details_from_override({
 
588
                "date_published": "None"})
540
589
        # ensure that archive.canonical.com archive roots are detected
541
590
        # as the partner channel
542
591
        self.assertEqual(app_details.date_published, "")
543
592
        # and again
544
 
        app_dict["date_published"] = "2012-01-21 02:15:10.358926"
545
 
        app_details = self._get_app_details_from_app_dict(app_dict)
 
593
        app_details = self._get_app_details_from_override({
 
594
                "date_published": "2012-01-21 02:15:10.358926"})
546
595
        # ensure that archive.canonical.com archive roots are detected
547
596
        # as the partner channel
548
597
        self.assertEqual(app_details.date_published, "2012-01-21 02:15:10")
549
598
 
550
599
    @patch("softwarecenter.db.update.get_region_cached")
551
600
    def test_region_blacklist(self, get_region_cached_mock):
552
 
        get_region_cached_mock.return_value = { "countrycode" : "es",
553
 
                                              }
554
 
        app_dict = make_software_center_agent_app_dict()
555
 
        app_dict["debtags"] = ["%s%s" % (REGION_BLACKLIST_TAG, "es")]
 
601
        """Test that the region blacklist ignores blacklisted locations"""
 
602
        get_region_cached_mock.return_value = { "countrycode" : "es",
 
603
                                              }
 
604
        app_dict = make_software_center_agent_app_dict({
 
605
                "debtags": ["%s%s" % (REGION_BLACKLIST_TAG, "es")]})
 
606
        item = PistonResponseObject.from_dict(app_dict)
 
607
        parser = SCAApplicationParser(item)
 
608
        doc = parser.make_doc(self.db._aptcache)
 
609
        self.assertEqual(doc, None)
 
610
 
 
611
    @patch("softwarecenter.db.update.get_region_cached")
 
612
    def test_region_blacklist_blacklists(self, get_region_cached_mock):
 
613
        """Test that the region blacklist adds non-blacklisted locations"""
 
614
        get_region_cached_mock.return_value = { "countrycode" : "de",
 
615
                                              }
 
616
        app_dict = make_software_center_agent_app_dict({
 
617
                "debtags": ["%s%s" % (REGION_BLACKLIST_TAG, "ES")]})
 
618
        item = PistonResponseObject.from_dict(app_dict)
 
619
        parser = SCAApplicationParser(item)
 
620
        doc = parser.make_doc(self.db._aptcache)
 
621
        self.assertNotEqual(doc, None)
 
622
 
 
623
    @patch("softwarecenter.db.update.get_region_cached")
 
624
    def test_region_whitelist_whitelists(self, get_region_cached_mock):
 
625
        """Test that the whitelist adds whitelisted locations"""
 
626
        get_region_cached_mock.return_value = { "countrycode" : "es",
 
627
                                              }
 
628
        app_dict = make_software_center_agent_app_dict({
 
629
                "debtags": ["%s%s" % (REGION_WHITELIST_TAG, "ES")]})
 
630
        item = PistonResponseObject.from_dict(app_dict)
 
631
        parser = SCAApplicationParser(item)
 
632
        doc = parser.make_doc(self.db._aptcache)
 
633
        self.assertNotEqual(doc, None)
 
634
 
 
635
    @patch("softwarecenter.db.update.get_region_cached")
 
636
    def test_region_whitelist_blacklists(self, get_region_cached_mock):
 
637
        """Test that the whitelist ignores non-whitelist locations"""
 
638
        get_region_cached_mock.return_value = { "countrycode" : "de",
 
639
                                              }
 
640
        app_dict = make_software_center_agent_app_dict({
 
641
                "debtags": ["%s%s" % (REGION_WHITELIST_TAG, "ES")]})
556
642
        # see _get_app_details_from_app_dict
557
643
        item = PistonResponseObject.from_dict(app_dict)
558
644
        parser = SCAApplicationParser(item)
559
 
        doc = make_doc_from_parser(parser, self.db._aptcache)
 
645
        doc = parser.make_doc(self.db._aptcache)
560
646
        self.assertEqual(doc, None)
561
647
 
562
648
 
699
785
        self.assertEqual(app.archive_suite, "")
700
786
 
701
787
 
 
788
class XapianQueryParserWorkarounds(unittest.TestCase):
 
789
    """This TestCase demonstrates the issues around the query
 
790
       parser wildcard support if the "-" char is part of the 
 
791
       pkgname and tests the workaround for this
 
792
 
 
793
       (http://trac.xapian.org/ticket/128)
 
794
    """
 
795
 
 
796
    def setUp(self):
 
797
        datadir = os.path.join(DATA_DIR, "desktop")
 
798
        self.db = get_test_db_from_app_install_data(datadir)
 
799
 
 
800
    def test_name_mangling_for_query_parser(self):
 
801
        # test that pkgnames with "-" get added in a mangled form
 
802
        i=0
 
803
        for it in self.db.postlist("APMsoftware_center"):
 
804
            i+=1
 
805
        self.assertEqual(i, 1)
 
806
 
 
807
    def test_query_parser_wildcard(self):
 
808
        enquire = xapian.Enquire(self.db)
 
809
        parser = xapian.QueryParser()
 
810
        parser.set_database(self.db)
 
811
        parser.add_prefix("pkg_wildcard", "AP")
 
812
        # this demonstrates the xapian bug with the query parser 
 
813
        # and "-" special chars, note that once this test fails (i.e.
 
814
        # the returned mset is "1" we can remove this workaround
 
815
        query = parser.parse_query(
 
816
            "pkg_wildcard:software-*", xapian.QueryParser.FLAG_WILDCARD)
 
817
        enquire.set_query(query)
 
818
        mset = enquire.get_mset(0, 100)
 
819
        self.assertEqual(len(mset), 0)
 
820
        # and the workaround
 
821
        parser.add_prefix("pkg_wildcard", "APM")
 
822
        query = parser.parse_query(
 
823
            "pkg_wildcard:software_*", xapian.QueryParser.FLAG_WILDCARD)
 
824
        enquire.set_query(query)
 
825
        mset = enquire.get_mset(0, 100)
 
826
        self.assertEqual(len(mset), 1)
 
827
 
 
828
 
702
829
class TrackDBTestCase(unittest.TestCase):
703
830
 
704
831
    def test_track_db_open(self):
727
854
        self.assertTrue(db._on_axi_stamp_changed.called)
728
855
 
729
856
 
 
857
 
 
858
class DBSearchTestCase(unittest.TestCase):
 
859
 
 
860
    APP_INFO_JSON="""
 
861
[
 
862
 {
 
863
    "application_name": "The apt",
 
864
    "package_name": "apt",
 
865
    "description": "meep"
 
866
 }
 
867
]
 
868
"""
 
869
 
 
870
    @classmethod
 
871
    def setUpClass(cls):
 
872
        cache = get_pkg_info()
 
873
        cache.open()
 
874
        db = xapian.WritableDatabase(TEST_DB,
 
875
                                     xapian.DB_CREATE_OR_OVERWRITE)
 
876
        update_from_json_string(db, cache, cls.APP_INFO_JSON, origin="local")
 
877
        db.close()
 
878
 
 
879
    def setUp(self):
 
880
        # create a fake database to simualte a run of software-center-agent
 
881
        # create a StoreDatabase and add our other db
 
882
        self.db = get_test_db()
 
883
        self.db.add_database(xapian.Database(TEST_DB))
 
884
        self.db.open(use_axi=True)
 
885
        self.enquire = AppEnquire(self.db._aptcache, self.db)
 
886
        
 
887
    def test_search_app_pkgname_duplication_lp891613(self):
 
888
        # simulate a pkg "apt" that is both in the agent and in the x-a-i db
 
889
        search_term = "apt"
 
890
        search_query = self.db.get_query_list_from_search_entry(search_term)
 
891
        self.enquire.set_query(search_query, nonblocking_load=False)
 
892
        self.assertTrue(len(self.enquire._matches) > 2)
 
893
        for m in self.enquire._matches:
 
894
            doc = m.document
 
895
            # ensure that all hits are "apps" and do not come from a-x-i
 
896
            self.assertNotEqual(
 
897
                doc.get_value(XapianValues.PKGNAME), "")
 
898
 
 
899
    def test_search_custom_pkgs_list_lp1043159(self):
 
900
        # simulate a pkg "apt" that is both in the agent and in the x-a-i db
 
901
        pkgs = ["apt","gedit"]
 
902
        search_query = self.db.get_query_for_pkgnames(pkgs)
 
903
        self.enquire.set_query(search_query, 
 
904
                               # custom package lists are always in this mode
 
905
                               nonapps_visible=NonAppVisibility.ALWAYS_VISIBLE,
 
906
                               nonblocking_load=False)
 
907
        self.assertEqual(len(self.enquire._matches), 2)
 
908
 
 
909
 
730
910
if __name__ == "__main__":
 
911
    #import logging
 
912
    #logging.basicConfig(level=logging.DEBUG)
731
913
    unittest.main()