Ejemplo n.º 1
0
    def create(self):
        """Create a new test environment.
        This sets up Trac, calls :meth:`create_repo` and sets up
        authentication.
        """
        os.mkdir(self.dirname)
        # testing.log gets any unused output from subprocesses
        self.logfile = open(os.path.join(self.dirname, 'testing.log'), 'w')
        self.create_repo()

        config_file = os.path.join(self.dirname, 'config.ini')
        config = Configuration(config_file)
        config.set('repositories', '.dir', self.repo_path_for_initenv())
        config.set('repositories', '.type', self.repotype)
        config.save()
        self._tracadmin('initenv', self.tracdir, self.dburi,
                        '--config=%s' % config_file)
        if call([
                sys.executable,
                os.path.join(self.trac_src, 'contrib', 'htpasswd.py'), "-c",
                "-b", self.htpasswd, "admin", "admin"
        ],
                close_fds=close_fds,
                cwd=self.command_cwd):
            raise Exception('Unable to setup admin password')
        self.adduser('user')
        self.adduser('joe')
        self.grant_perm('admin', 'TRAC_ADMIN')
        env = self.get_trac_environment()
        for component in self.get_enabled_components():
            env.config.set('components', component, 'enabled')
        env.config.save()
        self.post_create(env)
Ejemplo n.º 2
0
    def create(self, options=[], default_data=True):
        """Create the basic directory structure of the environment,
        initialize the database and populate the configuration file
        with default values.

        If options contains ('inherit', 'file'), default values will
        not be loaded; they are expected to be provided by that file
        or other options.

        :raises TracError: if the base directory of `path` does not exist.
        :raises TracError: if `path` exists and is not empty.
        """
        base_dir = os.path.dirname(self.path)
        if not os.path.exists(base_dir):
            raise TracError(
                _(
                    "Base directory '%(env)s' does not exist. Please create it "
                    "and retry.",
                    env=base_dir))

        if os.path.exists(self.path) and os.listdir(self.path):
            raise TracError(_("Directory exists and is not empty."))

        # Create the directory structure
        if not os.path.exists(self.path):
            os.mkdir(self.path)
        os.mkdir(self.htdocs_dir)
        os.mkdir(self.log_dir)
        os.mkdir(self.plugins_dir)
        os.mkdir(self.templates_dir)

        # Create a few files
        create_file(os.path.join(self.path, 'VERSION'), _VERSION + '\n')
        create_file(
            os.path.join(self.path, 'README'),
            'This directory contains a Trac environment.\n'
            'Visit https://trac.edgewall.org/ for more information.\n')

        # Setup the default configuration
        os.mkdir(self.conf_dir)
        config = Configuration(self.config_file_path)
        for section, name, value in options:
            config.set(section, name, value)
        config.save()
        self.setup_config()
        if not any((section, option) == ('inherit', 'file')
                   for section, option, value in options):
            self.config.set_defaults(self)
            self.config.save()

        # Create the sample configuration
        create_file(self.config_file_path + '.sample')
        self._update_sample_config()

        # Create the database
        dbm = DatabaseManager(self)
        dbm.init_db()
        if default_data:
            dbm.insert_default_data()
Ejemplo n.º 3
0
 def set_home_config(self, values):
     syspath = self.conf.getEnvironmentSysPath("home")
     setconf = Configuration(syspath + '/conf/trac.ini')
     try:
         for (main, sub, value) in values:
             setconf.set(main, sub, value)
         setconf.save()
     except:
         return False
     return True
Ejemplo n.º 4
0
 def set_home_config(self, values):
     syspath = self.conf.getEnvironmentSysPath("home")
     setconf = Configuration(syspath + '/conf/trac.ini')
     try:
         for (main, sub, value) in values:
             setconf.set(main, sub, value)
         setconf.save()
     except:
         return False
     return True
Ejemplo n.º 5
0
    def test_set_and_save(self):
        configfile = open(self.filename, 'w')
        configfile.close()

        config = Configuration(self.filename)
        config.set('a', 'option', 'x')
        self.assertEquals('x', config.get('a', 'option'))
        config.save()

        configfile = open(self.filename, 'r')
        self.assertEquals(['[a]\n', 'option = x\n', '\n'],
                          configfile.readlines())
        configfile.close()
Ejemplo n.º 6
0
 def _update_sample_config(self):
     filename = os.path.join(self.env.config_file_path + ".sample")
     if not os.path.isfile(filename):
         return
     config = Configuration(filename)
     for (section, name), option in Option.get_registry().iteritems():
         config.set(section, name, option.dumps(option.default))
     try:
         config.save()
         self.log.info(
             "Wrote sample configuration file with the new " "settings and their default values: %s", filename
         )
     except IOError as e:
         self.log.warn("Couldn't write sample configuration file (%s)", e, exc_info=True)
Ejemplo n.º 7
0
Archivo: env.py Proyecto: t2y/trac
 def _update_sample_config(self):
     filename = os.path.join(self.env.path, 'conf', 'trac.ini.sample')
     if not os.path.isfile(filename):
         return
     config = Configuration(filename)
     for (section, name), option in Option.get_registry().iteritems():
         config.set(section, name, option.dumps(option.default))
     try:
         config.save()
         self.log.info("Wrote sample configuration file with the new "
                       "settings and their default values: %s",
                       filename)
     except IOError as e:
         self.log.warn("Couldn't write sample configuration file (%s)", e,
                       exc_info=True)
Ejemplo n.º 8
0
 def writeconfig(self, filepath, dicts=[]):
     """Writes or updates a config file. A list of dictionaries is used so
     that options for different aspects of the configuration can be kept
     separate while being able to update the same sections. Note that the
     result is order dependent where two dictionaries update the same option.
     """
     config = Configuration(filepath)
     file_changed = False
     for data in dicts:
         for section, options in data.iteritems():
             for key, value in options.iteritems():
                 if config.get(section, key, None) != value:
                     # This should be expected to generate a false positive
                     # when two dictionaries update the same option
                     file_changed = True
                 config.set(section, key, value)
     if file_changed:
         if os.path.exists(filepath):
             backupfile(filepath)
         config.save()
Ejemplo n.º 9
0
 def writeconfig(self, filepath, dicts=[]):
     """Writes or updates a config file. A list of dictionaries is used so
     that options for different aspects of the configuration can be kept
     separate while being able to update the same sections. Note that the
     result is order dependent where two dictionaries update the same option.
     """
     config = Configuration(filepath)
     file_changed = False
     for data in dicts:
         for section, options in data.iteritems():
             for key, value in options.iteritems():
                 if config.get(section, key, None) != value:
                     # This should be expected to generate a false positive
                     # when two dictionaries update the same option
                     file_changed = True
                 config.set(section, key, value)
     if file_changed:
         if os.path.exists(filepath):
             backupfile(filepath)
         config.save()
Ejemplo n.º 10
0
class EnvironmentStub(ComponentManager):
    """A stub of the trac.env.Environment object for testing."""
    def __init__(self, default_data=False, enable=None):
        ComponentManager.__init__(self)
        self.enabled_components = enable
        self.db = InMemoryDatabase()

        from trac.config import Configuration
        self.config = Configuration(None)

        from trac.log import logger_factory
        self.log = logger_factory('test')

        from trac.web.href import Href
        self.href = Href('/trac.cgi')
        self.abs_href = Href('http://example.org/trac.cgi')

        from trac import db_default
        for section, name, value in db_default.default_config:
            self.config.set(section, name, value)
        if default_data:
            cursor = self.db.cursor()
            for table, cols, vals in db_default.data:
                cursor.executemany(
                    "INSERT INTO %s (%s) VALUES (%s)" %
                    (table, ','.join(cols), ','.join(['%s' for c in cols])),
                    vals)
            self.db.commit()

    def component_activated(self, component):
        component.env = self
        component.config = self.config
        component.log = self.log

    def is_component_enabled(self, cls):
        if self.enabled_components is None:
            return True
        return cls in self.enabled_components

    def get_db_cnx(self):
        return self.db
Ejemplo n.º 11
0
    def create(self, options=[]):
        """Create the basic directory structure of the environment,
        initialize the database and populate the configuration file
        with default values.

        If options contains ('inherit', 'file'), default values will
        not be loaded; they are expected to be provided by that file
        or other options.
        """
        # Create the directory structure
        if not os.path.exists(self.path):
            os.mkdir(self.path)
        os.mkdir(self.get_log_dir())
        os.mkdir(self.get_htdocs_dir())
        os.mkdir(os.path.join(self.path, 'plugins'))

        # Create a few files
        create_file(os.path.join(self.path, 'VERSION'), _VERSION + '\n')
        create_file(
            os.path.join(self.path, 'README'),
            'This directory contains a Trac environment.\n'
            'Visit http://trac.edgewall.org/ for more information.\n')

        # Setup the default configuration
        os.mkdir(os.path.join(self.path, 'conf'))
        create_file(os.path.join(self.path, 'conf', 'trac.ini.sample'))
        config = Configuration(os.path.join(self.path, 'conf', 'trac.ini'))
        for section, name, value in options:
            config.set(section, name, value)
        config.save()
        self.setup_config()
        if not any((section, option) == ('inherit', 'file')
                   for section, option, value in options):
            self.config.set_defaults(self)
            self.config.save()

        # Create the database
        DatabaseManager(self).init_db()
Ejemplo n.º 12
0
Archivo: env.py Proyecto: pkdevbox/trac
    def create(self, options=[]):
        """Create the basic directory structure of the environment,
        initialize the database and populate the configuration file
        with default values.

        If options contains ('inherit', 'file'), default values will
        not be loaded; they are expected to be provided by that file
        or other options.
        """
        # Create the directory structure
        if not os.path.exists(self.path):
            os.mkdir(self.path)
        os.mkdir(self.get_log_dir())
        os.mkdir(self.get_htdocs_dir())
        os.mkdir(os.path.join(self.path, 'plugins'))

        # Create a few files
        create_file(os.path.join(self.path, 'VERSION'), _VERSION + '\n')
        create_file(os.path.join(self.path, 'README'),
                    'This directory contains a Trac environment.\n'
                    'Visit http://trac.edgewall.org/ for more information.\n')

        # Setup the default configuration
        os.mkdir(os.path.join(self.path, 'conf'))
        create_file(self.config_file_path + '.sample')
        config = Configuration(self.config_file_path)
        for section, name, value in options:
            config.set(section, name, value)
        config.save()
        self.setup_config()
        if not any((section, option) == ('inherit', 'file')
                   for section, option, value in options):
            self.config.set_defaults(self)
            self.config.save()

        # Create the database
        DatabaseManager(self).init_db()
Ejemplo n.º 13
0
    def do(self):
        # Make backup of trac.ini before configuring it
        try:
            shutil.copy(self.conf_file, self.conf_file_back)
        except Exception:
            conf.log.exception("Could not create trac.ini backup")
            return False

        # Open trac.ini for configuration
        config = None
        try:
            config = Configuration(self.conf_file)
        except Exception:
            conf.log.exception("Error while reading config file!")
            return False

        # Enable correct plugin for repository
        try:
            vcs_plugin = self.__vcs_plugin()
            if vcs_plugin:
                config.set('components', vcs_plugin, 'enabled')

            config.set('trac', 'repository_type', self.vcs_type)
        except Exception:
            conf.log.exception("Could not set static settings for trac")
            return False

        try:
            config.set('project', 'descr', self.project.description)
        except Exception:
            conf.log.exception("Couldn't set description")
            return False

        # Remove attachment size (to enable global setting)
        try:
            config.remove("attachment", "max_size")
        except Exception:
            conf.log.exception("Could not remove attachment config property for a new project")

        # Save configuration
        try:
            config.save()
        except Exception:
            conf.log.exception("Failed to save configuration")
            return False

        self.success = True
        return True
Ejemplo n.º 14
0
    def do(self):
        # Make backup of trac.ini before configuring it
        try:
            shutil.copy(self.conf_file, self.conf_file_back)
        except Exception:
            conf.log.exception("Could not create trac.ini backup")
            return False

        # Open trac.ini for configuration
        config = None
        try:
            config = Configuration(self.conf_file)
        except Exception:
            conf.log.exception("Error while reading config file!")
            return False

        # Enable correct plugin for repository
        try:
            vcs_plugin = self.__vcs_plugin()
            if vcs_plugin:
                config.set('components', vcs_plugin, 'enabled')

            config.set('trac', 'repository_type', self.vcs_type)
        except Exception:
            conf.log.exception("Could not set static settings for trac")
            return False

        try:
            config.set('project', 'descr', self.project.description)
        except Exception:
            conf.log.exception("Couldn't set description")
            return False

        # Remove attachment size (to enable global setting)
        try:
            config.remove("attachment", "max_size")
        except Exception:
            conf.log.exception("Could not remove attachment config property for a new project")

        # Save configuration
        try:
            config.save()
        except Exception:
            conf.log.exception("Failed to save configuration")
            return False

        self.success = True
        return True
Ejemplo n.º 15
0
class EnvironmentStub(Environment):
    """A stub of the trac.env.Environment object for testing."""

    href = abs_href = None
    global_databasemanager = None

    def __init__(self,
                 default_data=False,
                 enable=None,
                 disable=None,
                 path=None,
                 destroying=False):
        """Construct a new Environment stub object.

        :param default_data: If True, populate the database with some
                             defaults.
        :param enable: A list of component classes or name globs to
                       activate in the stub environment.
        """
        ComponentManager.__init__(self)
        Component.__init__(self)

        self.systeminfo = []

        import trac
        self.path = path
        if self.path is None:
            self.path = os.path.dirname(trac.__file__)
            if not os.path.isabs(self.path):
                self.path = os.path.join(os.getcwd(), self.path)

        # -- configuration
        self.config = Configuration(None)
        # We have to have a ticket-workflow config for ''lots'' of things to
        # work.  So insert the basic-workflow config here.  There may be a
        # better solution than this.
        load_workflow_config_snippet(self.config, 'basic-workflow.ini')
        self.config.set('logging', 'log_level', 'DEBUG')
        self.config.set('logging', 'log_type', 'stderr')
        if enable is not None:
            self.config.set('components', 'trac.*', 'disabled')
        else:
            self.config.set('components', 'tracopt.versioncontrol.svn.*',
                            'enabled')
        for name_or_class in enable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'enabled')
        for name_or_class in disable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'disabled')

        # -- logging
        from trac.log import logger_handler_factory
        self.log, self._log_handler = logger_handler_factory('test')

        # -- database
        self.config.set('components', 'trac.db.*', 'enabled')
        self.dburi = get_dburi()

        init_global = False
        if self.global_databasemanager:
            self.components[DatabaseManager] = global_databasemanager
        else:
            self.config.set('trac', 'database', self.dburi)
            self.global_databasemanager = DatabaseManager(self)
            self.config.set('trac', 'debug_sql', True)
            self.config.set('logging', 'log_type', 'stderr')
            self.config.set('logging', 'log_level', 'DEBUG')
            init_global = not destroying

        if default_data or init_global:
            self.reset_db(default_data)

        from trac.web.href import Href
        self.href = Href('/trac.cgi')
        self.abs_href = Href('http://example.org/trac.cgi')

        self.known_users = []
        translation.activate(locale_en)

    def reset_db(self, default_data=None):
        """Remove all data from Trac tables, keeping the tables themselves.
        :param default_data: after clean-up, initialize with default data
        :return: True upon success
        """
        from trac import db_default
        scheme, db_prop = _parse_db_str(self.dburi)
        tables = []
        remove_sqlite_db = False
        try:
            with self.db_transaction as db:
                db.rollback()  # make sure there's no transaction in progress
                # check the database version
                database_version = db(
                    "SELECT value FROM system WHERE name='database_version'")
                if database_version:
                    database_version = int(database_version[0][0])
                if database_version == db_default.db_version:
                    # same version, simply clear the tables (faster)
                    m = sys.modules[__name__]
                    reset_fn = 'reset_%s_db' % scheme
                    if hasattr(m, reset_fn):
                        tables = getattr(m, reset_fn)(self, db_prop)
                else:
                    # different version or version unknown, drop the tables
                    remove_sqlite_db = True
                    self.destroy_db(scheme, db_prop)
        except Exception, e:
            # "Database not found ...",
            # "OperationalError: no such table: system" or the like
            pass

        db = None  # as we might shutdown the pool     FIXME no longer needed!

        if scheme == 'sqlite' and remove_sqlite_db:
            path = db_prop['path']
            if path != ':memory:':
                if not os.path.isabs(path):
                    path = os.path.join(self.path, path)
                self.global_databasemanager.shutdown()
                os.remove(path)

        if not tables:
            self.global_databasemanager.init_db()
            # we need to make sure the next get_db_cnx() will re-create
            # a new connection aware of the new data model - see #8518.
            if self.dburi != 'sqlite::memory:':
                self.global_databasemanager.shutdown()

        with self.db_transaction as db:
            if default_data:
                for table, cols, vals in db_default.get_data(db):
                    db.executemany(
                        "INSERT INTO %s (%s) VALUES (%s)" %
                        (table, ','.join(cols), ','.join(['%s'
                                                          for c in cols])),
                        vals)
            else:
                db("INSERT INTO system (name, value) VALUES (%s, %s)",
                   ('database_version', str(db_default.db_version)))
Ejemplo n.º 16
0
class EnvironmentStub(Environment):
    """A stub of the trac.env.Environment object for testing."""

    href = abs_href = None
    global_databasemanager = None

    def __init__(self, default_data=False, enable=None, disable=None,
                 path=None, destroying=False):
        """Construct a new Environment stub object.

        :param default_data: If True, populate the database with some
                             defaults.
        :param enable: A list of component classes or name globs to
                       activate in the stub environment.
        """
        ComponentManager.__init__(self)
        Component.__init__(self)

        self.systeminfo = []

        import trac
        self.path = path
        if self.path is None:
            self.path = os.path.dirname(trac.__file__)
            if not os.path.isabs(self.path):
                self.path = os.path.join(os.getcwd(), self.path)

        # -- configuration
        self.config = Configuration(None)
        # We have to have a ticket-workflow config for ''lots'' of things to
        # work.  So insert the basic-workflow config here.  There may be a
        # better solution than this.
        load_workflow_config_snippet(self.config, 'basic-workflow.ini')
        self.config.set('logging', 'log_level', 'DEBUG')
        self.config.set('logging', 'log_type', 'stderr')
        if enable is not None:
            self.config.set('components', 'trac.*', 'disabled')
        else:
            self.config.set('components', 'tracopt.versioncontrol.svn.*',
                            'enabled')
        for name_or_class in enable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'enabled')
        for name_or_class in disable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'disabled')

        # -- logging
        from trac.log import logger_handler_factory
        self.log, self._log_handler = logger_handler_factory('test')

        # -- database
        self.config.set('components', 'trac.db.*', 'enabled')
        self.dburi = get_dburi()

        init_global = False
        if self.global_databasemanager:
            self.components[DatabaseManager] = self.global_databasemanager
        else:
            self.config.set('trac', 'database', self.dburi)
            self.global_databasemanager = DatabaseManager(self)
            self.config.set('trac', 'debug_sql', True)
            self.config.set('logging', 'log_type', 'stderr')
            self.config.set('logging', 'log_level', 'DEBUG')
            init_global = not destroying

        if default_data or init_global:
            self.reset_db(default_data)

        from trac.web.href import Href
        self.href = Href('/trac.cgi')
        self.abs_href = Href('http://example.org/trac.cgi')

        self.known_users = []
        translation.activate(locale_en)

    def reset_db(self, default_data=None):
        """Remove all data from Trac tables, keeping the tables themselves.
        :param default_data: after clean-up, initialize with default data
        :return: True upon success
        """
        from trac import db_default
        scheme, db_prop = _parse_db_str(self.dburi)
        tables = []
        remove_sqlite_db = False
        try:
            with self.db_transaction as db:
                db.rollback() # make sure there's no transaction in progress
                # check the database version
                database_version = db(
                    "SELECT value FROM system WHERE name='database_version'")
                if database_version:
                    database_version = int(database_version[0][0])
                if database_version == db_default.db_version:
                    # same version, simply clear the tables (faster)
                    m = sys.modules[__name__]
                    reset_fn = 'reset_%s_db' % scheme
                    if hasattr(m, reset_fn):
                        tables = getattr(m, reset_fn)(self, db_prop)
                else:
                    # different version or version unknown, drop the tables
                    remove_sqlite_db = True
                    self.destroy_db(scheme, db_prop)
        except Exception, e:
            # "Database not found ...",
            # "OperationalError: no such table: system" or the like
            pass

        db = None # as we might shutdown the pool     FIXME no longer needed!

        if scheme == 'sqlite' and remove_sqlite_db:
            path = db_prop['path']
            if path != ':memory:':
                if not os.path.isabs(path):
                    path = os.path.join(self.path, path)
                self.global_databasemanager.shutdown()
                os.remove(path)

        if not tables:
            self.global_databasemanager.init_db()
            # we need to make sure the next get_db_cnx() will re-create
            # a new connection aware of the new data model - see #8518.
            if self.dburi != 'sqlite::memory:':
                self.global_databasemanager.shutdown()

        with self.db_transaction as db:
            if default_data:
                for table, cols, vals in db_default.get_data(db):
                    db.executemany("INSERT INTO %s (%s) VALUES (%s)"
                                   % (table, ','.join(cols),
                                      ','.join(['%s' for c in cols])),
                                   vals)
            else:
                db("INSERT INTO system (name, value) VALUES (%s, %s)",
                   ('database_version', str(db_default.db_version)))
Ejemplo n.º 17
0
class EnvironmentStub(Environment):
    """A stub of the trac.env.Environment class for testing."""

    required = False
    abstract = True

    def __init__(self,
                 default_data=False,
                 enable=None,
                 disable=None,
                 path=None,
                 destroying=False,
                 config=None):
        """Construct a new Environment stub object.

        :param default_data: If True, populate the database with some
                             defaults.
        :param enable: A list of component classes or name globs to
                       activate in the stub environment.
        :param disable: A list of component classes or name globs to
                        deactivate in the stub environment.
        :param path: The location of the environment in the file system.
                     No files or directories are created when specifying
                     this parameter.
        :param destroying: If True, the database will not be reset. This is
                           useful for cases when the object is being
                           constructed in order to call `destroy_db`.
        :param config: A list of (section, key, value) configuration
                       tuples.
        """
        if enable is not None and not isinstance(enable, (list, tuple)):
            raise TypeError('Keyword argument "enable" must be a list')
        if disable is not None and not isinstance(disable, (list, tuple)):
            raise TypeError('Keyword argument "disable" must be a list')

        ComponentManager.__init__(self)

        self._old_registry = None
        self._old_components = None

        import trac
        self.path = path
        if self.path is None:
            self.path = os.path.abspath(os.path.dirname(trac.__file__))
        self.path = os.path.normpath(os.path.normcase(self.path))

        # -- configuration
        self.config = Configuration(None)
        # We have to have a ticket-workflow config for ''lots'' of things to
        # work.  So insert the basic-workflow config here.  There may be a
        # better solution than this.
        load_workflow_config_snippet(self.config, 'basic-workflow.ini')
        self.config.set('logging', 'log_level', 'DEBUG')
        self.config.set('logging', 'log_type', 'none')  # Ignored.
        if enable is not None:
            self.config.set('components', 'trac.*', 'disabled')
        else:
            self.config.set('components', 'tracopt.versioncontrol.*',
                            'enabled')
        for name_or_class in enable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'enabled')
        for name_or_class in disable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'disabled')
        self.config.set('trac', 'permission_policies',
                        'DefaultPermissionPolicy, LegacyAttachmentPolicy')
        for item in config or []:
            self.config.set(*item)

        # -- logging
        self.setup_log()

        # -- database
        self.dburi = get_dburi()
        self.config.set('components', 'trac.db.*', 'enabled')
        self.config.set('trac', 'database', self.dburi)

        if not destroying:
            self.reset_db(default_data)

        self.config.set('trac', 'base_url', 'http://example.org/trac.cgi')

        translation.activate(locale_en)

    def setup_log(self):
        self.log = logging.getLogger('trac.test')
        level = self.log_level.upper()
        level_as_int = trac.log.LOG_LEVEL_MAP.get(level)
        self.log.setLevel(level_as_int)
        handler_cls = logging.handlers.BufferingHandler
        if not self.log.handlers:
            log_handler = handler_cls(sys.maxsize)  # Never flush implicitly.
            formatter = logging.Formatter(self.log_format)
            log_handler.setFormatter(formatter)
            self.log.addHandler(log_handler)
        elif len(self.log.handlers) == 1 and \
                isinstance(self.log.handlers[0], handler_cls):
            self.log.handlers[0].flush()  # Reset buffer.
        else:
            raise TracError("Logger has unexpected handler(s).")

    @property
    def log_messages(self):
        """Returns a list of tuples (level, message)."""
        return [(record.levelname, record.getMessage())
                for record in self.log.handlers[0].buffer]

    def reset_db(self, default_data=None):
        """Remove all data from Trac tables, keeping the tables themselves.

        :param default_data: after clean-up, initialize with default data
        :return: True upon success
        """
        from trac import db_default
        tables = []
        dbm = DatabaseManager(self)
        try:
            db_version = dbm.get_database_version()
        except (TracError, self.db_exc.DatabaseError):
            pass
        else:
            if db_version == db_default.db_version:
                # same version, simply clear the tables (faster)
                tables = dbm.reset_tables()
            else:
                # different version or version unknown, drop the tables
                self.destroy_db()

        if not tables:
            dbm.init_db()
            # Make sure the next db_query()/db_transaction() will create
            # a new connection aware of the new data model - see #8518.
            if self.dburi != 'sqlite::memory:':
                dbm.shutdown()

        if default_data:
            dbm.insert_into_tables(db_default.get_data)
        else:
            dbm.set_database_version(db_default.db_version)

    def destroy_db(self):
        """Destroy the database."""
        try:
            DatabaseManager(self).destroy_db()
        except (TracError, self.db_exc.DatabaseError):
            pass

    def clear_component_registry(self):
        """Clear the component registry.

        The registry entries are saved entries so they can be restored
        later using the `restore_component_registry` method.

        :since: 1.0.11
        :since 1.3.2: Deprecated and will be removed in 1.5.1. Create
                      components in `setUpClass` and remove them in
                      `tearDownClass` using `ComponentMeta.deregister`.
        """
        self._old_registry = ComponentMeta._registry
        self._old_components = ComponentMeta._components
        ComponentMeta._registry = {}

    def restore_component_registry(self):
        """Restore the component registry.

        The component registry must have been cleared and saved using
        the `clear_component_registry` method.

        :since: 1.0.11
        :since 1.3.2: Deprecated and will be removed in 1.5.1. Create
                      components in `setUpClass` and remove them in
                      `tearDownClass` using `ComponentMeta.deregister`.
        """
        if self._old_registry is None:
            raise TracError("The clear_component_registry method must be "
                            "called first.")
        ComponentMeta._registry = self._old_registry
        ComponentMeta._components = self._old_components

    # tearDown helper

    def reset_db_and_disk(self):
        """Performs a complete environment reset in a robust way.

        The database is reset, then the connections are shut down, and
        finally all environment files are removed from the disk.
        """
        self.reset_db()
        self.shutdown()  # really closes the db connections
        rmtree(self.env.path)
        if self._old_registry is not None:
            self.restore_component_registry()

    # other utilities

    def insert_users(self, users):
        """Insert a tuple representing a user session to the
        `session` and `session_attributes` tables.

        The tuple can be length 3 with entries username, name and
        email, in which case an authenticated user is assumed. The
        tuple can also be length 4, with the last entry specifying
        `1` for an authenticated user or `0` for an unauthenticated
        user.
        """
        with self.db_transaction as db:
            for row in users:
                if len(row) == 3:
                    username, name, email = row
                    authenticated = 1
                else:  # len(row) == 4
                    username, name, email, authenticated = row
                db("INSERT INTO session VALUES (%s, %s, %s)",
                   (username, authenticated, int(time_now())))
                db("INSERT INTO session_attribute VALUES (%s,%s,'name',%s)",
                   (username, authenticated, name))
                db("INSERT INTO session_attribute VALUES (%s,%s,'email',%s)",
                   (username, authenticated, email))

    # overridden

    def is_component_enabled(self, cls):
        if self._component_name(cls).startswith('__main__.'):
            return True
        return Environment.is_component_enabled(self, cls)
Ejemplo n.º 18
0
class EnvironmentStub(Environment):
    """A stub of the trac.env.Environment object for testing."""

    global_databasemanager = None
    required = False

    def __init__(self, default_data=False, enable=None, disable=None,
                 path=None, destroying=False):
        """Construct a new Environment stub object.

        :param default_data: If True, populate the database with some
                             defaults.
        :param enable: A list of component classes or name globs to
                       activate in the stub environment.
        :param disable: A list of component classes or name globs to
                        deactivate in the stub environment.
        :param path: The location of the environment in the file system.
                     No files or directories are created when specifying
                     this parameter.
        :param destroying: If True, the database will not be reset. This is
                           useful for cases when the object is being
                           constructed in order to call `destroy_db`.
        """
        if enable is not None and not isinstance(enable, (list, tuple)):
            raise TypeError('Keyword argument "enable" must be a list')
        if disable is not None and not isinstance(disable, (list, tuple)):
            raise TypeError('Keyword argument "disable" must be a list')

        ComponentManager.__init__(self)

        self.systeminfo = []

        import trac
        self.path = path
        if self.path is None:
            self.path = os.path.dirname(trac.__file__)
            if not os.path.isabs(self.path):
                self.path = os.path.join(os.getcwd(), self.path)

        # -- configuration
        self.config = Configuration(None)
        # We have to have a ticket-workflow config for ''lots'' of things to
        # work.  So insert the basic-workflow config here.  There may be a
        # better solution than this.
        load_workflow_config_snippet(self.config, 'basic-workflow.ini')
        self.config.set('logging', 'log_level', 'DEBUG')
        self.config.set('logging', 'log_type', 'stderr')
        if enable is not None:
            self.config.set('components', 'trac.*', 'disabled')
        else:
            self.config.set('components', 'tracopt.versioncontrol.*',
                            'enabled')
        for name_or_class in enable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'enabled')
        for name_or_class in disable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'disabled')

        # -- logging
        from trac.log import logger_handler_factory
        self.log, self._log_handler = logger_handler_factory('test')

        # -- database
        self.config.set('components', 'trac.db.*', 'enabled')
        self.dburi = get_dburi()

        init_global = False
        if self.global_databasemanager:
            self.components[DatabaseManager] = self.global_databasemanager
        else:
            self.config.set('trac', 'database', self.dburi)
            self.global_databasemanager = DatabaseManager(self)
            self.config.set('trac', 'debug_sql', True)
            init_global = not destroying

        if default_data or init_global:
            self.reset_db(default_data)

        self.config.set('trac', 'base_url', 'http://example.org/trac.cgi')

        self.known_users = []
        translation.activate(locale_en)

    def reset_db(self, default_data=None):
        """Remove all data from Trac tables, keeping the tables themselves.
        :param default_data: after clean-up, initialize with default data
        :return: True upon success
        """
        from trac import db_default
        scheme, db_prop = _parse_db_str(self.dburi)
        tables = []
        remove_sqlite_db = False
        try:
            with self.db_transaction as db:
                db.rollback()  # make sure there's no transaction in progress
                # check the database version
                database_version = self.get_version()
        except Exception:
            # "Database not found ...",
            # "OperationalError: no such table: system" or the like
            pass
        else:
            if database_version == db_default.db_version:
                # same version, simply clear the tables (faster)
                m = sys.modules[__name__]
                reset_fn = 'reset_%s_db' % scheme
                if hasattr(m, reset_fn):
                    tables = getattr(m, reset_fn)(self, db_prop)
            else:
                # different version or version unknown, drop the tables
                remove_sqlite_db = True
                self.destroy_db(scheme, db_prop)

        if scheme == 'sqlite' and remove_sqlite_db:
            path = db_prop['path']
            if path != ':memory:':
                if not os.path.isabs(path):
                    path = os.path.join(self.path, path)
                self.global_databasemanager.shutdown()
                os.remove(path)

        if not tables:
            self.global_databasemanager.init_db()
            # we need to make sure the next get_db_cnx() will re-create
            # a new connection aware of the new data model - see #8518.
            if self.dburi != 'sqlite::memory:':
                self.global_databasemanager.shutdown()

        with self.db_transaction as db:
            if default_data:
                for table, cols, vals in db_default.get_data(db):
                    db.executemany("INSERT INTO %s (%s) VALUES (%s)"
                                   % (table, ','.join(cols),
                                      ','.join(['%s'] * len(cols))), vals)
            else:
                db("INSERT INTO system (name, value) VALUES (%s, %s)",
                   ('database_version', str(db_default.db_version)))

    def destroy_db(self, scheme=None, db_prop=None):
        if not (scheme and db_prop):
            scheme, db_prop = _parse_db_str(self.dburi)
        try:
            with self.db_transaction as db:
                if scheme == 'postgres' and db.schema:
                    db('DROP SCHEMA %s CASCADE' % db.quote(db.schema))
                elif scheme == 'mysql':
                    for table in db.get_table_names():
                        db("DROP TABLE IF EXISTS `%s`" % table)
        except Exception:
            # "TracError: Database not found...",
            # psycopg2.ProgrammingError: schema "tractest" does not exist
            pass
        return False

    # overridden

    def is_component_enabled(self, cls):
        if self._component_name(cls).startswith('__main__.'):
            return True
        return Environment.is_component_enabled(self, cls)

    def get_known_users(self, cnx=None):
        return self.known_users
Ejemplo n.º 19
0
class MockEnvironment(Environment):
    """
    An mock Environment object which does not need real environment.
    Useful for when needing real environment like access to loaded plugins etc
    but no environment is at hand. Looks like normal project by default but can be
    made to look like home project environment or hybrid of both.

    .. WARNING:: Be careful when using this!
    """
    def __init__(self, config_file=None, enabled_plugins=None, path=None):
        """
        :param str config_file: path to the main configuration file.
            Defaults to ``/etc/trac/project.ini`` which is normal project configuration.
        :param list enabled_plugins:
            An explicit list of plugins to load, instead of reading enabled [components]
            from ``config_file``. If empty list is given no plugins are loaded.
            However plugins can be still listed via ``extra_plugins`` arg.
        :param str path: path to the imaginary trac location.
        """
        if config_file is None:
            # TODO: switch to some constant when can be done without conf
            from multiproject.core.configuration import conf
            config_file = conf.config_file

        # From Environment.setup_config:
        self.config = Configuration(config_file)

        if path is None:
            path = os.path.join(self.config.get('multiproject', 'sys_projects_root'), '__mock_environment__')
        if enabled_plugins is not None:
            # explicit list given, disable all from configuration
            for key, val in self.config.options('components'):
                self.config.remove('components', key)
            # and replace with the given list
            for plugin in enabled_plugins:
                self.config.set('components', plugin, u'enabled')

        # We override the Environment.__init__ here.

        # Environment.__init__ is as follows:

        # ComponentManager.__init__(self)
        #
        # self.path = path
        # self.systeminfo = []
        # self._href = self._abs_href = None
        #
        # if create:
        #     self.create(options)
        # else:
        #     self.verify()
        #     self.setup_config()
        #
        # if create:
        #     for setup_participant in self.setup_participants:
        #         setup_participant.environment_created()

        # The Environment.setup_config is as follows:

        # self.config = Configuration(os.path.join(self.path, 'conf',
        #         'trac.ini'))
        #         self.setup_log()
        #         from trac.loader import load_components
        #         plugins_dir = self.shared_plugins_dir
        #         load_components(self, plugins_dir and (plugins_dir,))

        # We use suitable lines from these as follows:

        # From Environment.__init__:
        ComponentManager.__init__(self)

        self.path = path
        self.systeminfo = []
        self._href = self._abs_href = None

        # Our own plugin hack ends here, and setup_config lines continue here
        self.setup_log()
        from trac.loader import load_components
        load_components(self)
Ejemplo n.º 20
0
class EnvironmentStub(Environment):
    """A stub of the trac.env.Environment object for testing."""

    href = abs_href = None
    dbenv = db = None

    def __init__(self, default_data=False, enable=None):
        """Construct a new Environment stub object.

        :param default_data: If True, populate the database with some
                             defaults.
        :param enable: A list of component classes or name globs to
                       activate in the stub environment.
        """
        ComponentManager.__init__(self)
        Component.__init__(self)
        self.systeminfo = []

        import trac
        self.path = os.path.dirname(trac.__file__)
        if not os.path.isabs(self.path):
            self.path = os.path.join(os.getcwd(), self.path)

        # -- configuration
        self.config = Configuration(None)
        # We have to have a ticket-workflow config for ''lots'' of things to
        # work.  So insert the basic-workflow config here.  There may be a
        # better solution than this.
        load_workflow_config_snippet(self.config, 'basic-workflow.ini')
        self.config.set('logging', 'log_level', 'DEBUG')
        self.config.set('logging', 'log_type', 'stderr')
        if enable is not None:
            self.config.set('components', 'trac.*', 'disabled')
        for name_or_class in enable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'enabled')

        # -- logging
        from trac.log import logger_handler_factory
        self.log, self._log_handler = logger_handler_factory('test')

        # -- database
        self.dburi = get_dburi()
        if self.dburi.startswith('sqlite'):
            self.config.set('trac', 'database', 'sqlite::memory:')
            self.db = InMemoryDatabase()

        if default_data:
            self.reset_db(default_data)

        from trac.web.href import Href
        self.href = Href('/trac.cgi')
        self.abs_href = Href('http://example.org/trac.cgi')

        self.known_users = []
        translation.activate(Locale and Locale('en', 'US'))

    def get_read_db(self):
        return self.get_db_cnx()

    def get_db_cnx(self, destroying=False):
        if self.db:
            return self.db  # in-memory SQLite

        # As most of the EnvironmentStubs are built at startup during
        # the test suite formation and the creation of test cases, we can't
        # afford to create a real db connection for each instance.
        # So we create a special EnvironmentStub instance in charge of
        # getting the db connections for all the other instances.
        dbenv = EnvironmentStub.dbenv
        if not dbenv:
            dbenv = EnvironmentStub.dbenv = EnvironmentStub()
            dbenv.config.set('trac', 'database', self.dburi)
            if not destroying:
                self.reset_db()  # make sure we get rid of previous garbage
        return DatabaseManager(dbenv).get_connection()

    def reset_db(self, default_data=None):
        """Remove all data from Trac tables, keeping the tables themselves.
        :param default_data: after clean-up, initialize with default data
        :return: True upon success
        """
        from trac import db_default
        if EnvironmentStub.dbenv:
            db = self.get_db_cnx()
            scheme, db_prop = _parse_db_str(self.dburi)

            tables = []
            db.rollback()  # make sure there's no transaction in progress
            try:
                # check the database version
                cursor = db.cursor()
                cursor.execute("SELECT value FROM system "
                               "WHERE name='database_version'")
                database_version = cursor.fetchone()
                if database_version:
                    database_version = int(database_version[0])
                if database_version == db_default.db_version:
                    # same version, simply clear the tables (faster)
                    m = sys.modules[__name__]
                    reset_fn = 'reset_%s_db' % scheme
                    if hasattr(m, reset_fn):
                        tables = getattr(m, reset_fn)(db, db_prop)
                else:
                    # different version or version unknown, drop the tables
                    self.destroy_db(scheme, db_prop)
            except:
                db.rollback()
                # tables are likely missing

            if not tables:
                del db
                dm = DatabaseManager(EnvironmentStub.dbenv)
                dm.init_db()
                # we need to make sure the next get_db_cnx() will re-create
                # a new connection aware of the new data model - see #8518.
                dm.shutdown()

        db = self.get_db_cnx()
        cursor = db.cursor()
        if default_data:
            for table, cols, vals in db_default.get_data(db):
                cursor.executemany(
                    "INSERT INTO %s (%s) VALUES (%s)" %
                    (table, ','.join(cols), ','.join(['%s' for c in cols])),
                    vals)
        elif EnvironmentStub.dbenv:
            cursor.execute(
                "INSERT INTO system (name, value) "
                "VALUES (%s, %s)",
                ('database_version', str(db_default.db_version)))
        db.commit()

    def destroy_db(self, scheme=None, db_prop=None):
        if not (scheme and db_prop):
            scheme, db_prop = _parse_db_str(self.dburi)

        db = self.get_db_cnx(destroying=True)
        cursor = db.cursor()
        try:
            if scheme == 'postgres' and db.schema:
                cursor.execute('DROP SCHEMA "%s" CASCADE' % db.schema)
            elif scheme == 'mysql':
                dbname = os.path.basename(db_prop['path'])
                cursor = db.cursor()
                cursor.execute(
                    'SELECT table_name FROM '
                    '  information_schema.tables '
                    'WHERE table_schema=%s', (dbname, ))
                tables = cursor.fetchall()
                for t in tables:
                    cursor.execute('DROP TABLE IF EXISTS `%s`' % t)
            db.commit()
        except Exception:
            db.rollback()

    # overriden

    def is_component_enabled(self, cls):
        if self._component_name(cls).startswith('__main__.'):
            return True
        return Environment.is_component_enabled(self, cls)

    def get_known_users(self, cnx=None):
        return self.known_users
Ejemplo n.º 21
0
    def sync(self, project):
        """Sync the trac environment with cydra
        
        This sets the options returned by ``get_default_options`` and adds Trac's own defaults if necessary"""
        if not self.has_env(project):
            logger.warning('Project %s has no Trac Environment to sync',
                           project.name)
            return

        tracini = os.path.join(self.get_env_path(project), 'conf', 'trac.ini')
        options = self.get_default_options(project)

        # if inherit is enabled, the default values are supposed to be in
        # the inherited file. Thus, we can truncate the config file to get a bare minimum
        if 'inherit_config' in self.component_config:
            options.append(
                ('inherit', 'file', self.component_config['inherit_config']))
            with open(tracini, 'w') as f:
                f.truncate()

        # re-create the configuration file
        config = Configuration(tracini)
        for section, name, value in options:
            config.set(section, name, value)
        config.save()

        # load defaults
        if not any((section, option) == ('inherit', 'file')
                   for section, option, value in options):
            config.set_defaults()
            config.save()

        # check if repositories in cydra match repositories in trac
        env = Environment(self.get_env_path(project))
        rm = RepositoryManager(env)
        trac_repos = rm.get_real_repositories()
        trac_repo_names = [r.reponame for r in trac_repos]

        for repotype, repos in project.data.get('plugins', {}).get('trac',
                                                                   {}).items():
            for repo, tracname in (repos or {}).items():
                if tracname not in trac_repo_names:
                    logger.warning(
                        "Removing trac mapping from cydra for %s repo %s",
                        repo, tracname)
                    del repos[repo]
                    if not repos:
                        del project.data.get('plugins', {}).get('trac',
                                                                {})[repotype]

        # Now do the reverse
        revmap = dict([(y, x) for (x, y) in self.typemap.items()])

        for repo in trac_repos:
            logger.debug('Looking at trac repo %s', repo.reponame)

            try:
                baseparts = repo.get_base().split(
                    ':'
                )  # This is extremely naiive and possibly breaks some time
                repotype, path = baseparts[0], baseparts[-1]
            except:
                logger.error("Unable to parse: " + repo.get_base())

            reponame = os.path.basename(path)
            if repotype == 'git':
                reponame = reponame[:-4]

            try:
                repository = project.get_repository(revmap[repotype], reponame)
            except:
                logger.error("Unable to locate %s %s (%s)", repotype, reponame,
                             path)
                repository = None

            logger.debug('Cydra repo %r', repository)

            if repository:
                # set this mapping if not there already
                project.data.setdefault('plugins', {}).setdefault(
                    'trac', {}).setdefault(repository.type,
                                           {})[repository.name] = repo.reponame
                logger.info('Setting trac mapping for %s %s -> %s',
                            repository.type, repository.name, repo.reponame)
            else:
                logger.error("Unable to load %s %s (%s)", revmap[repotype],
                             reponame, path)

        project.save()
Ejemplo n.º 22
0
    def _do_update(self, req):
        """Update component enablement."""
        components = req.args.getlist('component')
        oenabled = req.args.getlist('enable')
        penabled = req.args.getlist('prjenable')

        changes = False
        changed = {}

        # Set global project configuration
        prjconf = Configuration(conf.global_conf_path)

        for component in components:
            c_state = self.get_project_component_state(component, self.config)
            c_activated = (component in oenabled)

            if self.is_plugin_changed(c_state, c_activated):
                self.config.set('components', component,
                                c_activated and 'enabled' or 'disabled')
                self.log.info('%sabling component %s',
                                c_activated and 'En' or 'Dis', component)
                changes = True

            if prjconf:
                cip_state = self.get_project_component_state(component, prjconf)
                cip_activated = (component in penabled)

                if self.is_plugin_changed(cip_state, cip_activated):
                    self.log.info('%sabling project component %s',
                                    cip_activated and 'En' or 'Dis', component)
                    changed[component] = cip_activated and 'enabled' or 'disabled'
                    changes = True

        if prjconf:
            scomponents = req.args.getlist('setting')
            static_items = req.args.getlist('static_setting')

            for scomponent in scomponents:
                values = self.get_project_component_value(scomponent, None, prjconf)
                saved_value = self.parse_value(0, values)
                saved_static = self.parse_value(1, values)

                current_value = req.args.get(scomponent + '.value').replace('|', '')
                current_static = (scomponent in static_items)

                if saved_value != current_value or saved_static != current_static:
                    if current_static:
                        final_value = current_value
                    else:
                        final_value = current_value + '|no'

                    prjconf.set('settings', scomponent, final_value)
                    changes = True

        if changes:
            self.config.save()

            if prjconf:
                for key in changed.keys():
                    prjconf.set('components', key, changed[key])
                prjconf.save()
Ejemplo n.º 23
0
    def _do_update(self, req):
        """Update component enablement."""
        components = req.args.getlist('component')
        oenabled = req.args.getlist('enable')
        penabled = req.args.getlist('prjenable')

        changes = False
        changed = {}

        # Set global project configuration
        prjconf = Configuration(conf.global_conf_path)

        for component in components:
            c_state = self.get_project_component_state(component, self.config)
            c_activated = (component in oenabled)

            if self.is_plugin_changed(c_state, c_activated):
                self.config.set('components', component,
                                c_activated and 'enabled' or 'disabled')
                self.log.info('%sabling component %s', c_activated and 'En'
                              or 'Dis', component)
                changes = True

            if prjconf:
                cip_state = self.get_project_component_state(
                    component, prjconf)
                cip_activated = (component in penabled)

                if self.is_plugin_changed(cip_state, cip_activated):
                    self.log.info('%sabling project component %s',
                                  cip_activated and 'En' or 'Dis', component)
                    changed[
                        component] = cip_activated and 'enabled' or 'disabled'
                    changes = True

        if prjconf:
            scomponents = req.args.getlist('setting')
            static_items = req.args.getlist('static_setting')

            for scomponent in scomponents:
                values = self.get_project_component_value(
                    scomponent, None, prjconf)
                saved_value = self.parse_value(0, values)
                saved_static = self.parse_value(1, values)

                current_value = req.args.get(scomponent + '.value').replace(
                    '|', '')
                current_static = (scomponent in static_items)

                if saved_value != current_value or saved_static != current_static:
                    if current_static:
                        final_value = current_value
                    else:
                        final_value = current_value + '|no'

                    prjconf.set('settings', scomponent, final_value)
                    changes = True

        if changes:
            self.config.save()

            if prjconf:
                for key in changed.keys():
                    prjconf.set('components', key, changed[key])
                prjconf.save()
Ejemplo n.º 24
0
class EnvironmentStub(Environment):
    """A stub of the trac.env.Environment class for testing."""

    global_databasemanager = None
    required = False
    abstract = True

    def __init__(self, default_data=False, enable=None, disable=None,
                 path=None, destroying=False):
        """Construct a new Environment stub object.

        :param default_data: If True, populate the database with some
                             defaults.
        :param enable: A list of component classes or name globs to
                       activate in the stub environment.
        :param disable: A list of component classes or name globs to
                        deactivate in the stub environment.
        :param path: The location of the environment in the file system.
                     No files or directories are created when specifying
                     this parameter.
        :param destroying: If True, the database will not be reset. This is
                           useful for cases when the object is being
                           constructed in order to call `destroy_db`.
        """
        if enable is not None and not isinstance(enable, (list, tuple)):
            raise TypeError('Keyword argument "enable" must be a list')
        if disable is not None and not isinstance(disable, (list, tuple)):
            raise TypeError('Keyword argument "disable" must be a list')

        ComponentManager.__init__(self)

        self.systeminfo = []

        import trac
        self.path = path
        if self.path is None:
            self.path = os.path.dirname(trac.__file__)
            if not os.path.isabs(self.path):
                self.path = os.path.join(os.getcwd(), self.path)

        # -- configuration
        self.config = Configuration(None)
        # We have to have a ticket-workflow config for ''lots'' of things to
        # work.  So insert the basic-workflow config here.  There may be a
        # better solution than this.
        load_workflow_config_snippet(self.config, 'basic-workflow.ini')
        self.config.set('logging', 'log_level', 'DEBUG')
        self.config.set('logging', 'log_type', 'stderr')
        if enable is not None:
            self.config.set('components', 'trac.*', 'disabled')
        else:
            self.config.set('components', 'tracopt.versioncontrol.*',
                            'enabled')
        for name_or_class in enable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'enabled')
        for name_or_class in disable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'disabled')

        # -- logging
        from trac.log import logger_handler_factory
        self.log, self._log_handler = logger_handler_factory('test')

        # -- database
        self.config.set('components', 'trac.db.*', 'enabled')
        self.dburi = get_dburi()

        init_global = False
        if self.global_databasemanager:
            self.components[DatabaseManager] = self.global_databasemanager
        else:
            self.config.set('trac', 'database', self.dburi)
            self.global_databasemanager = DatabaseManager(self)
            self.config.set('trac', 'debug_sql', True)
            init_global = not destroying

        if default_data or init_global:
            self.reset_db(default_data)

        self.config.set('trac', 'base_url', 'http://example.org/trac.cgi')

        translation.activate(locale_en)

    def reset_db(self, default_data=None):
        """Remove all data from Trac tables, keeping the tables themselves.

        :param default_data: after clean-up, initialize with default data
        :return: True upon success
        """
        from trac import db_default
        tables = []
        dbm = self.global_databasemanager
        try:
            with self.db_transaction as db:
                db.rollback()  # make sure there's no transaction in progress
                # check the database version
                db_version = dbm.get_database_version()
        except (TracError, self.env.db_exc.DatabaseError):
            pass
        else:
            if db_version == db_default.db_version:
                # same version, simply clear the tables (faster)
                tables = dbm.reset_tables()
            else:
                # different version or version unknown, drop the tables
                self.destroy_db()

        if not tables:
            dbm.init_db()
            # we need to make sure the next get_db_cnx() will re-create
            # a new connection aware of the new data model - see #8518.
            if self.dburi != 'sqlite::memory:':
                dbm.shutdown()

        if default_data:
            dbm.insert_into_tables(db_default.get_data)
        else:
            dbm.set_database_version(db_default.db_version)

    def destroy_db(self, scheme=None, db_prop=None):
        """Destroy the database.

        :since 1.1.5: the `scheme` and `db_prop` parameters are deprecated and
                      will be removed in 1.3.1.
        """
        try:
            self.global_databasemanager.destroy_db()
        except (TracError, self.db_exc.DatabaseError):
            pass
        return False

    def insert_known_users(self, users):
        with self.env.db_transaction as db:
            for username, name, email in users:
                db("INSERT INTO session VALUES (%s, %s, %s)",
                   (username, 1, int(time.time())))
                db("INSERT INTO session_attribute VALUES (%s,%s,'name',%s)",
                   (username, 1, name))
                db("INSERT INTO session_attribute VALUES (%s,%s,'email',%s)",
                   (username, 1, email))

    # overridden

    def is_component_enabled(self, cls):
        if self._component_name(cls).startswith('__main__.'):
            return True
        return Environment.is_component_enabled(self, cls)
Ejemplo n.º 25
0
class EnvironmentStub(Environment):
    """A stub of the trac.env.Environment object for testing."""

    href = abs_href = None
    dbenv = db = None

    def __init__(self, default_data=False, enable=None):
        """Construct a new Environment stub object.

        :param default_data: If True, populate the database with some
                             defaults.
        :param enable: A list of component classes or name globs to
                       activate in the stub environment.
        """
        ComponentManager.__init__(self)
        Component.__init__(self)
        self.systeminfo = []

        import trac
        self.path = os.path.dirname(trac.__file__)
        if not os.path.isabs(self.path):
            self.path = os.path.join(os.getcwd(), self.path)

        # -- configuration
        self.config = Configuration(None)
        # We have to have a ticket-workflow config for ''lots'' of things to
        # work.  So insert the basic-workflow config here.  There may be a
        # better solution than this.
        load_workflow_config_snippet(self.config, 'basic-workflow.ini')
        self.config.set('logging', 'log_level', 'DEBUG')
        self.config.set('logging', 'log_type', 'stderr')
        if enable is not None:
            self.config.set('components', 'trac.*', 'disabled')
        for name_or_class in enable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'enabled')

        # -- logging
        from trac.log import logger_handler_factory
        self.log, self._log_handler = logger_handler_factory('test')

        # -- database
        self.dburi = get_dburi()
        if self.dburi.startswith('sqlite'):
            self.config.set('trac', 'database', 'sqlite::memory:')
            self.db = InMemoryDatabase()

        if default_data:
            self.reset_db(default_data)

        from trac.web.href import Href
        self.href = Href('/trac.cgi')
        self.abs_href = Href('http://example.org/trac.cgi')

        self.known_users = []
        translation.activate(Locale and Locale('en', 'US'))
        
    def get_read_db(self):
        return self.get_db_cnx()
    
    def get_db_cnx(self, destroying=False):
        if self.db:
            return self.db # in-memory SQLite

        # As most of the EnvironmentStubs are built at startup during
        # the test suite formation and the creation of test cases, we can't
        # afford to create a real db connection for each instance.
        # So we create a special EnvironmentStub instance in charge of
        # getting the db connections for all the other instances.
        dbenv = EnvironmentStub.dbenv
        if not dbenv:
            dbenv = EnvironmentStub.dbenv = EnvironmentStub()
            dbenv.config.set('trac', 'database', self.dburi)
            if not destroying:
                self.reset_db() # make sure we get rid of previous garbage
        return DatabaseManager(dbenv).get_connection()

    def reset_db(self, default_data=None):
        """Remove all data from Trac tables, keeping the tables themselves.
        :param default_data: after clean-up, initialize with default data
        :return: True upon success
        """
        from trac import db_default
        if EnvironmentStub.dbenv:
            db = self.get_db_cnx()
            scheme, db_prop = _parse_db_str(self.dburi)

            tables = []
            db.rollback() # make sure there's no transaction in progress
            try:
                # check the database version
                cursor = db.cursor()
                cursor.execute("SELECT value FROM system "
                               "WHERE name='database_version'")
                database_version = cursor.fetchone()
                if database_version:
                    database_version = int(database_version[0])
                if database_version == db_default.db_version:
                    # same version, simply clear the tables (faster)
                    m = sys.modules[__name__]
                    reset_fn = 'reset_%s_db' % scheme
                    if hasattr(m, reset_fn):
                        tables = getattr(m, reset_fn)(db, db_prop)
                else:
                    # different version or version unknown, drop the tables
                    self.destroy_db(scheme, db_prop)
            except:
                db.rollback()
                # tables are likely missing

            if not tables:
                del db
                dm = DatabaseManager(EnvironmentStub.dbenv)
                dm.init_db()
                # we need to make sure the next get_db_cnx() will re-create 
                # a new connection aware of the new data model - see #8518.
                dm.shutdown() 

        db = self.get_db_cnx()
        cursor = db.cursor()
        if default_data:
            for table, cols, vals in db_default.get_data(db):
                cursor.executemany("INSERT INTO %s (%s) VALUES (%s)"
                                   % (table, ','.join(cols),
                                      ','.join(['%s' for c in cols])),
                                   vals)
        elif EnvironmentStub.dbenv:
            cursor.execute("INSERT INTO system (name, value) "
                           "VALUES (%s, %s)",
                           ('database_version', str(db_default.db_version)))
        db.commit()

    def destroy_db(self, scheme=None, db_prop=None):
        if not (scheme and db_prop):
            scheme, db_prop = _parse_db_str(self.dburi)

        db = self.get_db_cnx(destroying=True)
        cursor = db.cursor()
        try:
            if scheme == 'postgres' and db.schema:
                cursor.execute('DROP SCHEMA "%s" CASCADE' % db.schema)
            elif scheme == 'mysql':
                dbname = os.path.basename(db_prop['path'])
                cursor = db.cursor()
                cursor.execute('SELECT table_name FROM '
                               '  information_schema.tables '
                               'WHERE table_schema=%s', (dbname,))
                tables = cursor.fetchall()
                for t in tables:
                    cursor.execute('DROP TABLE IF EXISTS `%s`' % t)
            db.commit()
        except Exception:
            db.rollback()

    # overriden

    def is_component_enabled(self, cls):
        if self._component_name(cls).startswith('__main__.'):
            return True
        return Environment.is_component_enabled(self, cls)

    def get_known_users(self, cnx=None):
        return self.known_users
Ejemplo n.º 26
0
    def sync(self, project):
        """Sync the trac environment with cydra
        
        This sets the options returned by ``get_default_options`` and adds Trac's own defaults if necessary"""
        if not self.has_env(project):
            logger.warning('Project %s has no Trac Environment to sync', project.name)
            return

        tracini = os.path.join(self.get_env_path(project), 'conf', 'trac.ini')
        options = self.get_default_options(project)

        # if inherit is enabled, the default values are supposed to be in
        # the inherited file. Thus, we can truncate the config file to get a bare minimum
        if 'inherit_config' in self.component_config:
            options.append(('inherit', 'file', self.component_config['inherit_config']))
            with open(tracini, 'w') as f:
                f.truncate()

        # re-create the configuration file
        config = Configuration(tracini)
        for section, name, value in options:
            config.set(section, name, value)
        config.save()

        # load defaults
        if not any((section, option) == ('inherit', 'file') for section, option, value in options):
            config.set_defaults()
            config.save()

        # check if repositories in cydra match repositories in trac
        env = Environment(self.get_env_path(project))
        rm = RepositoryManager(env)
        trac_repos = rm.get_real_repositories()
        trac_repo_names = [r.reponame for r in trac_repos]

        for repotype, repos in project.data.get('plugins', {}).get('trac', {}).items():
            for repo, tracname in (repos or {}).items():
                if tracname not in trac_repo_names:
                    logger.warning("Removing trac mapping from cydra for %s repo %s", repo, tracname)
                    del repos[repo]
                    if not repos:
                        del project.data.get('plugins', {}).get('trac', {})[repotype]

        # Now do the reverse
        revmap = dict([(y, x) for (x, y) in self.typemap.items()])

        for repo in trac_repos:
            logger.debug('Looking at trac repo %s', repo.reponame)

            try:
                baseparts = repo.get_base().split(':') # This is extremely naiive and possibly breaks some time
                repotype, path = baseparts[0], baseparts[-1]
            except:
                logger.error("Unable to parse: " + repo.get_base())

            reponame = os.path.basename(path)
            if repotype == 'git':
                reponame = reponame[:-4]

            try:
                repository = project.get_repository(revmap[repotype], reponame)
            except:
                logger.error("Unable to locate %s %s (%s)", repotype, reponame, path)
                repository = None

            logger.debug('Cydra repo %r', repository)

            if repository:
                # set this mapping if not there already
                project.data.setdefault('plugins', {}).setdefault('trac', {}).setdefault(repository.type, {})[repository.name] = repo.reponame
                logger.info('Setting trac mapping for %s %s -> %s', repository.type, repository.name, repo.reponame)
            else:
                logger.error("Unable to load %s %s (%s)", revmap[repotype], reponame, path)

        project.save()
Ejemplo n.º 27
0
class MockEnvironment(Environment):
    """
    An mock Environment object which does not need real environment.
    Useful for when needing real environment like access to loaded plugins etc
    but no environment is at hand. Looks like normal project by default but can be
    made to look like home project environment or hybrid of both.

    .. WARNING:: Be careful when using this!
    """
    def __init__(self, config_file=None, enabled_plugins=None, path=None):
        """
        :param str config_file: path to the main configuration file.
            Defaults to ``/etc/trac/project.ini`` which is normal project configuration.
        :param list enabled_plugins:
            An explicit list of plugins to load, instead of reading enabled [components]
            from ``config_file``. If empty list is given no plugins are loaded.
            However plugins can be still listed via ``extra_plugins`` arg.
        :param str path: path to the imaginary trac location.
        """
        if config_file is None:
            # TODO: switch to some constant when can be done without conf
            from multiproject.core.configuration import conf
            config_file = conf.config_file

        # From Environment.setup_config:
        self.config = Configuration(config_file)

        if path is None:
            path = os.path.join(
                self.config.get('multiproject', 'sys_projects_root'),
                '__mock_environment__')
        if enabled_plugins is not None:
            # explicit list given, disable all from configuration
            for key, val in self.config.options('components'):
                self.config.remove('components', key)
            # and replace with the given list
            for plugin in enabled_plugins:
                self.config.set('components', plugin, u'enabled')

        # We override the Environment.__init__ here.

        # Environment.__init__ is as follows:

        # ComponentManager.__init__(self)
        #
        # self.path = path
        # self.systeminfo = []
        # self._href = self._abs_href = None
        #
        # if create:
        #     self.create(options)
        # else:
        #     self.verify()
        #     self.setup_config()
        #
        # if create:
        #     for setup_participant in self.setup_participants:
        #         setup_participant.environment_created()

        # The Environment.setup_config is as follows:

        # self.config = Configuration(os.path.join(self.path, 'conf',
        #         'trac.ini'))
        #         self.setup_log()
        #         from trac.loader import load_components
        #         plugins_dir = self.shared_plugins_dir
        #         load_components(self, plugins_dir and (plugins_dir,))

        # We use suitable lines from these as follows:

        # From Environment.__init__:
        ComponentManager.__init__(self)

        self.path = path
        self.systeminfo = []
        self._href = self._abs_href = None

        # Our own plugin hack ends here, and setup_config lines continue here
        self.setup_log()
        from trac.loader import load_components
        load_components(self)
Ejemplo n.º 28
0
class EnvironmentStub(Environment):
    """A stub of the trac.env.Environment object for testing."""

    href = abs_href = None
    global_databasemanager = None
    required = False

    def __init__(self,
                 default_data=False,
                 enable=None,
                 disable=None,
                 path=None,
                 destroying=False):
        """Construct a new Environment stub object.

        :param default_data: If True, populate the database with some
                             defaults.
        :param enable: A list of component classes or name globs to
                       activate in the stub environment.
        :param disable: A list of component classes or name globs to
                        deactivate in the stub environment.
        :param path: The location of the environment in the file system.
                     No files or directories are created when specifying
                     this parameter.
        :param destroying: If True, the database will not be reset. This is
                           useful for cases when the object is being
                           constructed in order to call `destroy_db`.
        """
        if enable is not None and not isinstance(enable, (list, tuple)):
            raise TypeError('Keyword argument "enable" must be a list')
        if disable is not None and not isinstance(disable, (list, tuple)):
            raise TypeError('Keyword argument "disable" must be a list')

        ComponentManager.__init__(self)
        Component.__init__(self)

        self.systeminfo = []

        import trac
        self.path = path
        if self.path is None:
            self.path = os.path.dirname(trac.__file__)
            if not os.path.isabs(self.path):
                self.path = os.path.join(os.getcwd(), self.path)

        # -- configuration
        self.config = Configuration(None)
        # We have to have a ticket-workflow config for ''lots'' of things to
        # work.  So insert the basic-workflow config here.  There may be a
        # better solution than this.
        load_workflow_config_snippet(self.config, 'basic-workflow.ini')
        self.config.set('logging', 'log_level', 'DEBUG')
        self.config.set('logging', 'log_type', 'stderr')
        if enable is not None:
            self.config.set('components', 'trac.*', 'disabled')
        else:
            self.config.set('components', 'tracopt.versioncontrol.*',
                            'enabled')
        for name_or_class in enable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'enabled')
        for name_or_class in disable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'disabled')

        # -- logging
        from trac.log import logger_handler_factory
        self.log, self._log_handler = logger_handler_factory('test')

        # -- database
        self.config.set('components', 'trac.db.*', 'enabled')
        self.dburi = get_dburi()

        init_global = False
        if self.global_databasemanager:
            self.components[DatabaseManager] = self.global_databasemanager
        else:
            self.config.set('trac', 'database', self.dburi)
            self.global_databasemanager = DatabaseManager(self)
            self.config.set('trac', 'debug_sql', True)
            self.config.set('logging', 'log_type', 'stderr')
            self.config.set('logging', 'log_level', 'DEBUG')
            init_global = not destroying

        if default_data or init_global:
            self.reset_db(default_data)

        from trac.web.href import Href
        self.href = Href('/trac.cgi')
        self.abs_href = Href('http://example.org/trac.cgi')

        self.known_users = []
        translation.activate(locale_en)

    def reset_db(self, default_data=None):
        """Remove all data from Trac tables, keeping the tables themselves.
        :param default_data: after clean-up, initialize with default data
        :return: True upon success
        """
        from trac import db_default
        scheme, db_prop = _parse_db_str(self.dburi)
        tables = []
        remove_sqlite_db = False
        try:
            with self.db_transaction as db:
                db.rollback()  # make sure there's no transaction in progress
                # check the database version
                database_version = self.get_version()
        except Exception:
            # "Database not found ...",
            # "OperationalError: no such table: system" or the like
            pass
        else:
            if database_version == db_default.db_version:
                # same version, simply clear the tables (faster)
                m = sys.modules[__name__]
                reset_fn = 'reset_%s_db' % scheme
                if hasattr(m, reset_fn):
                    tables = getattr(m, reset_fn)(self, db_prop)
            else:
                # different version or version unknown, drop the tables
                remove_sqlite_db = True
                self.destroy_db(scheme, db_prop)

        db = None  # as we might shutdown the pool    FIXME no longer needed!

        if scheme == 'sqlite' and remove_sqlite_db:
            path = db_prop['path']
            if path != ':memory:':
                if not os.path.isabs(path):
                    path = os.path.join(self.path, path)
                self.global_databasemanager.shutdown()
                os.remove(path)

        if not tables:
            self.global_databasemanager.init_db()
            # we need to make sure the next get_db_cnx() will re-create
            # a new connection aware of the new data model - see #8518.
            if self.dburi != 'sqlite::memory:':
                self.global_databasemanager.shutdown()

        with self.db_transaction as db:
            if scheme == 'sqlite':
                # Speed-up tests with SQLite database
                db("PRAGMA synchronous = OFF")
            if default_data:
                for table, cols, vals in db_default.get_data(db):
                    db.executemany(
                        "INSERT INTO %s (%s) VALUES (%s)" %
                        (table, ','.join(cols), ','.join(['%s'] * len(cols))),
                        vals)
            else:
                db("INSERT INTO system (name, value) VALUES (%s, %s)",
                   ('database_version', str(db_default.db_version)))

    def destroy_db(self, scheme=None, db_prop=None):
        if not (scheme and db_prop):
            scheme, db_prop = _parse_db_str(self.dburi)
        try:
            with self.db_transaction as db:
                if scheme == 'postgres' and db.schema:
                    db('DROP SCHEMA %s CASCADE' % db.quote(db.schema))
                elif scheme == 'mysql':
                    for table in db.get_table_names():
                        db("DROP TABLE IF EXISTS `%s`" % table)
        except Exception:
            # "TracError: Database not found...",
            # psycopg2.ProgrammingError: schema "tractest" does not exist
            pass
        return False

    # overridden

    def is_component_enabled(self, cls):
        if self._component_name(cls).startswith('__main__.'):
            return True
        return Environment.is_component_enabled(self, cls)

    def get_known_users(self, cnx=None):
        return self.known_users
Ejemplo n.º 29
0
class EnvironmentStub(Environment):
    """A stub of the trac.env.Environment class for testing."""

    global_databasemanager = None
    required = False

    def __init__(self,
                 default_data=False,
                 enable=None,
                 disable=None,
                 path=None,
                 destroying=False):
        """Construct a new Environment stub object.

        :param default_data: If True, populate the database with some
                             defaults.
        :param enable: A list of component classes or name globs to
                       activate in the stub environment.
        :param disable: A list of component classes or name globs to
                        deactivate in the stub environment.
        :param path: The location of the environment in the file system.
                     No files or directories are created when specifying
                     this parameter.
        :param destroying: If True, the database will not be reset. This is
                           useful for cases when the object is being
                           constructed in order to call `destroy_db`.
        """
        if enable is not None and not isinstance(enable, (list, tuple)):
            raise TypeError('Keyword argument "enable" must be a list')
        if disable is not None and not isinstance(disable, (list, tuple)):
            raise TypeError('Keyword argument "disable" must be a list')

        ComponentManager.__init__(self)

        self.systeminfo = []

        import trac
        self.path = path
        if self.path is None:
            self.path = os.path.dirname(trac.__file__)
            if not os.path.isabs(self.path):
                self.path = os.path.join(os.getcwd(), self.path)

        # -- configuration
        self.config = Configuration(None)
        # We have to have a ticket-workflow config for ''lots'' of things to
        # work.  So insert the basic-workflow config here.  There may be a
        # better solution than this.
        load_workflow_config_snippet(self.config, 'basic-workflow.ini')
        self.config.set('logging', 'log_level', 'DEBUG')
        self.config.set('logging', 'log_type', 'stderr')
        if enable is not None:
            self.config.set('components', 'trac.*', 'disabled')
        else:
            self.config.set('components', 'tracopt.versioncontrol.*',
                            'enabled')
        for name_or_class in enable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'enabled')
        for name_or_class in disable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'disabled')

        # -- logging
        from trac.log import logger_handler_factory
        self.log, self._log_handler = logger_handler_factory('test')

        # -- database
        self.config.set('components', 'trac.db.*', 'enabled')
        self.dburi = get_dburi()

        init_global = False
        if self.global_databasemanager:
            self.components[DatabaseManager] = self.global_databasemanager
        else:
            self.config.set('trac', 'database', self.dburi)
            self.global_databasemanager = DatabaseManager(self)
            self.config.set('trac', 'debug_sql', True)
            init_global = not destroying

        if default_data or init_global:
            self.reset_db(default_data)

        self.config.set('trac', 'base_url', 'http://example.org/trac.cgi')

        translation.activate(locale_en)

    def reset_db(self, default_data=None):
        """Remove all data from Trac tables, keeping the tables themselves.

        :param default_data: after clean-up, initialize with default data
        :return: True upon success
        """
        from trac import db_default
        tables = []
        dbm = self.global_databasemanager
        try:
            with self.db_transaction as db:
                db.rollback()  # make sure there's no transaction in progress
                # check the database version
                db_version = dbm.get_database_version()
        except (TracError, self.env.db_exc.DatabaseError):
            pass
        else:
            if db_version == db_default.db_version:
                # same version, simply clear the tables (faster)
                tables = dbm.reset_tables()
            else:
                # different version or version unknown, drop the tables
                self.destroy_db()

        if not tables:
            dbm.init_db()
            # we need to make sure the next get_db_cnx() will re-create
            # a new connection aware of the new data model - see #8518.
            if self.dburi != 'sqlite::memory:':
                dbm.shutdown()

        if default_data:
            dbm.insert_into_tables(db_default.get_data)
        else:
            dbm.set_database_version(db_default.db_version)

    def destroy_db(self, scheme=None, db_prop=None):
        """Destroy the database.

        :since 1.1.5: the `scheme` and `db_prop` parameters are deprecated and
                      will be removed in 1.3.1.
        """
        try:
            self.global_databasemanager.destroy_db()
        except (TracError, self.db_exc.DatabaseError):
            pass
        return False

    def insert_known_users(self, users):
        with self.env.db_transaction as db:
            for username, name, email in users:
                db("INSERT INTO session VALUES (%s, %s, %s)",
                   (username, 1, int(time.time())))
                db("INSERT INTO session_attribute VALUES (%s,%s,'name',%s)",
                   (username, 1, name))
                db("INSERT INTO session_attribute VALUES (%s,%s,'email',%s)",
                   (username, 1, email))

    # overridden

    def is_component_enabled(self, cls):
        if self._component_name(cls).startswith('__main__.'):
            return True
        return Environment.is_component_enabled(self, cls)
Ejemplo n.º 30
0
class EnvironmentStub(Environment):
    """A stub of the trac.env.Environment class for testing."""

    global_databasemanager = None  # Deprecated, will be removed in 1.3.1
    required = False
    abstract = True

    def __init__(self, default_data=False, enable=None, disable=None,
                 path=None, destroying=False):
        """Construct a new Environment stub object.

        :param default_data: If True, populate the database with some
                             defaults.
        :param enable: A list of component classes or name globs to
                       activate in the stub environment.
        :param disable: A list of component classes or name globs to
                        deactivate in the stub environment.
        :param path: The location of the environment in the file system.
                     No files or directories are created when specifying
                     this parameter.
        :param destroying: If True, the database will not be reset. This is
                           useful for cases when the object is being
                           constructed in order to call `destroy_db`.
        """
        if enable is not None and not isinstance(enable, (list, tuple)):
            raise TypeError('Keyword argument "enable" must be a list')
        if disable is not None and not isinstance(disable, (list, tuple)):
            raise TypeError('Keyword argument "disable" must be a list')

        ComponentManager.__init__(self)

        self.systeminfo = []
        self._old_registry = None
        self._old_components = None

        import trac
        self.path = path
        if self.path is None:
            self.path = os.path.dirname(trac.__file__)
            if not os.path.isabs(self.path):
                self.path = os.path.join(os.getcwd(), self.path)

        # -- configuration
        self.config = Configuration(None)
        # We have to have a ticket-workflow config for ''lots'' of things to
        # work.  So insert the basic-workflow config here.  There may be a
        # better solution than this.
        load_workflow_config_snippet(self.config, 'basic-workflow.ini')
        self.config.set('logging', 'log_level', 'DEBUG')
        self.config.set('logging', 'log_type', 'stderr')
        if enable is not None:
            self.config.set('components', 'trac.*', 'disabled')
        else:
            self.config.set('components', 'tracopt.versioncontrol.*',
                            'enabled')
        for name_or_class in enable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'enabled')
        for name_or_class in disable or ():
            config_key = self._component_name(name_or_class)
            self.config.set('components', config_key, 'disabled')
        self.config.set('trac', 'permission_policies',
                        'DefaultPermissionPolicy, LegacyAttachmentPolicy')

        # -- logging
        from trac.log import logger_handler_factory
        self.log, self._log_handler = logger_handler_factory('test')
        self.log.addHandler(self._log_handler)

        # -- database
        self.dburi = get_dburi()
        self.config.set('components', 'trac.db.*', 'enabled')
        self.config.set('trac', 'database', self.dburi)
        self.config.set('trac', 'debug_sql', True)
        self.global_databasemanager = DatabaseManager(self)  # Remove in 1.3.1

        if not destroying:
            self.reset_db(default_data)

        self.config.set('trac', 'base_url', 'http://example.org/trac.cgi')

        translation.activate(locale_en)

    def reset_db(self, default_data=None):
        """Remove all data from Trac tables, keeping the tables themselves.

        :param default_data: after clean-up, initialize with default data
        :return: True upon success
        """
        from trac import db_default
        tables = []
        dbm = DatabaseManager(self)
        try:
            with self.db_transaction as db:
                db.rollback()  # make sure there's no transaction in progress
                # check the database version
                db_version = dbm.get_database_version()
        except (TracError, self.db_exc.DatabaseError):
            pass
        else:
            if db_version == db_default.db_version:
                # same version, simply clear the tables (faster)
                tables = dbm.reset_tables()
            else:
                # different version or version unknown, drop the tables
                self.destroy_db()

        if not tables:
            dbm.init_db()
            # we need to make sure the next get_db_cnx() will re-create
            # a new connection aware of the new data model - see #8518.
            if self.dburi != 'sqlite::memory:':
                dbm.shutdown()

        if default_data:
            dbm.insert_into_tables(db_default.get_data)
        else:
            dbm.set_database_version(db_default.db_version)

    def destroy_db(self, scheme=None, db_prop=None):
        """Destroy the database.

        :since 1.1.5: the `scheme` and `db_prop` parameters are deprecated and
                      will be removed in 1.3.1.
        """
        try:
            DatabaseManager(self).destroy_db()
        except (TracError, self.db_exc.DatabaseError):
            pass

    def clear_component_registry(self):
        """Clear the component registry.

        The registry entries are saved entries so they can be restored
        later using the `restore_component_registry` method.

        :since: 1.0.11
        """
        self._old_registry = ComponentMeta._registry
        self._old_components = ComponentMeta._components
        ComponentMeta._registry = {}

    def restore_component_registry(self):
        """Restore the component registry.

        The component registry must have been cleared and saved using
        the `clear_component_registry` method.

        :since: 1.0.11
        """
        if self._old_registry is None:
            raise TracError("The clear_component_registry method must be "
                            "called first.")
        ComponentMeta._registry = self._old_registry
        ComponentMeta._components = self._old_components

    # tearDown helper

    def reset_db_and_disk(self):
        """Performs a complete environment reset in a robust way.

        The database is reset, then the connections are shut down, and
        finally all environment files are removed from the disk.
        """
        self.reset_db()
        self.shutdown() # really closes the db connections
        rmtree(self.env.path)
        if self._old_registry is not None:
            self.restore_component_registry()

    # other utilities

    def insert_users(self, users):
        """Insert a tuple representing a user session to the
        `session` and `session_attributes` tables.

        The tuple can be length 3 with entries username, name and
        email, in which case an authenticated user is assumed. The
        tuple can also be length 4, with the last entry specifying
        `1` for an authenticated user or `0` for an unauthenticated
        user.
        """
        with self.db_transaction as db:
            for row in users:
                if len(row) == 3:
                    username, name, email = row
                    authenticated = 1
                else:  # len(row) == 4
                    username, name, email, authenticated = row
                db("INSERT INTO session VALUES (%s, %s, %s)",
                   (username, authenticated, int(time_now())))
                db("INSERT INTO session_attribute VALUES (%s,%s,'name',%s)",
                   (username, authenticated, name))
                db("INSERT INTO session_attribute VALUES (%s,%s,'email',%s)",
                   (username, authenticated, email))

    # overridden

    def is_component_enabled(self, cls):
        if self._component_name(cls).startswith('__main__.'):
            return True
        return Environment.is_component_enabled(self, cls)