Skip to content

Add argument validation to public APIs. Throw or Precondition/assert failure?#2189

Merged
ahsonkhan merged 16 commits intoAzure:masterfrom
ahsonkhan:ArgValidation
May 19, 2021
Merged

Add argument validation to public APIs. Throw or Precondition/assert failure?#2189
ahsonkhan merged 16 commits intoAzure:masterfrom
ahsonkhan:ArgValidation

Conversation

@ahsonkhan
Copy link
Copy Markdown
Contributor

@ahsonkhan ahsonkhan commented May 4, 2021

Fixes #1769

Currently, the PR shows one mechanism of validating arguments, which is to check and throw an exception.
Depending on which approach we take, new tests would be needed.

Depends on what we do in #2170

@ahsonkhan ahsonkhan added this to the [2021] May milestone May 4, 2021
@ahsonkhan ahsonkhan self-assigned this May 4, 2021
Copy link
Copy Markdown
Member

@antkmsft antkmsft left a comment

Choose a reason for hiding this comment

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

I like either exceptions or sometimes just going without validation is also fine, especially when do-noting/0-result and so on is possible, but sometimes when validation is expensive UB is fine. Not a fan of asserts/exits/terminations. But I my views are probably not mainstream, at least in our team.

Copy link
Copy Markdown
Member

@vhvb1989 vhvb1989 left a comment

Choose a reason for hiding this comment

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

The caller should fix the issue instead of adding code to recover for trying, for example, to read negative values.

@ahsonkhan ahsonkhan marked this pull request as ready for review May 7, 2021 21:50
@ahsonkhan ahsonkhan requested a review from RickWinter as a code owner May 7, 2021 21:50
@check-enforcer
Copy link
Copy Markdown

check-enforcer bot commented May 7, 2021

This pull request is protected by Check Enforcer.

What is Check Enforcer?

Check Enforcer helps ensure all pull requests are covered by at least one check-run (typically an Azure Pipeline). When all check-runs associated with this pull request pass then Check Enforcer itself will pass.

Why am I getting this message?

You are getting this message because Check Enforcer did not detect any check-runs being associated with this pull request within five minutes. This may indicate that your pull request is not covered by any pipelines and so Check Enforcer is correctly blocking the pull request being merged.

What should I do now?

If the check-enforcer check-run is not passing and all other check-runs associated with this PR are passing (excluding license-cla) then you could try telling Check Enforcer to evaluate your pull request again. You can do this by adding a comment to this pull request as follows:
/check-enforcer evaluate
Typically evaluation only takes a few seconds. If you know that your pull request is not covered by a pipeline and this is expected you can override Check Enforcer using the following command:
/check-enforcer override
Note that using the override command triggers alerts so that follow-up investigations can occur (PRs still need to be approved as normal).

What if I am onboarding a new service?

Often, new services do not have validation pipelines associated with them, in order to bootstrap pipelines for a new service, you can issue the following command as a pull request comment:
/azp run prepare-pipelines
This will run a pipeline that analyzes the source tree and creates the pipelines necessary to build and validate your pull request. Once the pipeline has been created you can trigger the pipeline using the following comment:
/azp run cpp - [service] - ci

Copy link
Copy Markdown
Member

@antkmsft antkmsft left a comment

Choose a reason for hiding this comment

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

LGTM!

Comment on lines +65 to +67
AZURE_ASSERT_MSG(
(data && length >= 0) || (!data && length == 0),
"Length cannot be negative, and length must be 0 if the data pointer is null.");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

length >= 0 is a tautology; std::size_t cannot represent negative values. Also, you've strengthened the condition here to forbid data != nullptr && length == 0 which seems like a perfectly valid empty range.

Suggested change
AZURE_ASSERT_MSG(
(data && length >= 0) || (!data && length == 0),
"Length cannot be negative, and length must be 0 if the data pointer is null.");
AZURE_ASSERT_MSG(
data != nullptr || length == 0,
"Length must be 0 if the data pointer is null.");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

length >= 0 is a tautology; std::size_t cannot represent negative values.

Agreed, already fixed. The change you were looking at is outdated.

Also, you've strengthened the condition here to forbid data != nullptr && length == 0 which seems like a perfectly valid empty range.

That was intentional, but let me revisit that to make sure it is what we want. Good call out :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Also, you've strengthened the condition here to forbid data != nullptr && length == 0 which seems like a perfectly valid empty range.

With the original assert ((data && length >= 0) || (!data && length == 0)), I don't think I had. Isn't that identical? That said, yours is simpler.


FileBodyStream::FileBodyStream(const std::string& filename)
{
AZURE_ASSERT_MSG(filename.size() > 0, "The file name must not be an empty string.");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It seems peculiar to me that this particular invalid file name is forbidden by precondition, whereas all other invalid file names result in a runtime error. I'd remove this precondition and let all invalid filenames be treated the same.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unlike other filename checks, the size of string is something the caller can trivially check. At least, that's what motivated this change. An empty string size is potentially an upstream string parsing bug, whereas invalid filename could be caused by file not existing or other reasons, so the type of root causes of the errors seem sufficiently different to me.

Comment on lines +37 to +43
#if defined(NDEBUG)
// Release build won't provide assert msg
ASSERT_DEATH(tb.Rewind(), "");
#else
ASSERT_DEATH(
tb.Rewind(),
"The specified BodyStream doesn't support Rewind which is required to guarantee fault ");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is begging for a new macro ASSERT_DEATH_MEOW(x, y) that expands to ASSERT_DEATH(x, "") when defined(NDEBUG) and to ASSERT_DEATH(x, y) otherwise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@vhvb1989 what do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

sure, it's a Macro just for tests. We can create an azure_assert_test.hpp with this macro and use it from cpp. It's a pretty suggestion :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Even a function would be good instead, as usually we don't like MACROS as per our guidelines :)

@ahsonkhan ahsonkhan merged commit 75d1755 into Azure:master May 19, 2021
@ahsonkhan ahsonkhan deleted the ArgValidation branch May 19, 2021 03:10
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.

The SDK should have some mechanism to validate arguments passed in to public APIs

5 participants