Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fail steps/tests/suites when expressions throw exceptions #15

Merged
merged 9 commits into from
Oct 8, 2024

Conversation

jonsmock
Copy link
Collaborator

@jonsmock jonsmock commented Sep 24, 2024

This PR conceptually adds try/catch around expressions, failing the step/test/suite if an expression throws an exception. Also, it fixes a bug, where if an expect condition fails, the run is not retried even if repeat is set correctly.

Before we add catching of expression exceptions, there is a pretty substantial refactoring to move the run-(step|test|suite) functions to become "pipelines" using -> and while->. while-> is a custom macro that takes a predicate as its first argument, then threads the initial value through functions as long as the previous initial or returned value matches the predicate. This is used as an "early exit" mechanism, as we interpolate various parts of steps/tests/suites.

Misc:

  • Dockerfile cleanup
  • Verifying skipped test count in GHA when omitting --continue-on-error
  • Interpolating step/test/suite names

docker-compose no longer supported in GHA
The 'expect' assertions are conceptually part of the 'run' success; if
'expect' assertions fail, the 'run' should be retried.

We do not need to re-evaluate the 'env' and other keys that are
evaluated before the 'run' executes; however, we do need to re-execute
the 'run' and re-evaluate the 'expect' assertions.
src/dctest/core.cljs Outdated Show resolved Hide resolved
src/dctest/core.cljs Outdated Show resolved Hide resolved
src/dctest/core.cljs Outdated Show resolved Hide resolved
src/dctest/core.cljs Outdated Show resolved Hide resolved
src/dctest/expressions.cljs Outdated Show resolved Hide resolved
src/dctest/expressions.cljs Outdated Show resolved Hide resolved
@jonsmock jonsmock force-pushed the fail-on-expression-exceptions branch 2 times, most recently from fe2b3d0 to d09caaf Compare September 30, 2024 15:42
These test/step execution functions were returning the context for two
reasons:

* they modified the context's :state :failed boolean (which somewhat
  conflates the overall run state and the individual test state)
* they anticipated supporting test and step outputs in the future

However, the :state :failed boolean would then have to be reset to false
in 'run-test, if the user was using --continue-on-error. Additionally,
returning the context meant we had to restore the previous :env.

Instead, it's much simpler to treat the context as immutable! If we need
to support outputs in the future, they should be returned as part of the
test/step result, and the higher level function can determine how to add
those outputs to a newly created context for the next test/step
execution.
Extracts 'run-expectations, 'execute-step-retries, 'run-tests, and
'run-suites. The first three of these return a step, step, and suite
respectively, so that the bodies of the 'run-test and 'run-suite
functions can use them with clojure.core/update.

Reorganize logging a bit.
@jonsmock jonsmock force-pushed the fail-on-expression-exceptions branch from d09caaf to 18f8e85 Compare October 4, 2024 15:02
Expressions in suite/test/step can throw, in which case we want to stop
evaluating and fail the suite/test/step (ideally preserving as much
information as possible, such as the interpolated :name). This change
adds a pending-> macro to handle the common pattern of:

1. check if the suite/test/step is skipped or failed (if so, short
   circuit)
2. otherwise, attempt to do the next thing (ex: evaluate an env, run a
   test's steps, etc)
3. if an unexpected exception is thrown, catch it, and mark the
   suite/test/step as :fail with an error

Helpers pass!/skip!/fail! are added to manipulate the suite/test/step
outcome consistently and tersely.

The pending-> macro has to be extracted into its own namespace and
duplicated in both clj and cljs files to support ShadowCLJS and nbb.
Now that suites and tests can throw exceptions, we switch to recording
the error on the suite, test, or step as appropriate (previously the
step errors were bubbled up and also assoc'd to the test).

To support this, we summarize errors at all levels in our summary before
displaying the list to the user. As an improvement, we also show where
the error came from (previously the user had to infer which step caused
the failure).
@jonsmock jonsmock force-pushed the fail-on-expression-exceptions branch from 18f8e85 to 2a8c697 Compare October 4, 2024 16:11
@jonsmock jonsmock requested a review from gdw2vs October 4, 2024 16:13
@jonsmock
Copy link
Collaborator Author

jonsmock commented Oct 4, 2024

Main change from original description:

while-> is discarded in favor of pending-> which acts like ->, implicitly try/catchs each form, and only proceeds to evaluate the next form if the initial/resulting expression is pending?. This allows us to take a suite/test/step and evaluate/interpolate different parts of them until an error occurs, at which point we short-circuit the evaluation and return a failure (retaining any parts we did evaluate sucessfully, such as the :name)

Copy link
Collaborator

@kanaka kanaka left a comment

Choose a reason for hiding this comment

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

We could use more docstrings and inline comments. It's pretty dense code with a reasonable level of cyclomatic complexity.

I've marked it approved because I think the doc improvement is general rather than something specific to this change. There is some great information in the commits that should become inline comments/docstrings.

{:message (str "Exception thrown: " (.-message ~err))}))))
forms)]
`(promesa.core/let [~sts ~suite-test-or-step
~@(interleave (repeat sts) (butlast forms))]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice. I understand now what this is doing, but it took some pondering. A comment would be worthwhile.

(P/let [step (pending-> step
(->> (skip-if-necessary context))
(update :env #(interpolate-any context %)))
context (update context :env merge
Copy link
Collaborator

Choose a reason for hiding this comment

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

The way the context manipulation interrupts the pending-> flow makes me wonder if this points to some higher abstraction. Just a thought.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Believe me, I've been thinking about that nonstop. If you have even a sketch of a better idea, I'd be willing to try to simplify this more. This was something like my 4th or 5th attempt. I think my ideal would be something where the env doesn't need to be copied into the context, but rather, simply by interpolating it in the suite/test/step, it now participates in future interpolating ... but I couldn't figure out how to make that work that wasn't more complicated than this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, if I come up with something I'll let you know ;-). Maybe after some hammock time.

@@ -363,14 +408,17 @@ Options:
(* 1000))))

(defn normalize [suite path]
Copy link
Collaborator

Choose a reason for hiding this comment

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

A brief docstring summarizing what sort of normalizing we are doing would be good.

@jonsmock
Copy link
Collaborator Author

jonsmock commented Oct 8, 2024

Thanks, everyone, for the very helpful reviews and feedback. I'll definitely address the docstring/comments in a separate PR.

@jonsmock jonsmock merged commit 797bf33 into main Oct 8, 2024
4 checks passed
@jonsmock jonsmock deleted the fail-on-expression-exceptions branch October 8, 2024 13:51
@jonsmock jonsmock mentioned this pull request Oct 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants