Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c6aa6ca
WIP Dependency unit testing development
ChristopherBignamini Aug 27, 2019
badd939
Merge branch 'master' into inter_depentent_check_unit_test
ChristopherBignamini Aug 29, 2019
e7bb5d3
Merge branch 'master' into inter_depentent_check_unit_test
ChristopherBignamini Sep 13, 2019
9fbfb49
test cleanup procedure implemented with dependencies
ChristopherBignamini Sep 18, 2019
548082c
Merge branch 'master' into inter_depentent_check_unit_test
ChristopherBignamini Sep 18, 2019
60d2156
Merge with master
ChristopherBignamini Sep 18, 2019
bca4959
Bug fixed
ChristopherBignamini Sep 18, 2019
4a9cb19
Coding style fixes
ChristopherBignamini Sep 18, 2019
ba6b693
Updated number of loaded checks in unit tests and fixed dependency gr…
ChristopherBignamini Sep 19, 2019
ef9acf6
Dependency graph building moved into Runner class
ChristopherBignamini Sep 19, 2019
db06b16
Code cleaning
ChristopherBignamini Oct 2, 2019
268f9e6
Cleanup with dependency tests moved
ChristopherBignamini Oct 4, 2019
023b283
Code cleaning
ChristopherBignamini Oct 4, 2019
75ae31d
Dependecies cleanup tests moved to new location
ChristopherBignamini Oct 4, 2019
279757a
Code cleaning
ChristopherBignamini Oct 4, 2019
b3f726b
Code cleaning
ChristopherBignamini Oct 4, 2019
fcde831
Merge branch 'master' into inter_depentent_check_unit_test
ChristopherBignamini Oct 4, 2019
4346353
Coding style fix
ChristopherBignamini Oct 15, 2019
5d9590c
Merge branch 'master' into inter_depentent_check_unit_test
ChristopherBignamini Oct 24, 2019
b276f50
Merge branch 'master' into inter_depentent_check_unit_test
ChristopherBignamini Oct 24, 2019
9ee4fb8
Coding style fixes
ChristopherBignamini Oct 24, 2019
f181c8b
Exception handling fix in cleanup procedure.
ChristopherBignamini Oct 25, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions reframe/frontend/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import reframe.core.runtime as runtime
import reframe.frontend.argparse as argparse
import reframe.frontend.check_filters as filters
import reframe.frontend.dependency as dependency
import reframe.utility.os_ext as os_ext
from reframe.core.exceptions import (EnvironError, ConfigError, ReframeError,
ReframeFatalError, format_exception,
Expand Down Expand Up @@ -501,6 +502,10 @@ def main():
allowed_environs)

# Act on checks
# Build dependency graph and reorder test case accordingly
dependency_graph = dependency.build_deps(testcases)
dependency.validate_deps(dependency_graph)
testcases = dependency.toposort(dependency_graph)

# Unload regression's module and load user-specified modules
if hasattr(settings, 'reframe_module'):
Expand Down Expand Up @@ -543,9 +548,9 @@ def main():
elif options.run:
# Setup the execution policy
if options.exec_policy == 'serial':
exec_policy = SerialExecutionPolicy()
exec_policy = SerialExecutionPolicy(dependency_graph)
elif options.exec_policy == 'async':
exec_policy = AsynchronousExecutionPolicy()
exec_policy = AsynchronousExecutionPolicy(dependency_graph)
else:
# This should not happen, since choices are handled by
# argparser
Expand Down
15 changes: 15 additions & 0 deletions reframe/frontend/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,18 @@ def visit(node, path):
cases_by_name[c.check.name] = [c]

return list(itertools.chain(*(cases_by_name[n] for n in visited)))


def create_ref_count(graph):

# TODO: create unit test
ref_count = {}
for node, deps in graph.items():
ref_count.setdefault(node, 0)
for adj in deps:
try:
ref_count[adj] += 1
except KeyError:
ref_count[adj] = 1

return ref_count
15 changes: 14 additions & 1 deletion reframe/frontend/executors/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import abc
import copy
import collections
import sys
import weakref

Expand All @@ -9,6 +10,7 @@
import reframe.core.runtime as runtime
from reframe.core.exceptions import (AbortTaskError, JobNotStartedError,
ReframeFatalError, TaskExit)
import reframe.frontend.dependency as dependency
from reframe.frontend.printer import PrettyPrinter
from reframe.frontend.statistics import TestStats

Expand Down Expand Up @@ -255,6 +257,7 @@ def stats(self):
return self._stats

def runall(self, testcases):

num_checks = len({tc.check.name for tc in testcases})
self._printer.separator('short double line',
'Running %d check(s)' % num_checks)
Expand Down Expand Up @@ -325,7 +328,7 @@ class ExecutionPolicy(abc.ABC):

