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

RUST-670 Expect unified test format operations to succeed #388

Merged
merged 3 commits into from
Jul 8, 2021

Conversation

isabelatkinson
Copy link
Contributor

@isabelatkinson isabelatkinson commented Jul 7, 2021

Finished up this GBF ticket as I had a small amount of work left and will be out next Friday.

This PR adds validation that the valid_fail tests in the Unified Test Format specification fail upon error.

#[serde(deserialize_with = "crate::bson_util::deserialize_u64_from_bson_number")]
#[serde(
deserialize_with = "crate::bson_util::deserialize_u64_from_bson_number",
serialize_with = "crate::bson::serde_helpers::serialize_u64_as_i64"
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 addition was necessary because the results from test operations are all serialized into Bson.

"Running tests from {}",
test_file_full_path.display().to_string()
);
pub(crate) async fn run_single_test<T, F, G>(path: PathBuf, run_test_file: &F)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The valid_fail tests need to be able to call this method directly to ensure that each test file in the directory fails; just calling run_spec_test will panic on the first test file and return.

)
})
.unwrap_or_else(|| {
if let Some(expect_error) = operation.expect_error {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only change here is refactoring the conditionals to unwrap the result if expect_error is not present rather than just when expect_result is present.

let path = path.join(&test_file_path);
let path_display = path.display().to_string();

std::panic::AssertUnwindSafe(run_single_test(path, &run_unified_format_test))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

My original plan for this work was to return errors directly from the unified test runner code, but given the amount of panics and unwraps in our tests this led to excessive verbosity. Instead I opted for catch_unwind, which catches panics and converts them into a Result that can be examined. AssertUnwindSafe is essentially a wrapper that suppresses a compiler error that would otherwise appear indicating that the future returned from run_single_test cannot be unwound safely. I'd be hesitant to use this in driver code given that it bypasses some compiler safety, but because this is just test code this feels like the best solution that doesn't over-complicate the test runner itself.

@@ -246,7 +246,7 @@ impl Deref for Operation {
pub(super) struct DeleteMany {
filter: Document,
#[serde(flatten)]
options: Option<DeleteOptions>,
options: DeleteOptions,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The combination of #[serde(flatten)] and wrapping these fields in an Option was silencing certain deserialization errors.

@isabelatkinson isabelatkinson marked this pull request as ready for review July 7, 2021 22:00
Copy link
Contributor

@NBSquare NBSquare left a comment

Choose a reason for hiding this comment

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

lgtm! just want a bit of clarification surrounding some of the changes to serialization logic, the restructures to testing all seem good though.

@@ -509,7 +509,7 @@ pub struct CountOptions {
///
/// This options maps to the `maxTimeMS` MongoDB query option, so the duration will be sent
/// across the wire as an integer number of milliseconds.
#[serde(deserialize_with = "deserialize_duration_from_u64_millis")]
#[serde(default, deserialize_with = "deserialize_duration_from_u64_millis")]
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason these are the only fields on which you've applied the default attribute?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

default is needed when deserialize_with is being used to default the field to None if it isn't specified. I added default to these fields specifically because they weren't properly deserializing from the test files.

Copy link
Contributor

Choose a reason for hiding this comment

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

Makes sense. Does it make more sense to include default as a container attribute instead now that the options structs aren't Options? I guess every field is optional so it doesn't matter much either way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah it shouldn't matter, default is only really necessary when we're overriding the default deserialization behavior with deserialize_with because the method provided there doesn't handle Options.

@isabelatkinson isabelatkinson merged commit 695b115 into mongodb:master Jul 8, 2021
@isabelatkinson isabelatkinson deleted the sync-valid-fail branch July 8, 2021 17:29
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.

None yet

4 participants