Changeset 12069
- Timestamp:
- Sep 27, 2012, 11:06:58 PM (12 years ago)
- Location:
- tagsplugin/trunk
- Files:
-
- 6 added
- 10 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
tagsplugin/trunk/changelog
r11932 r12069 1 1 Originally created by Muness Alrubaie, totally rewritten by 2 2 Author: Alec Thomas <alec@swapoff.org> 3 Maintainer: n.n.3 Maintainer: Steffen Hoffmann <hoff.st@web.de> 4 4 5 5 tractags-0.7 (not yet released) … … 36 36 * #9062: Wiki-page level-1-title broken on 0.12 37 37 * #9210: /tags page should not have a contextual navigation link 'Cloud' 38 * #9521: New install impossible on Trac 0.13dev 39 by adding generic db schema upgrade support 38 40 * TagsQuery now supports a context object 39 41 * refactor cloud rendering so it can be used by other plugins -
tagsplugin/trunk/tractags/__init__.py
r10780 r12069 2 2 # 3 3 # Copyright (C) 2006 Alec Thomas <alec@swapoff.org> 4 # Copyright (C) 2012 Steffen Hoffmann <hoff.st@web.de> 4 5 # 5 6 # This software is licensed as described in the file COPYING, which … … 12 13 13 14 import api 15 import db 14 16 import wiki 15 17 import ticket 16 18 import macros 17 19 import web_ui 18 import model19 20 import admin -
tagsplugin/trunk/tractags/model.py
r11984 r12069 1 from trac.core import Component, TracError, implements 2 from trac.env import IEnvironmentSetupParticipant 3 from trac.db import Table, Column, Index 4 from trac.db.api import DatabaseManager 5 6 7 class TagModelProvider(Component): 8 9 implements(IEnvironmentSetupParticipant) 10 11 SCHEMA = [ 12 Table('tags', key = ('tagspace', 'name', 'tag'))[ 13 Column('tagspace'), 14 Column('name'), 15 Column('tag'), 16 Index(['tagspace', 'name']), 17 Index(['tagspace', 'tag']),] 18 ] 19 def __init__(self): 20 # Preemptive check for rollback tolerance of read-only db connections. 21 # This is required to avoid breaking `environment_needs_upgrade`, 22 # if the plugin uses intentional db transaction errors for the test. 23 self.rollback_is_safe = True 24 try: 25 db = DatabaseManager(self.env).get_connection() 26 if hasattr(db, 'readonly'): 27 db = DatabaseManager(self.env).get_connection(readonly=True) 28 cursor = db.cursor() 29 # Test needed for rollback on read-only connections. 30 cursor.execute("SELECT COUNT(*) FROM system") 31 cursor.fetchone() 32 try: 33 db.rollback() 34 except AttributeError: 35 # Avoid rollback on read-only connections. 36 self.rollback_is_safe = False 37 return 38 # Test passed. 39 except TracError, e: 40 # Trac too old - expect no constraints. 41 return 42 43 # IEnvironmentSetupParticipant methods 44 def environment_created(self): 45 self._upgrade_db(self.env.get_db_cnx()) 46 47 def environment_needs_upgrade(self, db): 48 if self._need_migration(db): 49 return True 50 try: 51 cursor = db.cursor() 52 cursor.execute("SELECT COUNT(*) FROM tags") 53 cursor.fetchone() 54 return False 55 except Exception, e: 56 self.log.error("DatabaseError: %s", e) 57 if self.rollback_is_safe: 58 db.rollback() 59 return True 60 61 def upgrade_environment(self, db): 62 self._upgrade_db(db) 63 64 def _need_migration(self, db): 65 cursor = db.cursor() 66 # Special handling for the PostgreSQL Trac db backend. 67 if self.env.config.get('trac', 'database').startswith('postgres'): 68 cursor.execute(""" 69 SELECT relname 70 FROM pg_class 71 WHERE relname = 'wiki_namespace' 72 """) 73 if cursor.fetchone() is not None: 74 self.env.log.debug("tractags needs to migrate old data") 75 return True 76 else: 77 return False 78 try: 79 cursor.execute("SELECT COUNT(*) FROM wiki_namespace") 80 cursor.fetchone() 81 self.env.log.debug("tractags needs to migrate old data") 82 return True 83 except Exception, e: 84 # The expected outcome for any up-to-date installation. 85 if self.rollback_is_safe: 86 db.rollback() 87 return False 88 89 def _upgrade_db(self, db): 90 try: 91 try: 92 from trac.db import DatabaseManager 93 db_backend, _ = DatabaseManager(self.env)._get_connector() 94 except ImportError: 95 db_backend = self.env.get_db_cnx() 96 97 cursor = db.cursor() 98 for table in self.SCHEMA: 99 for stmt in db_backend.to_sql(table): 100 self.env.log.debug(stmt) 101 cursor.execute(stmt) 102 db.commit() 103 104 # Migrate old data 105 if self._need_migration(db): 106 cursor = db.cursor() 107 cursor.execute(""" 108 INSERT INTO tags 109 (tagspace, name, tag) 110 SELECT 'wiki', name, namespace 111 FROM wiki_namespace 112 """) 113 cursor.execute("DROP TABLE wiki_namespace") 114 db.commit() 115 except Exception, e: 116 self.log.error("DatabaseError: %s", e) 117 db.rollback() 118 raise 119 1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # 4 # Copyright (C) 2012 Steffen Hoffmann <hoff.st@web.de> 5 # 6 # This software is licensed as described in the file COPYING, which 7 # you should have received as part of this distribution. -
tagsplugin/trunk/tractags/tests/__init__.py
r10788 r12069 2 2 # 3 3 # Copyright (C) 2011 Odd Simon Simonsen <oddsimons@gmail.com> 4 # Copyright (C) 2012 Steffen Hoffmann <hoff.st@web.de> 4 5 # 5 6 # This software is licensed as described in the file COPYING, which … … 12 13 def test_suite(): 13 14 suite = unittest.TestSuite() 14 15 15 16 import tractags.tests.admin 16 17 suite.addTest(tractags.tests.admin.test_suite()) 17 18 18 19 import tractags.tests.api 19 20 suite.addTest(tractags.tests.api.test_suite()) 20 21 22 import tractags.tests.db 23 suite.addTest(tractags.tests.db.test_suite()) 24 21 25 import tractags.tests.macros 22 26 suite.addTest(tractags.tests.macros.test_suite()) 23 24 import tractags.tests.model 25 suite.addTest(tractags.tests.model.test_suite()) 26 27 27 28 import tractags.tests.query 28 29 suite.addTest(tractags.tests.query.test_suite()) 29 30 30 31 import tractags.tests.ticket 31 32 suite.addTest(tractags.tests.ticket.test_suite()) 32 33 33 34 import tractags.tests.web_ui 34 35 suite.addTest(tractags.tests.web_ui.test_suite()) 35 36 36 37 import tractags.tests.wiki 37 38 suite.addTest(tractags.tests.wiki.test_suite()) 38 39 39 40 return suite 40 41 … … 44 45 if __name__ == '__main__': 45 46 unittest.main(defaultTest='test_suite') 46 -
tagsplugin/trunk/tractags/tests/admin.py
r10670 r12069 2 2 # 3 3 # Copyright (C) 2011 Odd Simon Simonsen <oddsimons@gmail.com> 4 # Copyright (C) 2012 Steffen Hoffmann <hoff.st@web.de> 4 5 # 5 6 # This software is licensed as described in the file COPYING, which … … 7 8 # 8 9 9 import unittest10 10 import shutil 11 11 import tempfile 12 import unittest 12 13 13 14 from trac.test import EnvironmentStub, Mock 14 15 15 from tractags.model import TagModelProvider16 16 from tractags.admin import TagChangeAdminPanel 17 17 18 18 19 19 class TagChangeAdminPanelTestCase(unittest.TestCase): 20 20 21 21 def setUp(self): 22 22 self.env = EnvironmentStub( 23 23 enable=['trac.*', 'tractags.*']) 24 24 self.env.path = tempfile.mkdtemp() 25 TagModelProvider(self.env).environment_created() 26 25 27 26 self.tag_cap = TagChangeAdminPanel(self.env) 28 27 29 28 def tearDown(self): 30 29 shutil.rmtree(self.env.path) 31 30 32 31 def test_init(self): 33 32 # Empty test just to confirm that setUp and tearDown works -
tagsplugin/trunk/tractags/tests/api.py
r10670 r12069 2 2 # 3 3 # Copyright (C) 2011 Odd Simon Simonsen <oddsimons@gmail.com> 4 # Copyright (C) 2012 Steffen Hoffmann <hoff.st@web.de> 4 5 # 5 6 # This software is licensed as described in the file COPYING, which … … 7 8 # 8 9 9 import unittest10 10 import shutil 11 11 import tempfile 12 import unittest 12 13 13 14 from trac.test import EnvironmentStub, Mock 14 15 15 from tractags.model import TagModelProvider16 16 from tractags.api import TagSystem 17 17 18 18 19 19 class TagSystemTestCase(unittest.TestCase): 20 20 21 21 def setUp(self): 22 22 self.env = EnvironmentStub( 23 23 enable=['trac.*', 'tractags.*']) 24 24 self.env.path = tempfile.mkdtemp() 25 TagModelProvider(self.env).environment_created() 26 25 27 26 self.tag_s = TagSystem(self.env) 28 27 29 28 def tearDown(self): 30 29 shutil.rmtree(self.env.path) 31 30 32 31 def test_init(self): 33 32 # Empty test just to confirm that setUp and tearDown works -
tagsplugin/trunk/tractags/tests/db.py
r12068 r12069 2 2 # 3 3 # Copyright (C) 2011 Odd Simon Simonsen <oddsimons@gmail.com> 4 # Copyright (C) 2012 Steffen Hoffmann <hoff.st@web.de> 4 5 # 5 6 # This software is licensed as described in the file COPYING, which … … 7 8 # 8 9 9 import unittest10 10 import shutil 11 11 import tempfile 12 import unittest 12 13 14 from trac import __version__ as trac_version 15 from trac.db import Table, Column, Index 16 from trac.db.api import DatabaseManager 13 17 from trac.test import EnvironmentStub, Mock 14 18 15 from tractags.model import TagModelProvider 19 from tractags import db_default 20 from tractags.db import TagSetup 16 21 17 class TagsProviderTestCase(unittest.TestCase): 18 22 23 class TagSetupTestCase(unittest.TestCase): 24 19 25 def setUp(self): 20 26 self.env = EnvironmentStub( 21 enable=['trac.*' , 'tractags.*'])27 enable=['trac.*']) 22 28 self.env.path = tempfile.mkdtemp() 23 self.tag_mp = TagModelProvider(self.env) 24 self.tag_mp.environment_created() 25 29 # Initialize default (SQLite) db in memory. 30 self.db = DatabaseManager(self.env) 31 # Workaround required for Trac 0.11 up to 0.12.4 . 32 if trac_version < '0.13dev': 33 self.db.init_db() 34 26 35 def tearDown(self): 27 36 shutil.rmtree(self.env.path) 28 37 29 38 # Helpers 30 39 31 40 def _get_cursor_description(self, cursor): 32 41 # Cursors don't look the same across Trac versions 33 from trac import __version__ as trac_version34 42 if trac_version < '0.12': 35 43 return cursor.description 36 44 else: 37 45 return cursor.cursor.description 38 46 39 47 # Tests 40 41 def test_ table_exists(self):48 49 def test_new_install(self): 42 50 db = self.env.get_db_cnx() 51 setup = TagSetup(self.env) 52 self.assertEquals(0, setup.get_schema_version(db)) 53 54 setup.upgrade_environment(self.env.get_db_cnx()) 43 55 cursor = db.cursor() 44 56 tags = cursor.execute("SELECT * FROM tags").fetchall() … … 46 58 self.assertEquals(['tagspace', 'name', 'tag'], 47 59 [col[0] for col in self._get_cursor_description(cursor)]) 60 version = cursor.execute(""" 61 SELECT value 62 FROM system 63 WHERE name='tags_version' 64 """).fetchone() 65 self.assertEquals(db_default.schema_version, int(version[0])) 66 67 def test_upgrade_schema_v1(self): 68 # Ancient, unversioned schema - wiki only. 69 schema = [ 70 Table('wiki_namespace')[ 71 Column('name'), 72 Column('namespace'), 73 Index(['name', 'namespace']), 74 ] 75 ] 76 connector = self.db._get_connector()[0] 77 db = self.env.get_db_cnx() 78 cursor = db.cursor() 79 80 for table in schema: 81 for stmt in connector.to_sql(table): 82 cursor.execute(stmt) 83 # Populate table with migration test data. 84 cursor.execute(""" 85 INSERT INTO wiki_namespace 86 (name, namespace) 87 VALUES ('WikiStart', 'tag') 88 """) 89 # Current tractags schema is setup with enabled component anyway. 90 cursor.execute("DROP TABLE IF EXISTS tags") 91 92 tags = cursor.execute("SELECT * FROM wiki_namespace").fetchall() 93 self.assertEquals([('WikiStart', 'tag')], tags) 94 setup = TagSetup(self.env) 95 self.assertEquals(1, setup.get_schema_version(db)) 96 97 setup.upgrade_environment(self.env.get_db_cnx()) 98 cursor = db.cursor() 99 tags = cursor.execute("SELECT * FROM tags").fetchall() 100 # Db content should be migrated. 101 self.assertEquals([('wiki', 'WikiStart', 'tag')], tags) 102 self.assertEquals(['tagspace', 'name', 'tag'], 103 [col[0] for col in self._get_cursor_description(cursor)]) 104 version = cursor.execute(""" 105 SELECT value 106 FROM system 107 WHERE name='tags_version' 108 """).fetchone() 109 self.assertEquals(db_default.schema_version, int(version[0])) 110 111 def test_upgrade_schema_v2(self): 112 # Just register a current, but unversioned schema. 113 schema = [ 114 Table('tags', key=('tagspace', 'name', 'tag'))[ 115 Column('tagspace'), 116 Column('name'), 117 Column('tag'), 118 Index(['tagspace', 'name']), 119 Index(['tagspace', 'tag']), 120 ] 121 ] 122 connector = self.db._get_connector()[0] 123 db = self.env.get_db_cnx() 124 cursor = db.cursor() 125 # Current tractags schema is setup with enabled component anyway. 126 cursor.execute("DROP TABLE IF EXISTS tags") 127 128 for table in schema: 129 for stmt in connector.to_sql(table): 130 cursor.execute(stmt) 131 # Populate table with test data. 132 cursor.execute(""" 133 INSERT INTO tags 134 (tagspace, name, tag) 135 VALUES ('wiki', 'WikiStart', 'tag') 136 """) 137 138 tags = cursor.execute("SELECT * FROM tags").fetchall() 139 self.assertEquals([('wiki', 'WikiStart', 'tag')], tags) 140 setup = TagSetup(self.env) 141 self.assertEquals(2, setup.get_schema_version(db)) 142 143 setup.upgrade_environment(self.env.get_db_cnx()) 144 cursor = db.cursor() 145 tags = cursor.execute("SELECT * FROM tags").fetchall() 146 # Db should be unchanged. 147 self.assertEquals([('wiki', 'WikiStart', 'tag')], tags) 148 self.assertEquals(['tagspace', 'name', 'tag'], 149 [col[0] for col in self._get_cursor_description(cursor)]) 150 version = cursor.execute(""" 151 SELECT value 152 FROM system 153 WHERE name='tags_version' 154 """).fetchone() 155 self.assertEquals(db_default.schema_version, int(version[0])) 48 156 49 157 50 158 def test_suite(): 51 159 suite = unittest.TestSuite() 52 suite.addTest(unittest.makeSuite(Tag sProviderTestCase, 'test'))160 suite.addTest(unittest.makeSuite(TagSetupTestCase, 'test')) 53 161 return suite 54 162 -
tagsplugin/trunk/tractags/tests/macros.py
r11934 r12069 2 2 # 3 3 # Copyright (C) 2011 Odd Simon Simonsen <oddsimons@gmail.com> 4 # Copyright (C) 2012 Steffen Hoffmann <hoff.st@web.de> 4 5 # 5 6 # This software is licensed as described in the file COPYING, which … … 7 8 # 8 9 9 import unittest10 10 import shutil 11 11 import tempfile 12 import unittest 12 13 13 14 from trac.test import EnvironmentStub, Mock … … 15 16 from trac.web.href import Href 16 17 17 from tractags.model import TagModelProvider18 18 from tractags.macros import TagTemplateProvider, TagWikiMacros 19 19 … … 25 25 enable=['trac.*', 'tractags.*']) 26 26 self.env.path = tempfile.mkdtemp() 27 TagModelProvider(self.env).environment_created()28 27 29 28 # TagTemplateProvider is abstract, test using a subclass … … 44 43 enable=['trac.*', 'tractags.*']) 45 44 self.env.path = tempfile.mkdtemp() 46 TagModelProvider(self.env).environment_created()47 45 PermissionSystem(self.env).grant_permission('user', 'TAGS_VIEW') 48 46 49 47 self.tag_twm = TagWikiMacros(self.env) 50 48 51 49 def tearDown(self): 52 50 shutil.rmtree(self.env.path) 53 51 54 52 def test_empty_content(self): 55 53 req = Mock(args={}, … … 65 63 str(self.tag_twm.expand_macro(formatter, 'ListTagged', ''))) 66 64 65 67 66 class TagCloudMacroTestCase(unittest.TestCase): 68 67 69 68 def setUp(self): 70 69 self.env = EnvironmentStub( 71 70 enable=['trac.*', 'tractags.*']) 72 71 self.env.path = tempfile.mkdtemp() 73 TagModelProvider(self.env).environment_created() 74 72 75 73 self.tag_twm = TagWikiMacros(self.env) 76 74 77 75 def tearDown(self): 78 76 shutil.rmtree(self.env.path) 79 77 80 78 def test_init(self): 81 79 # Empty test just to confirm that setUp and tearDown works -
tagsplugin/trunk/tractags/tests/ticket.py
r10670 r12069 2 2 # 3 3 # Copyright (C) 2011 Odd Simon Simonsen <oddsimons@gmail.com> 4 # Copyright (C) 2012 Steffen Hoffmann <hoff.st@web.de> 4 5 # 5 6 # This software is licensed as described in the file COPYING, which … … 7 8 # 8 9 9 import unittest10 10 import shutil 11 11 import tempfile 12 import unittest 12 13 13 14 from trac.test import EnvironmentStub, Mock 14 15 15 from tractags.model import TagModelProvider16 16 from tractags.ticket import TicketTagProvider 17 17 18 18 19 19 class TicketTagProviderTestCase(unittest.TestCase): 20 20 21 21 def setUp(self): 22 22 self.env = EnvironmentStub( 23 23 enable=['trac.*', 'tractags.*']) 24 24 self.env.path = tempfile.mkdtemp() 25 TagModelProvider(self.env).environment_created() 26 25 27 26 self.tag_tp = TicketTagProvider(self.env) 28 27 29 28 def tearDown(self): 30 29 shutil.rmtree(self.env.path) 31 30 32 31 def test_init(self): 33 32 # Empty test just to confirm that setUp and tearDown works -
tagsplugin/trunk/tractags/tests/web_ui.py
r11935 r12069 8 8 # 9 9 10 import unittest11 10 import shutil 12 11 import tempfile 12 import unittest 13 13 14 14 from trac.test import EnvironmentStub, Mock … … 18 18 19 19 from tractags.api import TagSystem 20 from tractags.model import TagModelProvider21 20 from tractags.web_ui import TagRequestHandler 22 21 23 22 24 23 class TagRequestHandlerTestCase(unittest.TestCase): 25 24 26 25 def setUp(self): 27 26 self.env = EnvironmentStub( 28 27 enable=['trac.*', 'tractags.*']) 29 28 self.env.path = tempfile.mkdtemp() 30 TagModelProvider(self.env).environment_created() 31 29 32 30 self.tag_s = TagSystem(self.env) 33 31 self.tag_rh = TagRequestHandler(self.env) 34 32 35 33 perm_system = PermissionSystem(self.env) 36 34 self.anonymous = PermissionCache(self.env, 'anonymous') … … 41 39 self.admin = PermissionCache(self.env, 'admin') 42 40 perm_system.grant_permission('admin', 'TAGS_ADMIN') 43 41 44 42 self.href = Href('/trac') 45 43 self.abs_href = Href('http://example.org/trac') 46 44 47 45 def tearDown(self): 48 46 shutil.rmtree(self.env.path) 49 47 50 48 def test_matches(self): 51 49 req = Mock(path_info='/tags', … … 54 52 ) 55 53 self.assertEquals(True, self.tag_rh.match_request(req)) 56 54 57 55 def test_matches_no_permission(self): 58 56 req = Mock(path_info='/tags', … … 61 59 ) 62 60 self.assertEquals(False, self.tag_rh.match_request(req)) 63 61 64 62 def test_get_main_page(self): 65 63 req = Mock(path_info='/tags', -
tagsplugin/trunk/tractags/tests/wiki.py
r10670 r12069 2 2 # 3 3 # Copyright (C) 2011 Odd Simon Simonsen <oddsimons@gmail.com> 4 # Copyright (C) 2012 Steffen Hoffmann <hoff.st@web.de> 4 5 # 5 6 # This software is licensed as described in the file COPYING, which … … 7 8 # 8 9 9 import unittest10 10 import shutil 11 11 import tempfile 12 import unittest 12 13 13 14 from trac.test import EnvironmentStub, Mock 14 15 15 from tractags.model import TagModelProvider16 16 from tractags.wiki import WikiTagProvider 17 17 18 18 19 19 class WikiTagProviderTestCase(unittest.TestCase): 20 20 21 21 def setUp(self): 22 22 self.env = EnvironmentStub( 23 23 enable=['trac.*', 'tractags.*']) 24 24 self.env.path = tempfile.mkdtemp() 25 TagModelProvider(self.env).environment_created() 26 25 27 26 self.tag_wp = WikiTagProvider(self.env) 28 27 29 28 def tearDown(self): 30 29 shutil.rmtree(self.env.path) 31 30 32 31 def test_init(self): 33 32 # Empty test just to confirm that setUp and tearDown works
Note: See TracChangeset
for help on using the changeset viewer.