class LoaderTest(unittest.TestCase):
  def setUp(self):
    self.build_configuration = BuildConfiguration()
    self.working_set = WorkingSet()
    for entry in working_set.entries:
      self.working_set.add_entry(entry)

  def tearDown(self):
    Goal.clear()

  @contextmanager
  def create_register(self, build_file_aliases=None, register_goals=None, global_subsystems=None, module_name='register'):

    package_name = b'__test_package_{0}'.format(uuid.uuid4().hex)
    self.assertFalse(package_name in sys.modules)

    package_module = types.ModuleType(package_name)
    sys.modules[package_name] = package_module
    try:
      register_module_fqn = b'{0}.{1}'.format(package_name, module_name)
      register_module = types.ModuleType(register_module_fqn)
      setattr(package_module, module_name, register_module)
      sys.modules[register_module_fqn] = register_module

      def register_entrypoint(function_name, function):
        if function:
          setattr(register_module, function_name, function)

      register_entrypoint('build_file_aliases', build_file_aliases)
      register_entrypoint('global_subsystems', global_subsystems)
      register_entrypoint('register_goals', register_goals)

      yield package_name
    finally:
      del sys.modules[package_name]

  def assert_empty_aliases(self):
    registered_aliases = self.build_configuration.registered_aliases()
    self.assertEqual(0, len(registered_aliases.targets))
    self.assertEqual(0, len(registered_aliases.objects))
    self.assertEqual(0, len(registered_aliases.context_aware_object_factories))
    self.assertEqual(self.build_configuration.subsystem_types(), set())

  def test_load_valid_empty(self):
    with self.create_register() as backend_package:
      load_backend(self.build_configuration, backend_package)
      self.assert_empty_aliases()

  def test_load_valid_partial_aliases(self):
    aliases = BuildFileAliases.create(targets={'bob': DummyTarget},
                                      objects={'obj1': DummyObject1,
                                               'obj2': DummyObject2})
    with self.create_register(build_file_aliases=lambda: aliases) as backend_package:
      load_backend(self.build_configuration, backend_package)
      registered_aliases = self.build_configuration.registered_aliases()
      self.assertEqual(DummyTarget, registered_aliases.targets['bob'])
      self.assertEqual(DummyObject1, registered_aliases.objects['obj1'])
      self.assertEqual(DummyObject2, registered_aliases.objects['obj2'])
      self.assertEqual(self.build_configuration.subsystem_types(),
                       set([DummySubsystem1, DummySubsystem2]))

  def test_load_valid_partial_goals(self):
    def register_goals():
      Goal.by_name('jack').install(TaskRegistrar('jill', DummyTask))

    with self.create_register(register_goals=register_goals) as backend_package:
      Goal.clear()
      self.assertEqual(0, len(Goal.all()))

      load_backend(self.build_configuration, backend_package)
      self.assert_empty_aliases()
      self.assertEqual(1, len(Goal.all()))

      task_names = Goal.by_name('jack').ordered_task_names()
      self.assertEqual(1, len(task_names))

      task_name = task_names[0]
      self.assertEqual('jill', task_name)

  def test_load_invalid_entrypoint(self):
    def build_file_aliases(bad_arg):
      return BuildFileAliases.create()

    with self.create_register(build_file_aliases=build_file_aliases) as backend_package:
      with self.assertRaises(BuildConfigurationError):
        load_backend(self.build_configuration, backend_package)

  def test_load_invalid_module(self):
    with self.create_register(module_name='register2') as backend_package:
      with self.assertRaises(BuildConfigurationError):
        load_backend(self.build_configuration, backend_package)

  def test_load_missing_plugin(self):
    with self.assertRaises(PluginNotFound):
      self.load_plugins(['Foobar'])


  def get_mock_plugin(self, name, version, reg=None, alias=None, after=None):
    """Make a fake Distribution (optionally with entry points)

    Note the entry points do not actually point to code in the returned distribution --
    the distribution does not even have a location and does not contain any code, just metadata.

    A module is synthesized on the fly and installed into sys.modules under a random name.
    If optional entry point callables are provided, those are added as methods to the module and
    their name (foo/bar/baz in fake module) is added as the requested entry point to the mocked
    metadata added to the returned dist.

    :param str name: project_name for distribution (see pkg_resources)
    :param str version: version for distribution (see pkg_resources)
    :param callable reg: Optional callable for goal registration entry point
    :param callable alias: Optional callable for build_file_aliases entry point
    :param callable after: Optional callable for load_after list entry point
    """

    plugin_pkg = b'demoplugin{0}'.format(uuid.uuid4().hex)
    pkg = types.ModuleType(plugin_pkg)
    sys.modules[plugin_pkg] = pkg
    module_name = b'{0}.{1}'.format(plugin_pkg, 'demo')
    plugin = types.ModuleType(module_name)
    setattr(pkg, 'demo', plugin)
    sys.modules[module_name] = plugin

    metadata = {}
    entry_lines = []

    if reg is not None:
      setattr(plugin, 'foo', reg)
      entry_lines.append('register_goals = {}:foo\n'.format(module_name))

    if alias is not None:
      setattr(plugin, 'bar', alias)
      entry_lines.append('build_file_aliases = {}:bar\n'.format(module_name))

    if after is not None:
      setattr(plugin, 'baz', after)
      entry_lines.append('load_after = {}:baz\n'.format(module_name))

    if entry_lines:
      entry_data = '[pantsbuild.plugin]\n{}\n'.format('\n'.join(entry_lines))
      metadata = {'entry_points.txt': entry_data}

    return Distribution(project_name=name, version=version, metadata=MockMetadata(metadata))

  def load_plugins(self, plugins):
    load_plugins(self.build_configuration, plugins, load_from=self.working_set)

  def test_plugin_load_and_order(self):
    d1 = self.get_mock_plugin('demo1', '0.0.1', after=lambda: ['demo2'])
    d2 = self.get_mock_plugin('demo2', '0.0.3')
    self.working_set.add(d1)

    # Attempting to load 'demo1' then 'demo2' should fail as 'demo1' requires 'after'=['demo2'].
    with self.assertRaises(PluginLoadOrderError):
      self.load_plugins(['demo1', 'demo2'])

    # Attempting to load 'demo2' first should fail as it is not (yet) installed.
    with self.assertRaises(PluginNotFound):
      self.load_plugins(['demo2', 'demo1'])

    # Installing demo2 and then loading in correct order should work though.
    self.working_set.add(d2)
    self.load_plugins(['demo2>=0.0.2', 'demo1'])

    # But asking for a bad (not installed) version fails.
    with self.assertRaises(VersionConflict):
      self.load_plugins(['demo2>=0.0.5'])

  def test_plugin_installs_goal(self):
    def reg_goal():
      Goal.by_name('plugindemo').install(TaskRegistrar('foo', DummyTask))
    self.working_set.add(self.get_mock_plugin('regdemo', '0.0.1', reg=reg_goal))

    # Start without the custom goal.
    self.assertEqual(0, len(Goal.by_name('plugindemo').ordered_task_names()))

    # Load plugin which registers custom goal.
    self.load_plugins(['regdemo'])

    # Now the custom goal exists.
    self.assertEqual(1, len(Goal.by_name('plugindemo').ordered_task_names()))
    self.assertEqual('foo', Goal.by_name('plugindemo').ordered_task_names()[0])

  def test_plugin_installs_alias(self):
    def reg_alias():
      return BuildFileAliases.create(targets={'pluginalias': DummyTarget},
                                     objects={'FROMPLUGIN1': DummyObject1,
                                              'FROMPLUGIN2': DummyObject2})
    self.working_set.add(self.get_mock_plugin('aliasdemo', '0.0.1', alias=reg_alias))

    # Start with no aliases.
    self.assert_empty_aliases()

    # Now load the plugin which defines aliases.
    self.load_plugins(['aliasdemo'])

    # Aliases now exist.
    registered_aliases = self.build_configuration.registered_aliases()
    self.assertEqual(DummyTarget, registered_aliases.targets['pluginalias'])
    self.assertEqual(DummyObject1, registered_aliases.objects['FROMPLUGIN1'])
    self.assertEqual(DummyObject2, registered_aliases.objects['FROMPLUGIN2'])
    self.assertEqual(self.build_configuration.subsystem_types(),
                     {DummySubsystem1, DummySubsystem2})

  def test_subsystems(self):
    def global_subsystems():
      return {DummySubsystem1, DummySubsystem2}
    with self.create_register(global_subsystems=global_subsystems) as backend_package:
      load_backend(self.build_configuration, backend_package)
      self.assertEqual(self.build_configuration.subsystem_types(),
                       {DummySubsystem1, DummySubsystem2})
Example #2
0
class BuildConfigurationTest(unittest.TestCase):
  def setUp(self):
    self.build_configuration = BuildConfiguration()

  def _register_aliases(self, **kwargs):
    self.build_configuration.register_aliases(BuildFileAliases(**kwargs))

  def test_register_bad(self):
    with self.assertRaises(TypeError):
      self.build_configuration.register_aliases(42)

  def test_register_target_alias(self):
    class Fred(Target):
      pass

    self._register_aliases(targets={'fred': Fred})
    aliases = self.build_configuration.registered_aliases()
    self.assertEqual({}, aliases.target_macro_factories)
    self.assertEqual({}, aliases.objects)
    self.assertEqual({}, aliases.context_aware_object_factories)
    self.assertEqual(dict(fred=Fred), aliases.target_types)

    build_file = FilesystemBuildFile('/tmp', 'fred', must_exist=False)
    parse_state = self.build_configuration.initialize_parse_state(build_file)

    self.assertEqual(0, len(parse_state.registered_addressable_instances))
    self.assertEqual(1, len(parse_state.parse_globals))

    target_call_proxy = parse_state.parse_globals['fred']
    target_call_proxy(name='jake')

    self.assertEqual(1, len(parse_state.registered_addressable_instances))
    name, target_proxy = parse_state.registered_addressable_instances.pop()
    self.assertEqual('jake', target_proxy.addressed_name)
    self.assertEqual(Fred, target_proxy.addressed_type)

  def test_register_target_macro_facory(self):
    class Fred(Target):
      pass

    class FredMacro(TargetMacro):
      def __init__(self, parse_context):
        self._parse_context = parse_context

      def expand(self, *args, **kwargs):
        return self._parse_context.create_object(Fred, name='frog', dependencies=[kwargs['name']])

    class FredFactory(TargetMacro.Factory):
      @property
      def target_types(self):
        return {Fred}

      def macro(self, parse_context):
        return FredMacro(parse_context)

    factory = FredFactory()

    self._register_aliases(targets={'fred': factory})
    aliases = self.build_configuration.registered_aliases()
    self.assertEqual({}, aliases.target_types)
    self.assertEqual({}, aliases.objects)
    self.assertEqual({}, aliases.context_aware_object_factories)
    self.assertEqual(dict(fred=factory), aliases.target_macro_factories)

    build_file = FilesystemBuildFile('/tmp', 'fred', must_exist=False)
    parse_state = self.build_configuration.initialize_parse_state(build_file)

    self.assertEqual(0, len(parse_state.registered_addressable_instances))
    self.assertEqual(1, len(parse_state.parse_globals))

    target_call_proxy = parse_state.parse_globals['fred']
    target_call_proxy(name='jake')

    self.assertEqual(1, len(parse_state.registered_addressable_instances))
    name, target_proxy = parse_state.registered_addressable_instances.pop()
    self.assertEqual('frog', target_proxy.addressed_name)
    self.assertEqual(Fred, target_proxy.addressed_type)
    self.assertEqual(['jake'], target_proxy.dependency_specs)

  def test_register_exposed_object(self):
    self._register_aliases(objects={'jane': 42})

    aliases = self.build_configuration.registered_aliases()
    self.assertEqual({}, aliases.target_types)
    self.assertEqual({}, aliases.target_macro_factories)
    self.assertEqual({}, aliases.context_aware_object_factories)
    self.assertEqual(dict(jane=42), aliases.objects)

    build_file = FilesystemBuildFile('/tmp', 'jane', must_exist=False)
    parse_state = self.build_configuration.initialize_parse_state(build_file)

    self.assertEqual(0, len(parse_state.registered_addressable_instances))
    self.assertEqual(1, len(parse_state.parse_globals))
    self.assertEqual(42, parse_state.parse_globals['jane'])

  def test_register_exposed_context_aware_function(self):
    self.do_test_exposed_context_aware_function(lambda context: lambda: context.rel_path)
    self.do_test_exposed_context_aware_function(lambda context=None: lambda: context.rel_path)

  def george_method(self, parse_context):
    return lambda: parse_context.rel_path

  def test_register_exposed_context_aware_method(self):
    self.do_test_exposed_context_aware_function(self.george_method)

  @classmethod
  def george_classmethod(cls, parse_context):
    return lambda: parse_context.rel_path

  def test_register_exposed_context_aware_classmethod(self):
    self.do_test_exposed_context_aware_function(self.george_classmethod)

  @staticmethod
  def george_staticmethod(parse_context):
    return lambda: parse_context.rel_path

  def test_register_exposed_context_aware_staticmethod(self):
    self.do_test_exposed_context_aware_function(self.george_staticmethod)

  def do_test_exposed_context_aware_function(self, func, *args, **kwargs):
    with self.do_test_exposed_context_aware_object(func) as context_aware_object:
      self.assertEqual('george', context_aware_object(*args, **kwargs))

  def test_register_exposed_context_aware_class(self):
    class George(object):
      def __init__(self, parse_context):
        self._parse_context = parse_context

      def honorific(self):
        return len(self._parse_context.rel_path)

    with self.do_test_exposed_context_aware_object(George) as context_aware_object:
      self.assertEqual(6, context_aware_object.honorific())

  @contextmanager
  def do_test_exposed_context_aware_object(self, context_aware_object_factory):
    self._register_aliases(context_aware_object_factories={'george': context_aware_object_factory})

    aliases = self.build_configuration.registered_aliases()
    self.assertEqual({}, aliases.target_types)
    self.assertEqual({}, aliases.target_macro_factories)
    self.assertEqual({}, aliases.objects)
    self.assertEqual(dict(george=context_aware_object_factory),
                     aliases.context_aware_object_factories)

    with temporary_dir() as root:
      build_file_path = os.path.join(root, 'george', 'BUILD')
      touch(build_file_path)
      build_file = FilesystemBuildFile(root, 'george')
      parse_state = self.build_configuration.initialize_parse_state(build_file)

      self.assertEqual(0, len(parse_state.registered_addressable_instances))
      self.assertEqual(1, len(parse_state.parse_globals))
      yield parse_state.parse_globals['george']
Example #3
0
class BuildConfigurationTest(unittest.TestCase):
  def setUp(self):
    self.build_configuration = BuildConfiguration()

  def test_register_target_alias(self):
    class Fred(Target):
      pass

    self.build_configuration._register_target_alias('fred', Fred)
    aliases = self.build_configuration.registered_aliases()
    self.assertEqual({}, aliases.objects)
    self.assertEqual({}, aliases.context_aware_object_factories)
    self.assertEqual(dict(fred=Fred), aliases.targets)

    build_file = FilesystemBuildFile('/tmp', 'fred', must_exist=False)
    parse_state = self.build_configuration.initialize_parse_state(build_file)

    self.assertEqual(0, len(parse_state.registered_addressable_instances))
    self.assertEqual(1, len(parse_state.parse_globals))

    target_call_proxy = parse_state.parse_globals['fred']
    target_call_proxy(name='jake')
    self.assertEqual(1, len(parse_state.registered_addressable_instances))
    name, target_proxy = parse_state.registered_addressable_instances.pop()
    self.assertEqual('jake', target_proxy.addressed_name)
    self.assertEqual(Fred, target_proxy.addressed_type)

  def test_register_bad_target_alias(self):
    with self.assertRaises(TypeError):
      self.build_configuration._register_target_alias('fred', object())

    target = Target('fred', Address.parse('a:b'), BuildGraph(address_mapper=None))
    with self.assertRaises(TypeError):
      self.build_configuration._register_target_alias('fred', target)

  def test_register_exposed_object(self):
    self.build_configuration._register_exposed_object('jane', 42)

    aliases = self.build_configuration.registered_aliases()
    self.assertEqual({}, aliases.targets)
    self.assertEqual({}, aliases.context_aware_object_factories)
    self.assertEqual(dict(jane=42), aliases.objects)

    build_file = FilesystemBuildFile('/tmp', 'jane', must_exist=False)
    parse_state = self.build_configuration.initialize_parse_state(build_file)

    self.assertEqual(0, len(parse_state.registered_addressable_instances))
    self.assertEqual(1, len(parse_state.parse_globals))
    self.assertEqual(42, parse_state.parse_globals['jane'])

  def test_register_bad_exposed_object(self):
    with self.assertRaises(TypeError):
      self.build_configuration._register_exposed_object('jane', Target)

  def test_register_exposed_context_aware_function(self):
    self.do_test_exposed_context_aware_function(lambda context: lambda: context.rel_path)
    self.do_test_exposed_context_aware_function(lambda context=None: lambda: context.rel_path)

  def george_method(self, parse_context):
    return lambda: parse_context.rel_path

  def test_register_exposed_context_aware_method(self):
    self.do_test_exposed_context_aware_function(self.george_method)

  @classmethod
  def george_classmethod(cls, parse_context):
    return lambda: parse_context.rel_path

  def test_register_exposed_context_aware_classmethod(self):
    self.do_test_exposed_context_aware_function(self.george_classmethod)

  @staticmethod
  def george_staticmethod(parse_context):
    return lambda: parse_context.rel_path

  def test_register_exposed_context_aware_staticmethod(self):
    self.do_test_exposed_context_aware_function(self.george_staticmethod)

  def do_test_exposed_context_aware_function(self, func, *args, **kwargs):
    with self.do_test_exposed_context_aware_object(func) as context_aware_object:
      self.assertEqual('george', context_aware_object(*args, **kwargs))

  def test_register_exposed_context_aware_class(self):
    class George(object):
      def __init__(self, parse_context):
        self._parse_context = parse_context

      def honorific(self):
        return len(self._parse_context.rel_path)

    with self.do_test_exposed_context_aware_object(George) as context_aware_object:
      self.assertEqual(6, context_aware_object.honorific())

  @contextmanager
  def do_test_exposed_context_aware_object(self, context_aware_object_factory):
    self.build_configuration._register_exposed_context_aware_object_factory(
        'george', context_aware_object_factory)

    aliases = self.build_configuration.registered_aliases()
    self.assertEqual({}, aliases.targets)
    self.assertEqual({}, aliases.objects)
    self.assertEqual(dict(george=context_aware_object_factory),
                     aliases.context_aware_object_factories)

    with temporary_dir() as root:
      build_file_path = os.path.join(root, 'george', 'BUILD')
      touch(build_file_path)
      build_file = FilesystemBuildFile(root, 'george')
      parse_state = self.build_configuration.initialize_parse_state(build_file)

      self.assertEqual(0, len(parse_state.registered_addressable_instances))
      self.assertEqual(1, len(parse_state.parse_globals))
      yield parse_state.parse_globals['george']

  def test_register_bad_exposed_context_aware_object(self):
    with self.assertRaises(TypeError):
      self.build_configuration._register_exposed_context_aware_object_factory('george', 1)
class LoaderTest(unittest.TestCase):
    def setUp(self):
        self.build_configuration = BuildConfiguration()
        self.working_set = WorkingSet()
        for entry in working_set.entries:
            self.working_set.add_entry(entry)

    def tearDown(self):
        Goal.clear()

    @contextmanager
    def create_register(self,
                        build_file_aliases=None,
                        register_goals=None,
                        global_subsystems=None,
                        module_name='register'):

        package_name = b'__test_package_{0}'.format(uuid.uuid4().hex)
        self.assertFalse(package_name in sys.modules)

        package_module = types.ModuleType(package_name)
        sys.modules[package_name] = package_module
        try:
            register_module_fqn = b'{0}.{1}'.format(package_name, module_name)
            register_module = types.ModuleType(register_module_fqn)
            setattr(package_module, module_name, register_module)
            sys.modules[register_module_fqn] = register_module

            def register_entrypoint(function_name, function):
                if function:
                    setattr(register_module, function_name, function)

            register_entrypoint('build_file_aliases', build_file_aliases)
            register_entrypoint('global_subsystems', global_subsystems)
            register_entrypoint('register_goals', register_goals)

            yield package_name
        finally:
            del sys.modules[package_name]

    def assert_empty_aliases(self):
        registered_aliases = self.build_configuration.registered_aliases()
        self.assertEqual(0, len(registered_aliases.targets))
        self.assertEqual(0, len(registered_aliases.objects))
        self.assertEqual(
            0, len(registered_aliases.context_aware_object_factories))
        self.assertEqual(self.build_configuration.subsystems(), set())

    def test_load_valid_empty(self):
        with self.create_register() as backend_package:
            load_backend(self.build_configuration, backend_package)
            self.assert_empty_aliases()

    def test_load_valid_partial_aliases(self):
        aliases = BuildFileAliases.create(targets={'bob': DummyTarget},
                                          objects={
                                              'obj1': DummyObject1,
                                              'obj2': DummyObject2
                                          })
        with self.create_register(
                build_file_aliases=lambda: aliases) as backend_package:
            load_backend(self.build_configuration, backend_package)
            registered_aliases = self.build_configuration.registered_aliases()
            self.assertEqual(DummyTarget, registered_aliases.targets['bob'])
            self.assertEqual(DummyObject1, registered_aliases.objects['obj1'])
            self.assertEqual(DummyObject2, registered_aliases.objects['obj2'])
            self.assertEqual(self.build_configuration.subsystems(),
                             set([DummySubsystem1, DummySubsystem2]))

    def test_load_valid_partial_goals(self):
        def register_goals():
            Goal.by_name('jack').install(TaskRegistrar('jill', DummyTask))

        with self.create_register(
                register_goals=register_goals) as backend_package:
            Goal.clear()
            self.assertEqual(0, len(Goal.all()))

            load_backend(self.build_configuration, backend_package)
            self.assert_empty_aliases()
            self.assertEqual(1, len(Goal.all()))

            task_names = Goal.by_name('jack').ordered_task_names()
            self.assertEqual(1, len(task_names))

            task_name = task_names[0]
            self.assertEqual('jill', task_name)

    def test_load_invalid_entrypoint(self):
        def build_file_aliases(bad_arg):
            return BuildFileAliases.create()

        with self.create_register(
                build_file_aliases=build_file_aliases) as backend_package:
            with self.assertRaises(BuildConfigurationError):
                load_backend(self.build_configuration, backend_package)

    def test_load_invalid_module(self):
        with self.create_register(module_name='register2') as backend_package:
            with self.assertRaises(BuildConfigurationError):
                load_backend(self.build_configuration, backend_package)

    def test_load_missing_plugin(self):
        with self.assertRaises(PluginNotFound):
            self.load_plugins(['Foobar'])

    def get_mock_plugin(self, name, version, reg=None, alias=None, after=None):
        """Make a fake Distribution (optionally with entry points)

    Note the entry points do not actually point to code in the returned distribution --
    the distribution does not even have a location and does not contain any code, just metadata.

    A module is synthesized on the fly and installed into sys.modules under a random name.
    If optional entry point callables are provided, those are added as methods to the module and
    their name (foo/bar/baz in fake module) is added as the requested entry point to the mocked
    metadata added to the returned dist.

    :param str name: project_name for distribution (see pkg_resources)
    :param str version: version for distribution (see pkg_resources)
    :param callable reg: Optional callable for goal registration entry point
    :param callable alias: Optional callable for build_file_aliases entry point
    :param callable after: Optional callable for load_after list entry point
    """

        plugin_pkg = b'demoplugin{0}'.format(uuid.uuid4().hex)
        pkg = types.ModuleType(plugin_pkg)
        sys.modules[plugin_pkg] = pkg
        module_name = b'{0}.{1}'.format(plugin_pkg, 'demo')
        plugin = types.ModuleType(module_name)
        setattr(pkg, 'demo', plugin)
        sys.modules[module_name] = plugin

        metadata = {}
        entry_lines = []

        if reg is not None:
            setattr(plugin, 'foo', reg)
            entry_lines.append('register_goals = {}:foo\n'.format(module_name))

        if alias is not None:
            setattr(plugin, 'bar', alias)
            entry_lines.append(
                'build_file_aliases = {}:bar\n'.format(module_name))

        if after is not None:
            setattr(plugin, 'baz', after)
            entry_lines.append('load_after = {}:baz\n'.format(module_name))

        if entry_lines:
            entry_data = '[pantsbuild.plugin]\n{}\n'.format(
                '\n'.join(entry_lines))
            metadata = {'entry_points.txt': entry_data}

        return Distribution(project_name=name,
                            version=version,
                            metadata=MockMetadata(metadata))

    def load_plugins(self, plugins):
        load_plugins(self.build_configuration,
                     plugins,
                     load_from=self.working_set)

    def test_plugin_load_and_order(self):
        d1 = self.get_mock_plugin('demo1', '0.0.1', after=lambda: ['demo2'])
        d2 = self.get_mock_plugin('demo2', '0.0.3')
        self.working_set.add(d1)

        # Attempting to load 'demo1' then 'demo2' should fail as 'demo1' requires 'after'=['demo2'].
        with self.assertRaises(PluginLoadOrderError):
            self.load_plugins(['demo1', 'demo2'])

        # Attempting to load 'demo2' first should fail as it is not (yet) installed.
        with self.assertRaises(PluginNotFound):
            self.load_plugins(['demo2', 'demo1'])

        # Installing demo2 and then loading in correct order should work though.
        self.working_set.add(d2)
        self.load_plugins(['demo2>=0.0.2', 'demo1'])

        # But asking for a bad (not installed) version fails.
        with self.assertRaises(VersionConflict):
            self.load_plugins(['demo2>=0.0.5'])

    def test_plugin_installs_goal(self):
        def reg_goal():
            Goal.by_name('plugindemo').install(TaskRegistrar('foo', DummyTask))

        self.working_set.add(
            self.get_mock_plugin('regdemo', '0.0.1', reg=reg_goal))

        # Start without the custom goal.
        self.assertEqual(0,
                         len(Goal.by_name('plugindemo').ordered_task_names()))

        # Load plugin which registers custom goal.
        self.load_plugins(['regdemo'])

        # Now the custom goal exists.
        self.assertEqual(1,
                         len(Goal.by_name('plugindemo').ordered_task_names()))
        self.assertEqual('foo',
                         Goal.by_name('plugindemo').ordered_task_names()[0])

    def test_plugin_installs_alias(self):
        def reg_alias():
            return BuildFileAliases.create(
                targets={'pluginalias': DummyTarget},
                objects={
                    'FROMPLUGIN1': DummyObject1,
                    'FROMPLUGIN2': DummyObject2
                })

        self.working_set.add(
            self.get_mock_plugin('aliasdemo', '0.0.1', alias=reg_alias))

        # Start with no aliases.
        self.assert_empty_aliases()

        # Now load the plugin which defines aliases.
        self.load_plugins(['aliasdemo'])

        # Aliases now exist.
        registered_aliases = self.build_configuration.registered_aliases()
        self.assertEqual(DummyTarget,
                         registered_aliases.targets['pluginalias'])
        self.assertEqual(DummyObject1,
                         registered_aliases.objects['FROMPLUGIN1'])
        self.assertEqual(DummyObject2,
                         registered_aliases.objects['FROMPLUGIN2'])
        self.assertEqual(self.build_configuration.subsystems(),
                         {DummySubsystem1, DummySubsystem2})

    def test_subsystems(self):
        def global_subsystems():
            return {DummySubsystem1, DummySubsystem2}

        with self.create_register(
                global_subsystems=global_subsystems) as backend_package:
            load_backend(self.build_configuration, backend_package)
            self.assertEqual(self.build_configuration.subsystems(),
                             {DummySubsystem1, DummySubsystem2})
class LoaderTest(unittest.TestCase):
    def setUp(self):
        self.build_configuration = BuildConfiguration()

    def tearDown(self):
        Goal.clear()

    @contextmanager
    def create_register(
        self, build_file_aliases=None, register_commands=None, register_goals=None, module_name="register"
    ):

        package_name = b"__test_package_{0}".format(uuid.uuid4().hex)
        self.assertFalse(package_name in sys.modules)

        package_module = types.ModuleType(package_name)
        sys.modules[package_name] = package_module
        try:
            register_module_fqn = b"{0}.{1}".format(package_name, module_name)
            register_module = types.ModuleType(register_module_fqn)
            setattr(package_module, module_name, register_module)
            sys.modules[register_module_fqn] = register_module

            def register_entrypoint(function_name, function):
                if function:
                    setattr(register_module, function_name, function)

            register_entrypoint("build_file_aliases", build_file_aliases)
            register_entrypoint("register_commands", register_commands)
            register_entrypoint("register_goals", register_goals)

            yield package_name
        finally:
            del sys.modules[package_name]

    def assert_empty_aliases(self):
        registered_aliases = self.build_configuration.registered_aliases()
        self.assertEqual(0, len(registered_aliases.targets))
        self.assertEqual(0, len(registered_aliases.objects))
        self.assertEqual(0, len(registered_aliases.context_aware_object_factories))

    def test_load_valid_empty(self):
        with self.create_register() as backend_package:
            load_backend(self.build_configuration, backend_package)
            self.assert_empty_aliases()

    def test_load_valid_partial_aliases(self):
        aliases = BuildFileAliases.create(targets={"bob": Target}, objects={"MEANING_OF_LIFE": 42})
        with self.create_register(build_file_aliases=lambda: aliases) as backend_package:
            load_backend(self.build_configuration, backend_package)
            registered_aliases = self.build_configuration.registered_aliases()
            self.assertEqual(Target, registered_aliases.targets["bob"])
            self.assertEqual(42, registered_aliases.objects["MEANING_OF_LIFE"])

    def test_load_valid_partial_goals(self):
        def register_goals():
            Goal.by_name("jack").install(TaskRegistrar("jill", lambda: 42))

        with self.create_register(register_goals=register_goals) as backend_package:
            Goal.clear()
            self.assertEqual(0, len(Goal.all()))

            load_backend(self.build_configuration, backend_package)
            self.assert_empty_aliases()
            self.assertEqual(1, len(Goal.all()))

            task_names = Goal.by_name("jack").ordered_task_names()
            self.assertEqual(1, len(task_names))

            task_name = task_names[0]
            self.assertEqual("jill", task_name)

    def test_load_invalid_entrypoint(self):
        def build_file_aliases(bad_arg):
            return BuildFileAliases.create()

        with self.create_register(build_file_aliases=build_file_aliases) as backend_package:
            with self.assertRaises(BuildConfigurationError):
                load_backend(self.build_configuration, backend_package)

    def test_load_invalid_module(self):
        with self.create_register(module_name="register2") as backend_package:
            with self.assertRaises(BuildConfigurationError):
                load_backend(self.build_configuration, backend_package)
class BuildConfigurationTest(unittest.TestCase):
    def setUp(self):
        self.build_configuration = BuildConfiguration()

    def test_register_target_alias(self):
        class Fred(Target):
            pass

        self.build_configuration.register_target_alias('fred', Fred)
        aliases = self.build_configuration.registered_aliases()
        self.assertEqual({}, aliases.objects)
        self.assertEqual({}, aliases.context_aware_object_factories)
        self.assertEqual(dict(fred=Fred), aliases.targets)

        build_file = FilesystemBuildFile('/tmp', 'fred', must_exist=False)
        parse_state = self.build_configuration.initialize_parse_state(
            build_file)

        self.assertEqual(0, len(parse_state.registered_addressable_instances))
        self.assertEqual(1, len(parse_state.parse_globals))

        target_call_proxy = parse_state.parse_globals['fred']
        target_call_proxy(name='jake')
        self.assertEqual(1, len(parse_state.registered_addressable_instances))
        name, target_proxy = parse_state.registered_addressable_instances.pop()
        self.assertEqual('jake', target_proxy.name)
        self.assertEqual(Fred, target_proxy.target_type)

    def test_register_bad_target_alias(self):
        with self.assertRaises(TypeError):
            self.build_configuration.register_target_alias('fred', object())

        target = Target('fred', SyntheticAddress.parse('a:b'),
                        BuildGraph(address_mapper=None))
        with self.assertRaises(TypeError):
            self.build_configuration.register_target_alias('fred', target)

    def test_register_exposed_object(self):
        self.build_configuration.register_exposed_object('jane', 42)

        aliases = self.build_configuration.registered_aliases()
        self.assertEqual({}, aliases.targets)
        self.assertEqual({}, aliases.context_aware_object_factories)
        self.assertEqual(dict(jane=42), aliases.objects)

        build_file = FilesystemBuildFile('/tmp', 'jane', must_exist=False)
        parse_state = self.build_configuration.initialize_parse_state(
            build_file)

        self.assertEqual(0, len(parse_state.registered_addressable_instances))
        self.assertEqual(1, len(parse_state.parse_globals))
        self.assertEqual(42, parse_state.parse_globals['jane'])

    def test_register_bad_exposed_object(self):
        with self.assertRaises(TypeError):
            self.build_configuration.register_exposed_object('jane', Target)

    def test_register_exposed_context_aware_function(self):
        self.do_test_exposed_context_aware_function(
            lambda context: lambda: context.rel_path)
        self.do_test_exposed_context_aware_function(
            lambda context=None: lambda: context.rel_path)

    def george_method(self, parse_context):
        return lambda: parse_context.rel_path

    def test_register_exposed_context_aware_method(self):
        self.do_test_exposed_context_aware_function(self.george_method)

    @classmethod
    def george_classmethod(cls, parse_context):
        return lambda: parse_context.rel_path

    def test_register_exposed_context_aware_classmethod(self):
        self.do_test_exposed_context_aware_function(self.george_classmethod)

    @staticmethod
    def george_staticmethod(parse_context):
        return lambda: parse_context.rel_path

    def test_register_exposed_context_aware_staticmethod(self):
        self.do_test_exposed_context_aware_function(self.george_staticmethod)

    def do_test_exposed_context_aware_function(self, func, *args, **kwargs):
        with self.do_test_exposed_context_aware_object(
                func) as context_aware_object:
            self.assertEqual('george', context_aware_object(*args, **kwargs))

    def test_register_exposed_context_aware_class(self):
        class George(object):
            def __init__(self, parse_context):
                self._parse_context = parse_context

            def honorific(self):
                return len(self._parse_context.rel_path)

        with self.do_test_exposed_context_aware_object(
                George) as context_aware_object:
            self.assertEqual(6, context_aware_object.honorific())

    @contextmanager
    def do_test_exposed_context_aware_object(self,
                                             context_aware_object_factory):
        self.build_configuration.register_exposed_context_aware_object_factory(
            'george', context_aware_object_factory)

        aliases = self.build_configuration.registered_aliases()
        self.assertEqual({}, aliases.targets)
        self.assertEqual({}, aliases.objects)
        self.assertEqual(dict(george=context_aware_object_factory),
                         aliases.context_aware_object_factories)

        with temporary_dir() as root:
            build_file_path = os.path.join(root, 'george', 'BUILD')
            touch(build_file_path)
            build_file = FilesystemBuildFile(root, 'george')
            parse_state = self.build_configuration.initialize_parse_state(
                build_file)

            self.assertEqual(0,
                             len(parse_state.registered_addressable_instances))
            self.assertEqual(1, len(parse_state.parse_globals))
            yield parse_state.parse_globals['george']

    def test_register_bad_exposed_context_aware_object(self):
        with self.assertRaises(TypeError):
            self.build_configuration.register_exposed_context_aware_object_factory(
                'george', 1)
Example #7
0
class LoaderTest(unittest2.TestCase):
  def setUp(self):
    self.build_configuration = BuildConfiguration()

  def tearDown(self):
    Phase.clear()

  @contextmanager
  def create_register(self, build_file_aliases=None, register_commands=None, register_goals=None,
                      module_name='register'):

    package_name = b'__test_package_{0}'.format(uuid.uuid4().hex)
    self.assertFalse(package_name in sys.modules)

    package_module = types.ModuleType(package_name)
    sys.modules[package_name] = package_module
    try:
      register_module_fqn = b'{0}.{1}'.format(package_name, module_name)
      register_module = types.ModuleType(register_module_fqn)
      setattr(package_module, module_name, register_module)
      sys.modules[register_module_fqn] = register_module

      def register_entrypoint(function_name, function):
        if function:
          setattr(register_module, function_name, function)

      register_entrypoint('build_file_aliases', build_file_aliases)
      register_entrypoint('register_commands', register_commands)
      register_entrypoint('register_goals', register_goals)

      yield package_name
    finally:
      del sys.modules[package_name]

  def assert_empty_aliases(self):
    registered_aliases = self.build_configuration.registered_aliases()
    self.assertEqual(0, len(registered_aliases.targets))
    self.assertEqual(0, len(registered_aliases.objects))
    self.assertEqual(0, len(registered_aliases.context_aware_object_factories))

  def test_load_valid_empty(self):
    with self.create_register() as backend_package:
      load_backend(self.build_configuration, backend_package)
      self.assert_empty_aliases()

  def test_load_valid_partial_aliases(self):
    aliases = BuildFileAliases.create(targets={'bob': Target},
                                      objects={'MEANING_OF_LIFE': 42})
    with self.create_register(build_file_aliases=lambda: aliases) as backend_package:
      load_backend(self.build_configuration, backend_package)
      registered_aliases = self.build_configuration.registered_aliases()
      self.assertEqual(Target, registered_aliases.targets['bob'])
      self.assertEqual(42, registered_aliases.objects['MEANING_OF_LIFE'])

  def test_load_valid_partial_goals(self):
    def register_goals():
      Phase('jack').install(Goal('jill', lambda: 42))

    with self.create_register(register_goals=register_goals) as backend_package:
      Phase.clear()
      self.assertEqual(0, len(Phase.all()))

      load_backend(self.build_configuration, backend_package)
      self.assert_empty_aliases()
      self.assertEqual(1, len(Phase.all()))

      goals = Phase('jack').goals()
      self.assertEqual(1, len(goals))

      goal = goals[0]
      self.assertEqual('jill', goal.name)

  def test_load_invalid_entrypoint(self):
    def build_file_aliases(bad_arg):
      return BuildFileAliases.create()

    with self.create_register(build_file_aliases=build_file_aliases) as backend_package:
      with self.assertRaises(BuildConfigurationError):
        load_backend(self.build_configuration, backend_package)

  def test_load_invalid_module(self):
    with self.create_register(module_name='register2') as backend_package:
      with self.assertRaises(BuildConfigurationError):
        load_backend(self.build_configuration, backend_package)
Example #8
0
class LoaderTest(unittest.TestCase):
  def setUp(self):
    self.build_configuration = BuildConfiguration()

  def tearDown(self):
    Goal.clear()

  @contextmanager
  def create_register(self, build_file_aliases=None, register_commands=None, register_goals=None,
                      module_name='register'):

    package_name = b'__test_package_{0}'.format(uuid.uuid4().hex)
    self.assertFalse(package_name in sys.modules)

    package_module = types.ModuleType(package_name)
    sys.modules[package_name] = package_module
    try:
      register_module_fqn = b'{0}.{1}'.format(package_name, module_name)
      register_module = types.ModuleType(register_module_fqn)
      setattr(package_module, module_name, register_module)
      sys.modules[register_module_fqn] = register_module

      def register_entrypoint(function_name, function):
        if function:
          setattr(register_module, function_name, function)

      register_entrypoint('build_file_aliases', build_file_aliases)
      register_entrypoint('register_commands', register_commands)
      register_entrypoint('register_goals', register_goals)

      yield package_name
    finally:
      del sys.modules[package_name]

  def assert_empty_aliases(self):
    registered_aliases = self.build_configuration.registered_aliases()
    self.assertEqual(0, len(registered_aliases.targets))
    self.assertEqual(0, len(registered_aliases.objects))
    self.assertEqual(0, len(registered_aliases.context_aware_object_factories))

  def test_load_valid_empty(self):
    with self.create_register() as backend_package:
      load_backend(self.build_configuration, backend_package)
      self.assert_empty_aliases()

  def test_load_valid_partial_aliases(self):
    aliases = BuildFileAliases.create(targets={'bob': Target},
                                      objects={'MEANING_OF_LIFE': 42})
    with self.create_register(build_file_aliases=lambda: aliases) as backend_package:
      load_backend(self.build_configuration, backend_package)
      registered_aliases = self.build_configuration.registered_aliases()
      self.assertEqual(Target, registered_aliases.targets['bob'])
      self.assertEqual(42, registered_aliases.objects['MEANING_OF_LIFE'])

  def test_load_valid_partial_goals(self):
    def register_goals():
      Goal.by_name('jack').install(TaskRegistrar('jill', lambda: 42))

    with self.create_register(register_goals=register_goals) as backend_package:
      Goal.clear()
      self.assertEqual(0, len(Goal.all()))

      load_backend(self.build_configuration, backend_package)
      self.assert_empty_aliases()
      self.assertEqual(1, len(Goal.all()))

      task_names = Goal.by_name('jack').ordered_task_names()
      self.assertEqual(1, len(task_names))

      task_name = task_names[0]
      self.assertEqual('jill', task_name)

  def test_load_invalid_entrypoint(self):
    def build_file_aliases(bad_arg):
      return BuildFileAliases.create()

    with self.create_register(build_file_aliases=build_file_aliases) as backend_package:
      with self.assertRaises(BuildConfigurationError):
        load_backend(self.build_configuration, backend_package)

  def test_load_invalid_module(self):
    with self.create_register(module_name='register2') as backend_package:
      with self.assertRaises(BuildConfigurationError):
        load_backend(self.build_configuration, backend_package)