From 696ac856af123146b33acd3da1979ada4d58333a Mon Sep 17 00:00:00 2001 From: Hilary James Oliver Date: Mon, 12 Mar 2018 13:30:58 +1300 Subject: [PATCH 1/5] Fix prerequisite and output manipulation on task state changes. --- bin/cylc-reset | 16 +- lib/cylc/task_events_mgr.py | 20 +- lib/cylc/task_job_mgr.py | 2 +- lib/cylc/task_outputs.py | 4 + lib/cylc/task_pool.py | 66 ++--- lib/cylc/task_state.py | 141 +++++---- tests/cylc-kill/00-multi-hosts-compat.t | 2 +- tests/cylc-show/06-prereqs-outputs.t | 302 ++++++++++++++++++++ tests/cylc-show/prereqs-outputs/suite.rc | 59 ++++ tests/job-submission/04-submit-num/suite.rc | 2 +- tests/pre-initial/warm-insert/suite.rc | 4 +- tests/registration/00-simple.t | 3 + tests/registration/01-no-skip1.t | 2 + 13 files changed, 490 insertions(+), 133 deletions(-) create mode 100644 tests/cylc-show/06-prereqs-outputs.t create mode 100644 tests/cylc-show/prereqs-outputs/suite.rc diff --git a/bin/cylc-reset b/bin/cylc-reset index 7ed0441206c..5f63b308402 100755 --- a/bin/cylc-reset +++ b/bin/cylc-reset @@ -18,15 +18,19 @@ """cylc [control] reset [OPTIONS] ARGS -Force one or more task proxies in a running suite to change state and modify -their prerequisites and outputs accordingly. For example, --state=waiting -means "prerequisites not satisfied, outputs not completed"; --state=ready means -"prerequisites satisfied, outputs not completed" (this generally has the same -effect as using the "cylc trigger" command). +Force tasks to a specified state, and modify their prerequisites and outputs +accordingly. +Outputs are automatically updated to reflect the new task state, except for +custom message outputs - which can be manipulated directly with "--output". + +Prerequisites reflect the state of other tasks; they are not changed except +to unset them on resetting the task state to 'waiting' or earlier. + +To hold and release tasks use "cylc hold" and "cylc release". "cylc reset --state=spawn" is deprecated: use "cylc spawn" instead. -See the documentation for the -s/--state option for legal reset states.""" +""" import os import sys diff --git a/lib/cylc/task_events_mgr.py b/lib/cylc/task_events_mgr.py index 8d7a33b7f6b..3dbfbe82739 100644 --- a/lib/cylc/task_events_mgr.py +++ b/lib/cylc/task_events_mgr.py @@ -52,7 +52,7 @@ TASK_STATUS_FAILED, TASK_STATUS_SUCCEEDED) from cylc.task_outputs import ( TASK_OUTPUT_SUBMITTED, TASK_OUTPUT_STARTED, TASK_OUTPUT_SUCCEEDED, - TASK_OUTPUT_FAILED) + TASK_OUTPUT_FAILED, TASK_OUTPUT_SUBMIT_FAILED, TASK_OUTPUT_EXPIRED) from cylc.wallclock import ( get_current_time_string, RE_DATE_TIME_FORMAT_EXTENDED) @@ -250,7 +250,7 @@ def process_events(self, schd_ctx): def _poll_to_confirm(self, itask, status_gt, poll_func): """Poll itask to confirm an apparent state reversal.""" - if (itask.state.is_greater_than(status_gt) and not + if (itask.state.is_gt(status_gt) and not itask.state.confirming_with_poll): poll_func(self.suite, [itask], msg="polling %s to confirm state" % itask.identity) @@ -663,10 +663,15 @@ def _process_message_succeeded(self, itask, event_time): itask.summary['finished_time'] - itask.summary['started_time']) if not itask.state.outputs.all_completed(): - message = "Succeeded with unreported outputs:" + msg = "" for output in itask.state.outputs.get_not_completed(): - message += "\n " + output - LOG.info(message, itask=itask) + if output not in [TASK_OUTPUT_EXPIRED, + TASK_OUTPUT_SUBMIT_FAILED, + TASK_OUTPUT_FAILED]: + msg += "\n " + output + if msg: + LOG.info("Succeeded with outputs not completed: %s" % msg, + itask=itask) itask.state.reset_state(TASK_STATUS_SUCCEEDED) self.setup_event_handlers(itask, "succeeded", "job succeeded") @@ -735,9 +740,8 @@ def _process_message_submitted(self, itask, event_time): self.pflag = True if itask.state.status == TASK_STATUS_READY: - # In rare occassions, the submit command of a batch system has sent - # the job to its server, and the server has started the job before - # the job submit command returns. + # The job started message can (rarely) come in before the submit + # command returns - in which case do not go back to 'submitted'. itask.state.reset_state(TASK_STATUS_SUBMITTED) try: itask.timeout_timers[TASK_STATUS_SUBMITTED] = ( diff --git a/lib/cylc/task_job_mgr.py b/lib/cylc/task_job_mgr.py index 94e6a6af9c1..4fe9a367ffc 100644 --- a/lib/cylc/task_job_mgr.py +++ b/lib/cylc/task_job_mgr.py @@ -181,7 +181,7 @@ def submit_task_jobs(self, suite, itasks, is_simulation=False): This method uses prep_submit_task_job() as helper. - Return (list): list of tasks that attempted submission + Return (list): list of tasks that attempted submission. """ if is_simulation: return self._simulation_submit_task_jobs(itasks) diff --git a/lib/cylc/task_outputs.py b/lib/cylc/task_outputs.py index f0d57b3e3fc..ce7c5704886 100644 --- a/lib/cylc/task_outputs.py +++ b/lib/cylc/task_outputs.py @@ -60,6 +60,10 @@ class TaskOutputs(object): def __init__(self, tdef): self._by_message = {} self._by_trigger = {} + # Add standard outputs. + for output in _SORT_ORDERS: + self.add(output) + # Add custom message outputs. for trigger, message in tdef.outputs: self.add(message, trigger) diff --git a/lib/cylc/task_pool.py b/lib/cylc/task_pool.py index f4e5fdc38a1..2a8128ec5dd 100644 --- a/lib/cylc/task_pool.py +++ b/lib/cylc/task_pool.py @@ -329,23 +329,17 @@ def release_runahead_tasks(self): def load_db_task_pool_for_restart(self, row_idx, row): """Load a task from previous task pool. - The state of task prerequisites (satisfied or not) and outputs - (completed or not) is determined by the recorded TASK_STATUS: - - TASK_STATUS_WAITING - prerequisites and outputs unsatisified - TASK_STATUS_HELD - ditto (only waiting tasks can be held) - TASK_STATUS_QUEUED - prereqs satisfied, outputs not completed - (only tasks ready to run can get queued) - TASK_STATUS_READY - ditto - TASK_STATUS_SUBMITTED - ditto (but see *) - TASK_STATUS_SUBMIT_RETRYING - ditto - TASK_STATUS_RUNNING - ditto (but see *) - TASK_STATUS_FAILED - ditto (tasks must run in order to fail) - TASK_STATUS_RETRYING - ditto (tasks must fail in order to retry) - TASK_STATUS_SUCCEEDED - prerequisites satisfied, outputs completed - - (*) tasks reloaded with TASK_STATUS_SUBMITTED or TASK_STATUS_RUNNING - are polled to determine what their true status is. + Output completion status is loaded from the DB, and tasks recorded + as submitted or running are polled to confirm their true status. + + Prerequisite status (satisfied or not) is inferred from task status: + WAITING or HELD - all prerequisites unsatisified + status > QUEUED - all prerequisites satisfied. + TODO - this is not correct, e.g. a held task may have some (but not + all) satisified prerequisites; and a running task (etc.) could have + been manually triggered with unsatisfied prerequisites. See comments + in GitHub #2329 on how to fix this in the future. + """ if row_idx == 0: LOG.info("LOADING task proxies") @@ -403,8 +397,7 @@ def load_db_task_pool_for_restart(self, row_idx, row): itask.state.reset_state(status) - # Tasks that are running or finished can have completed custom - # outputs + # Running or finished task can have completed custom outputs. if status in [ TASK_STATUS_RUNNING, TASK_STATUS_FAILED, TASK_STATUS_SUCCEEDED]: @@ -614,6 +607,7 @@ def get_ready_tasks(self): n_release -= 1 ready_tasks.append(itask) itask.reset_manual_trigger() + # (Set to 'ready' is done just before job submission). # else leaved queued LOG.debug('%d task(s) de-queued' % len(ready_tasks)) @@ -955,7 +949,8 @@ def spawn_all_tasks(self): itask.state.status != TASK_STATUS_SUBMIT_FAILED and ( itask.tdef.spawn_ahead or - itask.state.is_greater_than(TASK_STATUS_READY) + itask.state.status == TASK_STATUS_EXPIRED or + itask.state.is_gt(TASK_STATUS_READY) ) ): if self.force_spawn(itask) is not None: @@ -1044,24 +1039,17 @@ def spawn_tasks(self, items): return len(bad_items) def reset_task_states(self, items, status, outputs): - """Reset task states.""" + """Operator-forced task status reset and output manipulation.""" itasks, bad_items = self.filter_task_proxies(items) for itask in itasks: if status and status != itask.state.status: LOG.info("resetting state to %s" % status, itask=itask) - if status == TASK_STATUS_READY: - # Pseudo state (in this context) - - # set waiting and satisified. - itask.state.reset_state(TASK_STATUS_WAITING) - itask.state.set_prerequisites_all_satisfied() - itask.state.unset_special_outputs() - itask.state.outputs.set_all_incomplete() - else: - itask.state.reset_state(status) - if status in [ - TASK_STATUS_FAILED, TASK_STATUS_SUBMIT_FAILED]: - itask.set_event_time('finished', - get_current_time_string()) + itask.state.reset_state(status, forced=True) + if status in [TASK_STATUS_FAILED, + TASK_STATUS_SUBMIT_FAILED]: + # TODO - HUH? SUBMIT_FAILED? WHAT ABOUT SUCCEEDED? + itask.set_event_time('finished', + get_current_time_string()) if outputs: for output in outputs: is_completed = True @@ -1070,10 +1058,12 @@ def reset_task_states(self, items, status, outputs): output = output[1:] if output == '*' and is_completed: itask.state.outputs.set_all_completed() - LOG.info("reset all output to completed", itask=itask) + LOG.info("reset all outputs to completed", + itask=itask) elif output == '*': itask.state.outputs.set_all_incomplete() - LOG.info("reset all output to incomplete", itask=itask) + LOG.info("reset all outputs to incomplete", + itask=itask) else: ret = itask.state.outputs.set_msg_trg_completion( message=output, is_completed=is_completed) @@ -1105,7 +1095,7 @@ def remove_tasks(self, items, spawn=False): return len(bad_items) def trigger_tasks(self, items, back_out=False): - """Trigger tasks.""" + """Operator-forced task triggering.""" itasks, bad_items = self.filter_task_proxies(items) n_warnings = len(bad_items) for itask in itasks: @@ -1120,7 +1110,7 @@ def trigger_tasks(self, items, back_out=False): continue itask.manual_trigger = True if not itask.state.status == TASK_STATUS_QUEUED: - itask.state.reset_state(TASK_STATUS_READY) + itask.state.reset_state(TASK_STATUS_READY, forced=True) return n_warnings def check_auto_shutdown(self): diff --git a/lib/cylc/task_state.py b/lib/cylc/task_state.py index 769dcae7426..8a559e5eead 100644 --- a/lib/cylc/task_state.py +++ b/lib/cylc/task_state.py @@ -30,19 +30,32 @@ from cylc.wallclock import get_current_time_string -# Task status names. +# Task status names and meanings. +# Held back from dependency matching, in the runahead pool: TASK_STATUS_RUNAHEAD = "runahead" +# Held back from job submission due to un-met prerequisites: TASK_STATUS_WAITING = "waiting" +# Held back from job submission even if prerequisites are met: TASK_STATUS_HELD = "held" +# Prerequisites met, but held back in a limited internal queue: TASK_STATUS_QUEUED = "queued" +# Ready (prerequisites met) to be passed to job submission system: TASK_STATUS_READY = "ready" +# Prerequisites unmet for too long - will never be submitted now: TASK_STATUS_EXPIRED = "expired" +# Job submitted to run: TASK_STATUS_SUBMITTED = "submitted" +# Job submission failed: TASK_STATUS_SUBMIT_FAILED = "submit-failed" +# Job submission failed but will try again soon: TASK_STATUS_SUBMIT_RETRYING = "submit-retrying" +# Job execution started, but not completed yet: TASK_STATUS_RUNNING = "running" +# Job execution completed successfully: TASK_STATUS_SUCCEEDED = "succeeded" +# Job execution failed: TASK_STATUS_FAILED = "failed" +# Job execution failed, but will try again soon: TASK_STATUS_RETRYING = "retrying" # Tasks statuses ordered according to task runtime progression. @@ -51,15 +64,15 @@ TASK_STATUS_WAITING, TASK_STATUS_HELD, TASK_STATUS_QUEUED, - TASK_STATUS_READY, TASK_STATUS_EXPIRED, - TASK_STATUS_SUBMITTED, + TASK_STATUS_READY, TASK_STATUS_SUBMIT_FAILED, TASK_STATUS_SUBMIT_RETRYING, + TASK_STATUS_SUBMITTED, + TASK_STATUS_RETRYING, TASK_STATUS_RUNNING, - TASK_STATUS_SUCCEEDED, TASK_STATUS_FAILED, - TASK_STATUS_RETRYING + TASK_STATUS_SUCCEEDED ] TASK_STATUSES_ALL = set(TASK_STATUSES_ORDERED) @@ -75,14 +88,12 @@ TASK_STATUS_RETRYING ]) -# Task statuses we can manually reset a task TO. +# Task statuses we can manually reset a task to. TASK_STATUSES_CAN_RESET_TO = set([ TASK_STATUS_SUBMITTED, TASK_STATUS_SUBMIT_FAILED, TASK_STATUS_RUNNING, TASK_STATUS_WAITING, - TASK_STATUS_HELD, - TASK_STATUS_READY, TASK_STATUS_EXPIRED, TASK_STATUS_SUCCEEDED, TASK_STATUS_FAILED @@ -156,6 +167,18 @@ ]) +def status_leq(status_a, status_b): + """"Return True if status_a <= status_b""" + return (TASK_STATUSES_ORDERED.index(status_a) <= + TASK_STATUSES_ORDERED.index(status_b)) + + +def status_geq(status_a, status_b): + """"Return True if status_a >= status_b""" + return (TASK_STATUSES_ORDERED.index(status_a) >= + TASK_STATUSES_ORDERED.index(status_b)) + + class TaskState(object): """Task status and utilities.""" @@ -188,14 +211,7 @@ def __init__(self, tdef, point, status, hold_swap): # set unsatisfied self.external_triggers[ext] = False - # Message outputs. self.outputs = TaskOutputs(tdef) - - # Standard outputs. - self.outputs.add(TASK_OUTPUT_SUBMITTED) - self.outputs.add(TASK_OUTPUT_STARTED) - self.outputs.add(TASK_OUTPUT_SUCCEEDED) - self.kill_failed = False self.confirming_with_poll = False @@ -276,17 +292,6 @@ def get_resolved_dependencies(self): return list(sorted(dep for prereq in self.prerequisites for dep in prereq.get_resolved_dependencies())) - def unset_special_outputs(self): - """Remove special outputs added for triggering purposes. - - (Otherwise they appear as incomplete outputs when the task finishes). - - """ - self.kill_failed = False - self.outputs.remove(TASK_OUTPUT_EXPIRED) - self.outputs.remove(TASK_OUTPUT_SUBMIT_FAILED) - self.outputs.remove(TASK_OUTPUT_FAILED) - def set_held(self): """Set state to TASK_STATUS_HELD, if possible. @@ -313,57 +318,41 @@ def unset_held(self): else: self.reset_state(self.hold_swap) - def reset_state(self, status): - """Reset status of task.""" - if status == TASK_STATUS_EXPIRED: - self.set_prerequisites_all_satisfied() - self.unset_special_outputs() + def reset_state(self, status, forced=False): + """Change status, and manipulate outputs and prerequisites accordingly. + + Outputs are manipulated on manual state reset to reflect the new task + status, except for custom outputs on reset to succeeded or later - + these can be completed if need be using "cylc reset --output". + + Prerequisites, which reflect the state of *other tasks*, are not + manipulated, except to unset them on reset to waiting or earlier. + (TODO - we should not do this - see GitHub #2329). + + The held state is handled in set/unset_held() for swap-state handling. + + """ + self.kill_failed = False + + # Set standard outputs in accordance with task state. + if status_leq(status, TASK_STATUS_SUBMITTED): self.outputs.set_all_incomplete() - self.outputs.add(TASK_OUTPUT_EXPIRED, is_completed=True) - elif status == TASK_STATUS_WAITING: + self.outputs.set_completion( + TASK_OUTPUT_EXPIRED, status == TASK_STATUS_EXPIRED) + self.outputs.set_completion( + TASK_OUTPUT_SUBMITTED, status_geq(status, TASK_STATUS_SUBMITTED)) + self.outputs.set_completion( + TASK_OUTPUT_STARTED, status_geq(status, TASK_STATUS_RUNNING)) + self.outputs.set_completion( + TASK_OUTPUT_SUBMIT_FAILED, status == TASK_STATUS_SUBMIT_FAILED) + self.outputs.set_completion( + TASK_OUTPUT_SUCCEEDED, status == TASK_STATUS_SUCCEEDED) + self.outputs.set_completion( + TASK_OUTPUT_FAILED, status == TASK_STATUS_FAILED) + + # Set prerequisites on forced reset to waiting (see docstring). + if forced and status == TASK_STATUS_WAITING: self.set_prerequisites_not_satisfied() - self.unset_special_outputs() - self.outputs.set_all_incomplete() - elif status == TASK_STATUS_READY: - self.set_prerequisites_all_satisfied() - self.unset_special_outputs() - self.outputs.set_all_incomplete() - elif status == TASK_STATUS_SUBMITTED: - self.set_prerequisites_all_satisfied() - self.outputs.set_completion(TASK_OUTPUT_SUBMITTED, True) - # In case of manual reset, set final outputs incomplete (but assume - # completed message outputs remain completed). - self.outputs.set_completion(TASK_OUTPUT_SUCCEEDED, False) - self.outputs.set_completion(TASK_OUTPUT_FAILED, False) - elif status == TASK_STATUS_RUNNING: - self.set_prerequisites_all_satisfied() - self.outputs.set_completion(TASK_OUTPUT_SUBMITTED, True) - self.outputs.set_completion(TASK_OUTPUT_STARTED, True) - # In case of manual reset, set final outputs incomplete (but assume - # completed message outputs remain completed). - self.outputs.set_completion(TASK_OUTPUT_SUCCEEDED, False) - self.outputs.set_completion(TASK_OUTPUT_FAILED, False) - elif status == TASK_STATUS_SUBMIT_RETRYING: - self.set_prerequisites_all_satisfied() - self.outputs.remove(TASK_OUTPUT_SUBMITTED) - elif status == TASK_STATUS_SUBMIT_FAILED: - self.set_prerequisites_all_satisfied() - self.outputs.remove(TASK_OUTPUT_SUBMITTED) - self.outputs.add(TASK_OUTPUT_SUBMIT_FAILED, is_completed=True) - elif status == TASK_STATUS_SUCCEEDED: - self.set_prerequisites_all_satisfied() - self.unset_special_outputs() - self.outputs.set_completion(TASK_OUTPUT_SUBMITTED, True) - self.outputs.set_completion(TASK_OUTPUT_STARTED, True) - self.outputs.set_completion(TASK_OUTPUT_SUCCEEDED, True) - elif status == TASK_STATUS_RETRYING: - self.set_prerequisites_all_satisfied() - self.outputs.set_all_incomplete() - elif status == TASK_STATUS_FAILED: - self.set_prerequisites_all_satisfied() - self.outputs.set_all_incomplete() - # Set a new failed output just as if a failure message came in - self.outputs.add(TASK_OUTPUT_FAILED, is_completed=True) return self._set_state(status) @@ -398,7 +387,7 @@ def _set_state(self, status): message += " (%s)" % self.hold_swap LOG.debug(message, itask=self.identity) - def is_greater_than(self, status): + def is_gt(self, status): """"Return True if self.status > status.""" return (TASK_STATUSES_ORDERED.index(self.status) > TASK_STATUSES_ORDERED.index(status)) diff --git a/tests/cylc-kill/00-multi-hosts-compat.t b/tests/cylc-kill/00-multi-hosts-compat.t index 3586227b2e1..fa274d7e38f 100755 --- a/tests/cylc-kill/00-multi-hosts-compat.t +++ b/tests/cylc-kill/00-multi-hosts-compat.t @@ -26,7 +26,7 @@ fi set_test_number 3 -export CYLC_CONF_PATH= +create_test_globalrc install_suite "${TEST_NAME_BASE}" "${TEST_NAME_BASE}" run_ok "${TEST_NAME_BASE}-validate" \ diff --git a/tests/cylc-show/06-prereqs-outputs.t b/tests/cylc-show/06-prereqs-outputs.t new file mode 100644 index 00000000000..72910c83dde --- /dev/null +++ b/tests/cylc-show/06-prereqs-outputs.t @@ -0,0 +1,302 @@ +#!/bin/bash +# THIS FILE IS PART OF THE CYLC SUITE ENGINE. +# Copyright (C) 2008-2018 NIWA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +#------------------------------------------------------------------------------- +# Use "cylc show" to confirm that the prerequisites and outputs of a task (with +# conditional triggers) are as expected after various internal and forced state +# changes. See GitHub #2599, #2600, #2329. + +. $(dirname $0)/test_header + +set_test_number 10 + +install_suite $TEST_NAME_BASE prereqs-outputs +SHARE=$(cylc get-site-config --print-run-dir)/${SUITE_NAME}/share + +#------------------------------------------------------------------------------- +TEST_NAME=$TEST_NAME_BASE-validate +run_ok $TEST_NAME cylc validate "$SUITE_NAME" + +#------------------------------------------------------------------------------- +TEST_NAME=$TEST_NAME_BASE-run +suite_run_ok $TEST_NAME cylc run --debug --no-detach "$SUITE_NAME" + +#------------------------------------------------------------------------------- +# While bar_4 runs, before baz.1 runs. +TEST_NAME=$TEST_NAME_BASE-show-bar_x4 +cmp_ok "${SHARE}/bar_x4.out" <<__SHOW_OUTPUT__ +title: (not given) +description: (not given) +URL: (not given) + +prerequisites (- => not satisfied): + - ((1 | 0) & (3 | 2) & (5 | 4) & (7 | 6)) + + 0 = bar_x1.1 failed + - 1 = bar_x1.1 succeeded + + 2 = bar_x2.1 failed + - 3 = bar_x2.1 succeeded + + 4 = bar_x3.1 failed + - 5 = bar_x3.1 succeeded + - 6 = bar_x4.1 failed + - 7 = bar_x4.1 succeeded + + (0 | 1 | 2 | 3) + - 0 = foo_x1.1 succeeded + - 1 = foo_x2.1 succeeded + - 2 = foo_x3.1 succeeded + + 3 = foo_x4.1 succeeded + +outputs (- => not completed): + - baz.1 expired + - baz.1 submitted + - baz.1 submit-failed + - baz.1 started + - baz.1 succeeded + - baz.1 failed +__SHOW_OUTPUT__ + +#------------------------------------------------------------------------------- +# While baz.1 runs +TEST_NAME=$TEST_NAME_BASE-show-baz +cmp_ok "${SHARE}/baz2.out" <<__SHOW_OUTPUT__ +title: (not given) +description: (not given) +URL: (not given) + +prerequisites (- => not satisfied): + + ((1 | 0) & (3 | 2) & (5 | 4) & (7 | 6)) + + 0 = bar_x1.1 failed + - 1 = bar_x1.1 succeeded + + 2 = bar_x2.1 failed + - 3 = bar_x2.1 succeeded + + 4 = bar_x3.1 failed + - 5 = bar_x3.1 succeeded + - 6 = bar_x4.1 failed + + 7 = bar_x4.1 succeeded + + (0 | 1 | 2 | 3) + - 0 = foo_x1.1 succeeded + - 1 = foo_x2.1 succeeded + - 2 = foo_x3.1 succeeded + + 3 = foo_x4.1 succeeded + +outputs (- => not completed): + - baz.1 expired + + baz.1 submitted + - baz.1 submit-failed + + baz.1 started + - baz.1 succeeded + - baz.1 failed +__SHOW_OUTPUT__ + +#------------------------------------------------------------------------------- +# After baz.1 succeeded. +TEST_NAME=$TEST_NAME_BASE-show-succeeded +cmp_ok "${SHARE}/succeeded.out" <<__SHOW_OUTPUT__ +title: (not given) +description: (not given) +URL: (not given) + +prerequisites (- => not satisfied): + + ((1 | 0) & (3 | 2) & (5 | 4) & (7 | 6)) + + 0 = bar_x1.1 failed + - 1 = bar_x1.1 succeeded + + 2 = bar_x2.1 failed + - 3 = bar_x2.1 succeeded + + 4 = bar_x3.1 failed + - 5 = bar_x3.1 succeeded + - 6 = bar_x4.1 failed + + 7 = bar_x4.1 succeeded + + (0 | 1 | 2 | 3) + - 0 = foo_x1.1 succeeded + - 1 = foo_x2.1 succeeded + - 2 = foo_x3.1 succeeded + + 3 = foo_x4.1 succeeded + +outputs (- => not completed): + - baz.1 expired + + baz.1 submitted + - baz.1 submit-failed + + baz.1 started + + baz.1 succeeded + - baz.1 failed +__SHOW_OUTPUT__ + +#------------------------------------------------------------------------------- +# After baz.1 reset to expired. +TEST_NAME=$TEST_NAME_BASE-show-expired +cmp_ok "${SHARE}/expired.out" <<__SHOW_OUTPUT__ +title: (not given) +description: (not given) +URL: (not given) + +prerequisites (- => not satisfied): + + ((1 | 0) & (3 | 2) & (5 | 4) & (7 | 6)) + + 0 = bar_x1.1 failed + - 1 = bar_x1.1 succeeded + + 2 = bar_x2.1 failed + - 3 = bar_x2.1 succeeded + + 4 = bar_x3.1 failed + - 5 = bar_x3.1 succeeded + - 6 = bar_x4.1 failed + + 7 = bar_x4.1 succeeded + + (0 | 1 | 2 | 3) + - 0 = foo_x1.1 succeeded + - 1 = foo_x2.1 succeeded + - 2 = foo_x3.1 succeeded + + 3 = foo_x4.1 succeeded + +outputs (- => not completed): + + baz.1 expired + - baz.1 submitted + - baz.1 submit-failed + - baz.1 started + - baz.1 succeeded + - baz.1 failed +__SHOW_OUTPUT__ + +#------------------------------------------------------------------------------- +# After baz.1 reset to failed. +TEST_NAME=$TEST_NAME_BASE-show-failed +cmp_ok "${SHARE}/failed.out" <<__SHOW_OUTPUT__ +title: (not given) +description: (not given) +URL: (not given) + +prerequisites (- => not satisfied): + + ((1 | 0) & (3 | 2) & (5 | 4) & (7 | 6)) + + 0 = bar_x1.1 failed + - 1 = bar_x1.1 succeeded + + 2 = bar_x2.1 failed + - 3 = bar_x2.1 succeeded + + 4 = bar_x3.1 failed + - 5 = bar_x3.1 succeeded + - 6 = bar_x4.1 failed + + 7 = bar_x4.1 succeeded + + (0 | 1 | 2 | 3) + - 0 = foo_x1.1 succeeded + - 1 = foo_x2.1 succeeded + - 2 = foo_x3.1 succeeded + + 3 = foo_x4.1 succeeded + +outputs (- => not completed): + - baz.1 expired + + baz.1 submitted + - baz.1 submit-failed + + baz.1 started + - baz.1 succeeded + + baz.1 failed +__SHOW_OUTPUT__ + +#------------------------------------------------------------------------------- +# After baz.1 reset to submit-failed +TEST_NAME=$TEST_NAME_BASE-show-submit-failed +cmp_ok "${SHARE}/submit-failed.out" <<__SHOW_OUTPUT__ +title: (not given) +description: (not given) +URL: (not given) + +prerequisites (- => not satisfied): + + ((1 | 0) & (3 | 2) & (5 | 4) & (7 | 6)) + + 0 = bar_x1.1 failed + - 1 = bar_x1.1 succeeded + + 2 = bar_x2.1 failed + - 3 = bar_x2.1 succeeded + + 4 = bar_x3.1 failed + - 5 = bar_x3.1 succeeded + - 6 = bar_x4.1 failed + + 7 = bar_x4.1 succeeded + + (0 | 1 | 2 | 3) + - 0 = foo_x1.1 succeeded + - 1 = foo_x2.1 succeeded + - 2 = foo_x3.1 succeeded + + 3 = foo_x4.1 succeeded + +outputs (- => not completed): + - baz.1 expired + - baz.1 submitted + + baz.1 submit-failed + - baz.1 started + - baz.1 succeeded + - baz.1 failed +__SHOW_OUTPUT__ + +#------------------------------------------------------------------------------- +# After removing upstream dependences and then resetting baz.1 to waiting. +TEST_NAME=$TEST_NAME_BASE-show-waiting +cmp_ok "${SHARE}/waiting.out" <<__SHOW_OUTPUT__ +title: (not given) +description: (not given) +URL: (not given) + +prerequisites (- => not satisfied): + - ((1 | 0) & (3 | 2) & (5 | 4) & (7 | 6)) + - 0 = bar_x1.1 failed + - 1 = bar_x1.1 succeeded + - 2 = bar_x2.1 failed + - 3 = bar_x2.1 succeeded + - 4 = bar_x3.1 failed + - 5 = bar_x3.1 succeeded + - 6 = bar_x4.1 failed + - 7 = bar_x4.1 succeeded + - (0 | 1 | 2 | 3) + - 0 = foo_x1.1 succeeded + - 1 = foo_x2.1 succeeded + - 2 = foo_x3.1 succeeded + - 3 = foo_x4.1 succeeded + +outputs (- => not completed): + - baz.1 expired + - baz.1 submitted + - baz.1 submit-failed + - baz.1 started + - baz.1 succeeded + - baz.1 failed +__SHOW_OUTPUT__ + +#------------------------------------------------------------------------------- +# After baz.1 manually triggered (prereqs should remain unset). +TEST_NAME=$TEST_NAME_BASE-show-trigger +cmp_ok "${SHARE}/trigger.out" <<__SHOW_OUTPUT__ +title: (not given) +description: (not given) +URL: (not given) + +prerequisites (- => not satisfied): + - ((1 | 0) & (3 | 2) & (5 | 4) & (7 | 6)) + - 0 = bar_x1.1 failed + - 1 = bar_x1.1 succeeded + - 2 = bar_x2.1 failed + - 3 = bar_x2.1 succeeded + - 4 = bar_x3.1 failed + - 5 = bar_x3.1 succeeded + - 6 = bar_x4.1 failed + - 7 = bar_x4.1 succeeded + - (0 | 1 | 2 | 3) + - 0 = foo_x1.1 succeeded + - 1 = foo_x2.1 succeeded + - 2 = foo_x3.1 succeeded + - 3 = foo_x4.1 succeeded + +outputs (- => not completed): + - baz.1 expired + + baz.1 submitted + - baz.1 submit-failed + + baz.1 started + - baz.1 succeeded + - baz.1 failed +__SHOW_OUTPUT__ + +purge_suite $SUITE_NAME diff --git a/tests/cylc-show/prereqs-outputs/suite.rc b/tests/cylc-show/prereqs-outputs/suite.rc new file mode 100644 index 00000000000..853b37606f6 --- /dev/null +++ b/tests/cylc-show/prereqs-outputs/suite.rc @@ -0,0 +1,59 @@ +[meta] + title = "Capture baz prereqs and outputs after various state changes." +[cylc] + [[events]] + timeout = PT1M + abort on timeout = True + [[parameters]] + m = succeeded, expired, failed, submit-failed, waiting + x = 1..4 + [[parameter templates]] + m = %(m)s +[scheduling] + [[dependencies]] + graph = """FOO:succeed-any & BAR:finish-all => baz & baz2 + baz => + => + => trigger""" +[runtime] + [[FOO, BAR]] + [[RESET]] + pre-script = """ + if [[ "${CYLC_TASK_NAME}" == "waiting" ]]; then + # Remove upstream outputs so it's clear that prereqs get unset. + cylc remove ${CYLC_SUITE_NAME} FOO + cylc remove ${CYLC_SUITE_NAME} BAR + sleep 5 + fi + NEW_STATE=${CYLC_TASK_NAME} + CUR_STATE=$(cylc dump --tasks ${CYLC_SUITE_NAME} \ + | grep 'baz, 1' | awk '{print $3}' | sed -e 's/,//') + if [[ "${NEW_STATE}" != "${CUR_STATE}" ]]; then + cylc reset --state=${NEW_STATE} ${CYLC_SUITE_NAME} baz.1 + fi + sleep 5""" + [[SHOW]] + post-script = """ + sleep 5 + OUT=${CYLC_SUITE_SHARE_DIR}/${CYLC_TASK_NAME}.out + cylc show ${CYLC_SUITE_NAME} baz.1 > ${OUT}""" + [[foo]] + inherit = FOO + script = false + [[foo]] + script = true + [[bar]] + inherit = BAR + script = false + [[bar]] + inherit = BAR, SHOW + script = true + [[baz]] + script = sleep 10 + [[baz2]] + inherit = None, SHOW + [[]] + inherit = None, RESET, SHOW + [[trigger]] + inherit = None, SHOW + script = cylc trigger ${CYLC_SUITE_NAME} baz.1; sleep 5 diff --git a/tests/job-submission/04-submit-num/suite.rc b/tests/job-submission/04-submit-num/suite.rc index c5696122336..1c30fbad8c8 100644 --- a/tests/job-submission/04-submit-num/suite.rc +++ b/tests/job-submission/04-submit-num/suite.rc @@ -20,7 +20,7 @@ test "${CYLC_TASK_SUBMIT_NUMBER}" -gt "${CYLC_TASK_TRY_NUMBER}" execution retry delays=2*PT0S [[bar]] script = """ -cylc reset -f -s ready "${CYLC_SUITE_NAME}" foo 1 +cylc trigger "${CYLC_SUITE_NAME}" foo 1 """ [[baz]] script = """ diff --git a/tests/pre-initial/warm-insert/suite.rc b/tests/pre-initial/warm-insert/suite.rc index 202be762ae4..0fdab9dfb22 100644 --- a/tests/pre-initial/warm-insert/suite.rc +++ b/tests/pre-initial/warm-insert/suite.rc @@ -1,8 +1,8 @@ [cylc] UTC mode = true [[event hooks]] - # Reset the pre-start foo inserted by the inserter task. - stalled handler = cylc reset %(suite)s foo.20100101T1200Z -s ready + # Trigger the pre-start foo inserted by the inserter task. + stalled handler = cylc trigger %(suite)s foo.20100101T1200Z abort on timeout = True timeout = PT2M [[reference test]] diff --git a/tests/registration/00-simple.t b/tests/registration/00-simple.t index dcc6ffc0a19..17d9414d2b6 100755 --- a/tests/registration/00-simple.t +++ b/tests/registration/00-simple.t @@ -16,6 +16,8 @@ # along with this program. If not, see . #------------------------------------------------------------------------------- # Test cylc suite registration +# WARNING: bad directories under ~/cylc-run can screw this test. + . "$(dirname "$0")/test_header" set_test_number 7 @@ -43,6 +45,7 @@ run_ok "${TEST_NAME_BASE}-print" cylc print contains_ok "${TEST_NAME_BASE}-print.stdout" <<__OUT__ ${SUITE_NAME} | the quick brown fox | ${TEST_DIR}/${SUITE_NAME} __OUT__ + cmp_ok "${TEST_NAME_BASE}-print.stderr" <'/dev/null' purge_suite "${SUITE_NAME}" diff --git a/tests/registration/01-no-skip1.t b/tests/registration/01-no-skip1.t index 97781255f0c..d969f2fbc36 100755 --- a/tests/registration/01-no-skip1.t +++ b/tests/registration/01-no-skip1.t @@ -16,6 +16,8 @@ # along with this program. If not, see . #------------------------------------------------------------------------------- # Test cylc print doesn't skip special names at root level, +# WARNING: bad directories under ~/cylc-run can screw this test. + # e.g. "~/cylc-run/work" . "$(dirname "$0")/test_header" set_test_number 3 From 1031d02a6b9ccf308f74211621f4de08e4f9da0c Mon Sep 17 00:00:00 2001 From: Hilary James Oliver Date: Fri, 16 Mar 2018 09:51:34 +1300 Subject: [PATCH 2/5] Forced reset arg not needed. --- lib/cylc/task_pool.py | 4 ++-- lib/cylc/task_state.py | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/cylc/task_pool.py b/lib/cylc/task_pool.py index 2a8128ec5dd..0ab9905c093 100644 --- a/lib/cylc/task_pool.py +++ b/lib/cylc/task_pool.py @@ -1044,7 +1044,7 @@ def reset_task_states(self, items, status, outputs): for itask in itasks: if status and status != itask.state.status: LOG.info("resetting state to %s" % status, itask=itask) - itask.state.reset_state(status, forced=True) + itask.state.reset_state(status) if status in [TASK_STATUS_FAILED, TASK_STATUS_SUBMIT_FAILED]: # TODO - HUH? SUBMIT_FAILED? WHAT ABOUT SUCCEEDED? @@ -1110,7 +1110,7 @@ def trigger_tasks(self, items, back_out=False): continue itask.manual_trigger = True if not itask.state.status == TASK_STATUS_QUEUED: - itask.state.reset_state(TASK_STATUS_READY, forced=True) + itask.state.reset_state(TASK_STATUS_READY) return n_warnings def check_auto_shutdown(self): diff --git a/lib/cylc/task_state.py b/lib/cylc/task_state.py index 8a559e5eead..f0b12f734df 100644 --- a/lib/cylc/task_state.py +++ b/lib/cylc/task_state.py @@ -318,7 +318,7 @@ def unset_held(self): else: self.reset_state(self.hold_swap) - def reset_state(self, status, forced=False): + def reset_state(self, status): """Change status, and manipulate outputs and prerequisites accordingly. Outputs are manipulated on manual state reset to reflect the new task @@ -329,6 +329,9 @@ def reset_state(self, status, forced=False): manipulated, except to unset them on reset to waiting or earlier. (TODO - we should not do this - see GitHub #2329). + Note this method could take an additional argument to distinguish + internal and manually forced state changes, if needed. + The held state is handled in set/unset_held() for swap-state handling. """ @@ -350,8 +353,8 @@ def reset_state(self, status, forced=False): self.outputs.set_completion( TASK_OUTPUT_FAILED, status == TASK_STATUS_FAILED) - # Set prerequisites on forced reset to waiting (see docstring). - if forced and status == TASK_STATUS_WAITING: + # Unset prerequisites on reset to waiting (see docstring). + if status == TASK_STATUS_WAITING: self.set_prerequisites_not_satisfied() return self._set_state(status) From 3ecfacf0ff163f5440fbd7d5fdba7ef42b7244db Mon Sep 17 00:00:00 2001 From: Hilary James Oliver Date: Fri, 16 Mar 2018 09:51:46 +1300 Subject: [PATCH 3/5] Fixed tests. --- tests/cylc-reset/03-output-2.t | 3 +++ tests/cylc-show/00-simple.t | 15 +++++++++++++++ tests/cylc-show/05-complex.t | 3 +++ 3 files changed, 21 insertions(+) diff --git a/tests/cylc-reset/03-output-2.t b/tests/cylc-reset/03-output-2.t index 04eac2fdbd4..b1da1b3f34a 100755 --- a/tests/cylc-reset/03-output-2.t +++ b/tests/cylc-reset/03-output-2.t @@ -32,9 +32,12 @@ prerequisites (- => not satisfied): (None) outputs (- => not completed): + - t1.1 expired + t1.1 submitted + - t1.1 submit-failed + t1.1 started + t1.1 succeeded + - t1.1 failed - t1.1 Greet World - t1.1 Hello World __OUT__ diff --git a/tests/cylc-show/00-simple.t b/tests/cylc-show/00-simple.t index cacbfcb26a8..bd91032c0bc 100644 --- a/tests/cylc-show/00-simple.t +++ b/tests/cylc-show/00-simple.t @@ -58,9 +58,12 @@ prerequisites (- => not satisfied): + bar.20141106T0900Z succeeded outputs (- => not completed): + - foo.20141106T0900Z expired + foo.20141106T0900Z submitted + - foo.20141106T0900Z submit-failed + foo.20141106T0900Z started - foo.20141106T0900Z succeeded + - foo.20141106T0900Z failed __SHOW_OUTPUT__ #------------------------------------------------------------------------------- TEST_NAME=$TEST_NAME_BASE-show-json @@ -101,10 +104,18 @@ cmp_json_ok "$TEST_NAME-taskinstance" "$TEST_NAME-taskinstance" \ ] ], "outputs": [ + [ + "foo.20141106T0900Z expired", + false + ], [ "foo.20141106T0900Z submitted", true ], + [ + "foo.20141106T0900Z submit-failed", + false + ], [ "foo.20141106T0900Z started", true @@ -112,6 +123,10 @@ cmp_json_ok "$TEST_NAME-taskinstance" "$TEST_NAME-taskinstance" \ [ "foo.20141106T0900Z succeeded", false + ], + [ + "foo.20141106T0900Z failed", + false ] ], "meta": { diff --git a/tests/cylc-show/05-complex.t b/tests/cylc-show/05-complex.t index b29e1ff5371..0df3ed74ed8 100644 --- a/tests/cylc-show/05-complex.t +++ b/tests/cylc-show/05-complex.t @@ -45,9 +45,12 @@ prerequisites (- => not satisfied): - 5 = f.20000101T0000Z succeeded outputs (- => not completed): + - f.20000102T0000Z expired - f.20000102T0000Z submitted + - f.20000102T0000Z submit-failed - f.20000102T0000Z started - f.20000102T0000Z succeeded + - f.20000102T0000Z failed __OUT__ #------------------------------------------------------------------------------- TEST_NAME="${TEST_NAME_BASE}-short" From 35bdbe50ca9d3c2ae0ee2147116ab19a2e4f0e34 Mon Sep 17 00:00:00 2001 From: Hilary James Oliver Date: Tue, 20 Mar 2018 13:07:34 +1300 Subject: [PATCH 4/5] Clarify summary time updates. --- lib/cylc/task_events_mgr.py | 19 ++++++++++--------- lib/cylc/task_pool.py | 12 +++++------- lib/cylc/task_proxy.py | 4 ++-- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/cylc/task_events_mgr.py b/lib/cylc/task_events_mgr.py index 3dbfbe82739..b602f11dadf 100644 --- a/lib/cylc/task_events_mgr.py +++ b/lib/cylc/task_events_mgr.py @@ -350,7 +350,7 @@ def process_message(self, itask, severity, message, poll_func, elif message.startswith(TaskMessage.VACATION_MESSAGE_PREFIX): # Task job pre-empted into a vacation state self._db_events_insert(itask, "vacated", message) - itask.set_event_time('started') # reset + itask.set_summary_time('started') # unset if TASK_STATUS_SUBMIT_RETRYING in itask.try_timers: itask.try_timers[TASK_STATUS_SUBMIT_RETRYING].num = 0 itask.job_vacated = True @@ -596,7 +596,7 @@ def _process_message_failed(self, itask, event_time, message): """Helper for process_message, handle a failed message.""" if event_time is None: event_time = get_current_time_string() - itask.set_event_time('finished', event_time) + itask.set_summary_time('finished', event_time) self.suite_db_mgr.put_update_task_jobs(itask, { "run_status": 1, "time_run_exit": event_time, @@ -629,7 +629,7 @@ def _process_message_started(self, itask, event_time): LOG.warning("Vacated job restarted", itask=itask) self.pflag = True itask.state.reset_state(TASK_STATUS_RUNNING) - itask.set_event_time('started', event_time) + itask.set_summary_time('started', event_time) self.suite_db_mgr.put_update_task_jobs(itask, { "time_run": itask.summary['started_time_string']}) if itask.summary['execution_time_limit']: @@ -652,7 +652,7 @@ def _process_message_started(self, itask, event_time): def _process_message_succeeded(self, itask, event_time): """Helper for process_message, handle a succeeded message.""" self.pflag = True - itask.set_event_time('finished', event_time) + itask.set_summary_time('finished', event_time) self.suite_db_mgr.put_update_task_jobs(itask, { "run_status": 0, "time_run_exit": event_time, @@ -688,7 +688,6 @@ def _process_message_submit_failed(self, itask, event_time): if (TASK_STATUS_SUBMIT_RETRYING not in itask.try_timers or itask.try_timers[TASK_STATUS_SUBMIT_RETRYING].next() is None): # No submission retry lined up: definitive failure. - itask.set_event_time('finished', event_time) self.pflag = True # See github #476. self.setup_event_handlers( @@ -726,14 +725,16 @@ def _process_message_submitted(self, itask, event_time): if itask.tdef.run_mode == 'simulation': # Simulate job execution at this point. - itask.set_event_time('started', event_time) + itask.set_summary_time('submitted', event_time) + itask.set_summary_time('started', event_time) itask.state.reset_state(TASK_STATUS_RUNNING) itask.state.outputs.set_completion(TASK_OUTPUT_STARTED, True) return - itask.set_event_time('submitted', event_time) - itask.set_event_time('started') - itask.set_event_time('finished') + itask.set_summary_time('submitted', event_time) + # Unset started and finished times in case of resubmission. + itask.set_summary_time('started') + itask.set_summary_time('finished') itask.summary['latest_message'] = TASK_OUTPUT_SUBMITTED self.setup_event_handlers( itask, TASK_OUTPUT_SUBMITTED, 'job submitted') diff --git a/lib/cylc/task_pool.py b/lib/cylc/task_pool.py index 0ab9905c093..18ddb187aa6 100644 --- a/lib/cylc/task_pool.py +++ b/lib/cylc/task_pool.py @@ -375,9 +375,9 @@ def load_db_task_pool_for_restart(self, row_idx, row): itask.task_owner = None itask.task_host = user_at_host if time_submit: - itask.set_event_time('submitted', time_submit) + itask.set_summary_time('submitted', time_submit) if time_run: - itask.set_event_time('started', time_run) + itask.set_summary_time('started', time_run) if timeout is not None: itask.timeout_timers[status] = timeout @@ -1045,11 +1045,9 @@ def reset_task_states(self, items, status, outputs): if status and status != itask.state.status: LOG.info("resetting state to %s" % status, itask=itask) itask.state.reset_state(status) - if status in [TASK_STATUS_FAILED, - TASK_STATUS_SUBMIT_FAILED]: - # TODO - HUH? SUBMIT_FAILED? WHAT ABOUT SUCCEEDED? - itask.set_event_time('finished', - get_current_time_string()) + if status in [TASK_STATUS_FAILED, TASK_STATUS_SUCCEEDED]: + itask.set_summary_time('finished', + get_current_time_string()) if outputs: for output in outputs: is_completed = True diff --git a/lib/cylc/task_proxy.py b/lib/cylc/task_proxy.py index ab15d5874dc..1fab6fac80e 100644 --- a/lib/cylc/task_proxy.py +++ b/lib/cylc/task_proxy.py @@ -229,8 +229,8 @@ def reset_manual_trigger(self): for timer in self.try_timers.values(): timer.timeout = None - def set_event_time(self, event_key, time_str=None): - """Set event time in self.summary + def set_summary_time(self, event_key, time_str=None): + """Set an event time in self.summary Set values of both event_key + "_time" and event_key + "_time_string". """ From fe9facdaa9032eb2b3fbb0ca450fd8ba6d309256 Mon Sep 17 00:00:00 2001 From: Sadie Bartholomew Date: Fri, 13 Apr 2018 17:55:47 +0100 Subject: [PATCH 5/5] Replace 'registration' test warnings with logic to bypass issue --- tests/registration/00-simple.t | 12 ++++++++++-- tests/registration/01-no-skip1.t | 13 +++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/registration/00-simple.t b/tests/registration/00-simple.t index 17d9414d2b6..30c106dc849 100755 --- a/tests/registration/00-simple.t +++ b/tests/registration/00-simple.t @@ -16,7 +16,6 @@ # along with this program. If not, see . #------------------------------------------------------------------------------- # Test cylc suite registration -# WARNING: bad directories under ~/cylc-run can screw this test. . "$(dirname "$0")/test_header" set_test_number 7 @@ -46,7 +45,16 @@ contains_ok "${TEST_NAME_BASE}-print.stdout" <<__OUT__ ${SUITE_NAME} | the quick brown fox | ${TEST_DIR}/${SUITE_NAME} __OUT__ -cmp_ok "${TEST_NAME_BASE}-print.stderr" <'/dev/null' +# Filter out errors from 'bad' suites in the 'cylc-run' directory +NONSPECIFIC_ERR2='\[Errno 2\] No such file or directory:' +SPECIFIC_ERR2="$NONSPECIFIC_ERR2 '$HOME/cylc-run/$SUITE_NAME/suite.rc'" +ERR2_COUNT=$(grep -c "$SPECIFIC_ERR2" "${TEST_NAME_BASE}-print.stderr") +if [ "$ERR2_COUNT" -eq "0" ]; then + grep -v -s "$NONSPECIFIC_ERR2" "${TEST_NAME_BASE}-print.stderr" > "${TEST_NAME_BASE}-print-filtered.stderr" + cmp_ok "${TEST_NAME_BASE}-print-filtered.stderr" <'/dev/null' +else + fail "${TEST_NAME_BASE}-print.stderr" +fi purge_suite "${SUITE_NAME}" exit diff --git a/tests/registration/01-no-skip1.t b/tests/registration/01-no-skip1.t index d969f2fbc36..d1e97bc109a 100755 --- a/tests/registration/01-no-skip1.t +++ b/tests/registration/01-no-skip1.t @@ -16,7 +16,6 @@ # along with this program. If not, see . #------------------------------------------------------------------------------- # Test cylc print doesn't skip special names at root level, -# WARNING: bad directories under ~/cylc-run can screw this test. # e.g. "~/cylc-run/work" . "$(dirname "$0")/test_header" @@ -39,7 +38,17 @@ run_ok "${TEST_NAME_BASE}-print" cylc print contains_ok "${TEST_NAME_BASE}-print.stdout" <<__OUT__ work | the quick brown fox | ${TEST_DIR}/${SUITE_NAME} __OUT__ -cmp_ok "${TEST_NAME_BASE}-print.stderr" <'/dev/null' + +# Filter out errors from 'bad' suites in the 'cylc-run' directory +NONSPECIFIC_ERR2='\[Errno 2\] No such file or directory:' +SPECIFIC_ERR2="$NONSPECIFIC_ERR2 '$HOME/cylc-run/$SUITE_NAME/suite.rc'" +ERR2_COUNT=$(grep -c "$SPECIFIC_ERR2" "${TEST_NAME_BASE}-print.stderr") +if [ "$ERR2_COUNT" -eq "0" ]; then + grep -v -s "$NONSPECIFIC_ERR2" "${TEST_NAME_BASE}-print.stderr" > "${TEST_NAME_BASE}-print-filtered.stderr" + cmp_ok "${TEST_NAME_BASE}-print-filtered.stderr" <'/dev/null' +else + fail "${TEST_NAME_BASE}-print.stderr" +fi rm -f "${RUND}/work" purge_suite "${SUITE_NAME}"