An execution policy implements the regression check pipeline.'''

def __init__(self):
def __init__(self, dependency_graph=None):
# Options controlling the check execution
self.skip_system_check = False
self.force_local = False
Expand All @@ -351,6 +354,16 @@ def __init__(self):

self.stats = None

# Check dependencies data
self.dependency_graph = dependency_graph
# For each check to be executed in a refame run
# ref_count[c] stores the number of checks depending
# on check c
if dependency_graph is not None:
self.ref_count = dependency.create_ref_count(dependency_graph)
else:
self.ref_count = None

def __repr__(self):
return debug.repr(self)

Expand Down
25 changes: 20 additions & 5 deletions reframe/frontend/executors/policies.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@


class SerialExecutionPolicy(ExecutionPolicy):
def __init__(self):
super().__init__()
def __init__(self, dependency_graph=None):
super().__init__(dependency_graph)
self._tasks = []

def runcase(self, case):
Expand Down Expand Up @@ -45,7 +45,22 @@ def runcase(self, case):
if not self.skip_performance_check:
task.performance()

task.cleanup(not self.keep_stage_files)
# Execute cleanup of current case if all dependent cases have been
# executed
if self.ref_count is None or self.ref_count[case] == 0:
task.cleanup(not self.keep_stage_files)

# Execute cleanup of dependencies if all dependent cases have been
# executed
if self.dependency_graph is not None:
for dep in self.dependency_graph[case]:
self.ref_count[dep] -= 1
if self.ref_count[dep] == 0:
# Check if dep has failed before cleaning
for t in self.stats.tasks():
if t.testcase == dep and t.failed is False:
t.cleanup(not self.keep_stage_files)
break

except TaskExit:
return
Expand Down Expand Up @@ -92,9 +107,9 @@ def __call__(self, x, init_rate):


class AsynchronousExecutionPolicy(ExecutionPolicy, TaskEventListener):
def __init__(self):
def __init__(self, dependency_graph=None):

super().__init__()
super().__init__(dependency_graph)

# All currently running tasks
self._running_tasks = []
Expand Down
127 changes: 127 additions & 0 deletions unittests/resources/checks_unlisted/cleanup_checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#
# Checks for testing the cleanup procedure with dependencies
#

import reframe as rfm
import reframe.utility.sanity as sn


@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?

def __init__(self):
self.local = True
self.executable = 'echo DependencyT0'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']
self.depends_on('DependencyT1')


@rfm.simple_test
class DependencyT1(rfm.RunOnlyRegressionTest):
def __init__(self):
self.local = True
self.executable = 'echo DependencyT1'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']
self.depends_on('DependencyT2')


@rfm.simple_test
class DependencyT2(rfm.RunOnlyRegressionTest):
def __init__(self):
self.local = True
self.executable = 'echo DependencyT2'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']
self.depends_on('DependencyT3')


@rfm.simple_test
class DependencyT3(rfm.RunOnlyRegressionTest):
def __init__(self):
self.local = True
self.executable = 'echo DependencyT3'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']


@rfm.simple_test
class MultiDependencyT0(rfm.RunOnlyRegressionTest):
def __init__(self):
self.local = True
self.executable = 'echo MultiDependencyT0'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']
self.depends_on('MultiDependencyT1')
self.depends_on('MultiDependencyT2')


@rfm.simple_test
class MultiDependencyT1(rfm.RunOnlyRegressionTest):
def __init__(self):
self.local = True
self.executable = 'echo MultiDependencyT1'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']
self.depends_on('MultiDependencyT6')


@rfm.simple_test
class MultiDependencyT2(rfm.RunOnlyRegressionTest):
def __init__(self):
self.local = True
self.executable = 'echo MultiDependencyT2'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']
self.depends_on('MultiDependencyT3')
self.depends_on('MultiDependencyT4')


@rfm.simple_test
class MultiDependencyT3(rfm.RunOnlyRegressionTest):
def __init__(self):
self.local = True
self.executable = 'echo MultiDependencyT3'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']
self.depends_on('MultiDependencyT6')
self.depends_on('MultiDependencyT5')


@rfm.simple_test
class MultiDependencyT4(rfm.RunOnlyRegressionTest):
def __init__(self):
self.local = True
self.executable = 'echo MultiDependencyT4'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']
self.depends_on('MultiDependencyT5')


@rfm.simple_test
class MultiDependencyT5(rfm.RunOnlyRegressionTest):
def __init__(self):
self.local = True
self.executable = 'echo MultiDependencyT5'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']


@rfm.simple_test
class MultiDependencyT6(rfm.RunOnlyRegressionTest):
def __init__(self):
self.local = True
self.executable = 'echo MultiDependencyT6'
self.sanity_patterns = sn.assert_found('Dependency', self.stdout)
self.valid_systems = ['*']
self.valid_prog_environs = ['*']
22 changes: 22 additions & 0 deletions unittests/resources/checks_unlisted/test_cleanup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import unittest
from unittests.test_cli import TestFrontend


class TestCleanup(TestFrontend):
def test_dependency_cli(self):
self.checkpath = [
'unittests/resources/checks_unlisted/cleanup_checks.py']
self.action = 'run'
self.more_options = ['-n', 'Dependency']
returncode, stdout, _ = self._run_reframe()
self.assertIn('Running 4 check(s)', stdout)
self.assertEqual(0, returncode)

def test_multi_dependency_cli(self):
self.checkpath = [
'unittests/resources/checks_unlisted/cleanup_checks.py']
self.action = 'run'
self.more_options = ['-n', 'MultiDependency']
returncode, stdout, _ = self._run_reframe()
self.assertIn('Running 7 check(s)', stdout)
self.assertEqual(0, returncode)