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

[P4Testgen] Fix behavior of coverage on edge cases with no nodes to cover #4275

Merged
merged 11 commits into from
Dec 17, 2023

Conversation

vlstill
Copy link
Contributor

@vlstill vlstill commented Dec 5, 2023

This PR changes behavior of testgen on various edge cases with regard to node coverage:

  • If there are no coverable nodes, then set coverage to 100 %.
    • This could be a bit controversial, but I'd say it makes sense to have an empty program be considered fully covered. It does get a bit more tricky if --track-coverage TABLE_ENTRIES is enabled (without STATEMENTS) and there are no tables.
  • the --assert-min-coverage can no longer be enabled unless --track-coverage is enabled as it does not make sense without anything to cover.
  • The test "banner" is now different for the case the coverage is disabled and for the case coverage is enabled and there are no coverable nodes:
    • ============ Test 0 ============ (coverage disabled)
    • ============ Test 0: No coverable nodes ============


/// @returns true if and only if at least one of the coverage-collection options (cover*) is
/// enabled
bool coverageEnabled() const { return coverStatements || coverTableEntries; }
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've opted to add this getter to make it more likely this will be properly updated when new coverage types are added.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead, I would add an option to CoverageOptions that sets this value true, whenever one of the values is set.

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 am not sure I understand what you mean. Do you suggest to have a derived boolean argument in the struct? That would seem like needless duplication of information at risk of inconsistency to me.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, set the value here: https://github.com/p4lang/p4c/blob/main/backends/p4tools/modules/testgen/options.cpp#L256

You can easily precompute the boolean value here. It will also automatically pick up any new coverage option that might be added. This way you do not need to hard-code all the possible coverage options in the helper.

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 don't like that all that much. The CoverageOptions struct is not strictly bound to be filled by options only, it is not even defined in the headers that process options. Therefore, having a derived attribute adds an invariant to the class that needs to hold when the class values are modified/set. Having the getter needs an invariant too (that it ORs all the cover* values), but this one is at least localized to the class itself.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You could also add this flag to the testgen options class instead of the options structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, let's do it that way. Although I still think having the member function did make sense.

@vlstill vlstill marked this pull request as ready for review December 5, 2023 08:12
@vlstill vlstill requested a review from fruffy December 5, 2023 08:12
@vlstill vlstill changed the title [P4Testgen] Fix behaviour of coverage on edge cases with no nodes to cover [P4Testgen] Fix behavior of coverage on edge cases with no nodes to cover Dec 5, 2023
@@ -115,6 +115,14 @@ int Testgen::mainImpl(const IR::P4Program *program) {
if (seed) {
printFeature("test_info", 4, "============ Program seed %1% =============\n", *seed);
}
if (testgenOptions.minCoverage > 0 && !testgenOptions.coverageOptions.coverageEnabled()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is something I would catch in the options early, if possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seemed quite early to me, but I've now moved it even earlier just after option processing.

Copy link
Collaborator

@fruffy fruffy Dec 5, 2023

Choose a reason for hiding this comment

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

Options should be processed as early as possible to get immediate feedback. Otherwise, you run your tests for a couple of minutes then get this error.

We typically validate options already for each lambda we add. For example, here you could throw an error if you want to enable minCoverage without previously enable coverage in some way. This way you also do not require an extra validate call. This requires ordering, but that seems fine to me. This is a limitation of the way our options work.

I am also okay with adding validate, but not sure how much usage that function call will see.

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 don't like requiring one to be set before another, that is why I did not want to put the validation into the lambdas.

backends/p4tools/modules/testgen/lib/test_backend.cpp Outdated Show resolved Hide resolved

/// @returns true if and only if at least one of the coverage-collection options (cover*) is
/// enabled
bool coverageEnabled() const { return coverStatements || coverTableEntries; }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead, I would add an option to CoverageOptions that sets this value true, whenever one of the values is set.

@fruffy fruffy added the p4tools Topics related to the P4Tools back end label Dec 6, 2023
@vlstill vlstill requested a review from fruffy December 7, 2023 13:25
"It is not allowed to have --assert-min-coverage set to non-zero without a coverage "
"tracking enabled with --track-coverage option. Without coverage tracking, the "
"--assert-min-coverage is meaningless.");
FATAL_ERROR("Incompatible options given.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

I approved but I do not think this is necessary. We will exist if we encounter an error during processing. If not, that is a bug.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems like the check is not early enough and before the compiler context is set it is not possible to check error count either.

tests/empty_1/p4/main.p4:19: fatal error: when writing output to : Broken pipe
   19 |
      |
compilation terminated.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm that is a little odd, we have been using ::error just fine in the options classes and the compiler context is set up before the validate call.

The entire way options are handled in P4Testgen is a little funky (there are a bunch of hacks to set up context). So there might be something awry there.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe the exception thrown with FATAL_ERROR causes the problem? We have not tested any exception handling that early.

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 think the handler for errors in the option parsing works differently, it also prints the usage if the argument parsing fails. What I was testing (and what produced this weird error) was just having the error there and no FATAL_ERROR, so there were no exceptions thrown.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Did this get resolved?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unless we change error handling during process, the FATAL_ERROR is the best bet to cleanly end the processing (in the compiler, there are only warnings, so this does not matter).

Copy link
Collaborator

Choose a reason for hiding this comment

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

I looked into this, basically we need to do this

if (::errorCount() > 0) {
    return std::nullopt;
}

after the validate call to not return a broken compile context. We could also return true/false in validate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made it return. ::errorCount() is not available before the context is set (it could be extracted from the context, but this seems cleaner to me).

@vlstill
Copy link
Contributor Author

vlstill commented Dec 11, 2023

Rebased + using the same name as is used in the ParserOptions (predecessor of CompilerOptions) for this purpose (it is not a base class, so the defintions are independent).

@vlstill vlstill merged commit 2d7550a into p4lang:main Dec 17, 2023
13 checks passed
@vlstill vlstill deleted the testgen-cov-empty branch December 17, 2023 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p4tools Topics related to the P4Tools back end
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants