def test_get_or_create_invalid_prefix(self):
        """Test the ``get_or_create_group`` method of ``Autogroup`` when there is already a group
        with the same prefix, but followed by other non-underscore characters."""
        label_prefix = 'new_test_prefix_TestAutogroup'
        # I create a group with the same prefix, but followed by non-underscore
        # characters. These should be ignored in the logic.
        AutoGroup(label=f'{label_prefix}xx').store()

        # Check that there are no groups to begin with
        queryb = QueryBuilder().append(AutoGroup, filters={'label': label_prefix})
        assert not list(queryb.all())
        queryb = QueryBuilder().append(AutoGroup, filters={'label': {'like': r'{}\_%'.format(label_prefix)}})
        assert not list(queryb.all())

        # First group (no existing one)
        autogroup = Autogroup()
        autogroup.set_group_label_prefix(label_prefix)
        group = autogroup.get_or_create_group()
        expected_label = label_prefix
        self.assertEqual(
            group.label, expected_label,
            f"The auto-group should be labelled '{expected_label}', it is instead '{group.label}'"
        )

        # Second group (only one with no suffix existing)
        autogroup = Autogroup()
        autogroup.set_group_label_prefix(label_prefix)
        group = autogroup.get_or_create_group()
        expected_label = f'{label_prefix}_1'
        self.assertEqual(
            group.label, expected_label,
            f"The auto-group should be labelled '{expected_label}', it is instead '{group.label}'"
        )
Exemple #2
0
def setup_groups(clear_database_before_test):
    """Create some groups to test the `GroupParamType` parameter type for the command line infrastructure.

    We create an initial group with a random name and then on purpose create two groups with a name that matches exactly
    the ID and UUID, respectively, of the first one. This allows us to test the rules implemented to solve ambiguities
    that arise when determing the identifier type.
    """
    entity_01 = Group(label='group_01').store()
    entity_02 = AutoGroup(label=str(entity_01.pk)).store()
    entity_03 = ImportGroup(label=str(entity_01.uuid)).store()
    return entity_01, entity_02, entity_03
Exemple #3
0
    def get_or_create_group(self):
        """Return the current `AutoGroup`, or create one if None has been set yet.

        This function implements a somewhat complex logic that is however needed
        to make sure that, even if `verdi run` is called at the same time multiple
        times, e.g. in a for loop in bash, there is never the risk that two ``verdi run``
        Unix processes try to create the same group, with the same label, ending
        up in a crash of the code (see PR #3650).

        Here, instead, we make sure that if this concurrency issue happens,
        one of the two will get a IntegrityError from the DB, and then recover
        trying to create a group with a different label (with a numeric suffix appended),
        until it manages to create it.
        """
        from aiida.orm import QueryBuilder

        # When this function is called, if it is the first time, just generate
        # a new group name (later on, after this ``if`` block`).
        # In that case, we will later cache in ``self._group_label`` the group label,
        # So the group with the same name can be returned quickly in future
        # calls of this method.
        if self._group_label is not None:
            builder = QueryBuilder().append(
                AutoGroup, filters={'label': self._group_label})
            results = [res[0] for res in builder.iterall()]
            if results:
                # If it is not empty, it should have only one result due to the uniqueness constraints
                assert len(
                    results
                ) == 1, 'I got more than one autogroup with the same label!'
                return results[0]
            # There are no results: probably the group has been deleted.
            # I continue as if it was not cached
            self._group_label = None

        label_prefix = self.get_group_label_prefix()
        # Try to do a preliminary QB query to avoid to do too many try/except
        # if many of the prefix_NUMBER groups already exist
        queryb = QueryBuilder().append(
            AutoGroup,
            filters={
                'or': [{
                    'label': {
                        '==': label_prefix
                    }
                }, {
                    'label': {
                        'like': escape_for_sql_like(label_prefix + '_') + '%'
                    }
                }]
            },
            project='label')
        existing_group_labels = [
            res[0][len(label_prefix):] for res in queryb.all()
        ]
        existing_group_ints = []
        for label in existing_group_labels:
            if label == '':
                # This is just the prefix without name - corresponds to counter = 0
                existing_group_ints.append(0)
            elif label.startswith('_'):
                try:
                    existing_group_ints.append(int(label[1:]))
                except ValueError:
                    # It's not an integer, so it will never collide - just ignore it
                    pass

        if not existing_group_ints:
            counter = 0
        else:
            counter = max(existing_group_ints) + 1

        while True:
            try:
                label = label_prefix if counter == 0 else '{}_{}'.format(
                    label_prefix, counter)
                group = AutoGroup(label=label).store()
                self._group_label = group.label
            except exceptions.IntegrityError:
                counter += 1
            else:
                break

        return group
Exemple #4
0
    def test_get_or_create(self):
        """Test the ``get_or_create_group`` method of ``Autogroup``."""
        label_prefix = 'test_prefix_TestAutogroup'

        # Check that there are no groups to begin with
        queryb = QueryBuilder().append(AutoGroup,
                                       filters={'label': label_prefix})
        assert not list(queryb.all())
        queryb = QueryBuilder().append(
            AutoGroup,
            filters={'label': {
                'like': r'{}\_%'.format(label_prefix)
            }})
        assert not list(queryb.all())

        # First group (no existing one)
        autogroup = Autogroup()
        autogroup.set_group_label_prefix(label_prefix)
        group = autogroup.get_or_create_group()
        expected_label = label_prefix
        self.assertEqual(
            group.label, expected_label,
            "The auto-group should be labelled '{}', it is instead '{}'".
            format(expected_label, group.label))

        # Second group (only one with no suffix existing)
        autogroup = Autogroup()
        autogroup.set_group_label_prefix(label_prefix)
        group = autogroup.get_or_create_group()
        expected_label = label_prefix + '_1'
        self.assertEqual(
            group.label, expected_label,
            "The auto-group should be labelled '{}', it is instead '{}'".
            format(expected_label, group.label))

        # Second group (only one suffix _1 existing)
        autogroup = Autogroup()
        autogroup.set_group_label_prefix(label_prefix)
        group = autogroup.get_or_create_group()
        expected_label = label_prefix + '_2'
        self.assertEqual(
            group.label, expected_label,
            "The auto-group should be labelled '{}', it is instead '{}'".
            format(expected_label, group.label))

        # I create a group with a large integer suffix (9)
        AutoGroup(label='{}_9'.format(label_prefix)).store()
        # The next autogroup should become number 10
        autogroup = Autogroup()
        autogroup.set_group_label_prefix(label_prefix)
        group = autogroup.get_or_create_group()
        expected_label = label_prefix + '_10'
        self.assertEqual(
            group.label, expected_label,
            "The auto-group should be labelled '{}', it is instead '{}'".
            format(expected_label, group.label))

        # I create a group with a non-integer suffix (15a), it should be ignored
        AutoGroup(label='{}_15b'.format(label_prefix)).store()
        # The next autogroup should become number 11
        autogroup = Autogroup()
        autogroup.set_group_label_prefix(label_prefix)
        group = autogroup.get_or_create_group()
        expected_label = label_prefix + '_11'
        self.assertEqual(
            group.label, expected_label,
            "The auto-group should be labelled '{}', it is instead '{}'".
            format(expected_label, group.label))