Skip to content

Conversation

@ChristopherBignamini
Copy link
Contributor

No description provided.

@pep8speaks
Copy link

pep8speaks commented Sep 18, 2019

Hello @ChristopherBignamini, Thank you for updating!

Cheers! There are no PEP8 issues in this Pull Request!Do see the ReFrame Coding Style Guide

Comment last updated at 2019-10-25 14:37:28 UTC

@ChristopherBignamini ChristopherBignamini changed the title [WIP] Test dependency for serial execution policy [wip] Test dependency for serial execution policy Sep 18, 2019
@vkarak
Copy link
Contributor

vkarak commented Sep 19, 2019

@ChristopherBignamini Unit tests fail. Can you fix them?

@vkarak vkarak changed the title [wip] Test dependency for serial execution policy [wip] [feat] Test dependency for serial execution policy Sep 20, 2019
@ChristopherBignamini
Copy link
Contributor Author

I have a two tests that fail (randomly) with Python 3.5

test_kbd_interrupt_in_wait_with_limited_concurrency and test_kbd_interrupt_in_setup_with_concurrency

In both cases there are no dependencies but I am calling the toposort method on the test list independently of the presence of dependencies. The resulting test list is reordered wrt the original list specified in the unit test and this cause, sometimes, the failure of the test. I could check if there is any dependency and if not skip the toposort step. Alternatively, I can think of a possible fix for the unit tests.

@vkarak vkarak changed the title [wip] [feat] Test dependency for serial execution policy [wip] [feat] Add support for running interdependent tests with the serial execution policy Sep 21, 2019
exec_policy.sched_nodelist = options.nodelist
exec_policy.sched_exclude_nodelist = options.exclude_nodes
exec_policy.sched_options = options.job_options

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

# TODO: create unit test
deps_count = {}
for test, deps in graph.items():
try:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this try/except is needed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need all the checks that will be executed in a reframe run in the ref_count: let's say that I've completed the task for check C, I need to know if I can delete the corresponding output and I do that when ref_count[c] goes to 0 as I do with checks C dependencies. For this reason I need to include C in the list and I'm doing it in the same way of its dependencies.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this I understand. My point here was about whether the double try/except clause is actually needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I don't understand the first += 1? Sth like the following should be enough I guess:

for test, deps in graph.items():
    ref_count.setdefault(test, 0)
    for d in deps:
        try:
            ref_count[test] += 1
        except KeyError:
            ref_count[test] = 0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right, I don't need the +1.
By the way, I see that you are using the test variable as key in the inner loop instead of using dep, but currently the content of ref_count[c], for a given check c, is the number of checks depending on c. Should I change the ref_count data meaning?

deps_count[test] += 1
except KeyError:
deps_count[test] = 0
for dep in deps:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for dep in deps:
for adj in deps:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


# TODO: create unit test
deps_count = {}
for test, deps in graph.items():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for test, deps in graph.items():
for node, deps in graph.items():

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

self.assertEqual(0, returncode)

def test_dependency_cli(self):
self.checkpath = ['unittests/resources/checks/frontend_checks.py']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't put these tests in the frontend_checks.py but rather in a different subdirectory next to the resources/checks. You wouldn't need then to update the loader unit tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can put them inside checks_unlisted/.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I have to put everything in /unittests/resources/checks_unlisted/? I mean the main test file and the single tests? There is a dependencies subfolder, should I put everything in there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

self.stats = None

# Check dependencies data
self.dependency_tree = collections.OrderedDict()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why you initialise it like that. Check my comment above on how to redesign it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

dependency_clean_up_task.cleanup(
not self.keep_stage_files,
False)
break
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why all this has to be in the finally?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to be sure that the cleanup step is always executed, also in case of exceptions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it should be that? If a test fails, its cleanup phase is not executed by just throwing the exception. Also I think that this part might be simplified if you make the serial policy also a TaskEventListener so that it gets notified when a task finishes. Then you can adjust the reference count in a cleaner way.

dependency.print_deps(dependency_graph)
testcases = dependency.toposort(dependency_graph)
self._policy.dependency_tree = dependency_graph
self._policy.dependency_count = dependency.create_deps_count(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like very much the dependency_count as a name. I would call it ref_count and have a comment explaining it. In practice, you are doing a reference counting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

self.dependency_count[dep] -= 1
if self.dependency_count[dep] == 0:
# Check if dep has failed before cleaning
for t in self.stats.tasks():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to associate the reference counter to the RegressionTask so that you avoid this for loop here and the use of stats.

# Check if dep has failed before cleaning
for t in self.stats.tasks():
if t.testcase == dep and t.failed is False:
dependency_clean_up_task = RegressionTask(dep)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you create a new "task" here? I'd just call the deferred cleanup() on the original one.

Also cleanup may throw, so it has to be treated correctly. That's why I don't think putting this loop in the finally is correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@vkarak
Copy link
Contributor

vkarak commented Oct 4, 2019

@ChristopherBignamini Can you fix the conflicts with the master?

@vkarak vkarak removed this from the ReFrame Sprint 2019w41 milestone Oct 23, 2019


@rfm.simple_test
class DependencyT0(rfm.RunOnlyRegressionTest):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't self.sourcesdir = None missing from these tests?

@vkarak
Copy link
Contributor

vkarak commented Oct 27, 2019

Closing this one. Replaced by #1015.

@vkarak vkarak closed this Oct 27, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants