Skip to content

Fix test reliability issues with checks in GetObjectRoot#179

Merged
wilbaker merged 4 commits intomicrosoft:masterfrom
wilbaker:users/wilbaker/get_object_root_reliability_fix
Aug 16, 2018
Merged

Fix test reliability issues with checks in GetObjectRoot#179
wilbaker merged 4 commits intomicrosoft:masterfrom
wilbaker:users/wilbaker/get_object_root_reliability_fix

Conversation

@wilbaker
Copy link
Member

@wilbaker wilbaker commented Aug 16, 2018

Fix for issue #178

The problem with the current GetObjectRoot is the check there are only two items in the local cache root folder.

This was a valid check when the functional tests were not run in parallel, but now that they are run in parallel it's possible that in one test GVFS is reading mapping.dat (which requires creating\holding mapping.dat.lock) while another test is calling GetObjectRoot (which doesn't need to read mapping.dat but still wants to validate the integrity of the local cache root folder).

To make these checks more robust GetObjectRoot will now check for the specific files it expects (and does not expect) to be present.

Example of a test failing due to this issue:
18227.3

@wilbaker wilbaker added the type: test-reliability Issues that contribute to test failures label Aug 16, 2018
@wilbaker wilbaker self-assigned this Aug 16, 2018
Copy link
Contributor

@sanoursa sanoursa left a comment

Choose a reason for hiding this comment

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

My overall feedback is that this test was too hard to decipher. The logic all makes sense now, but it took a lot of work to figure out what the intent was.

Consider reworking it to something more like:

this.LocalCacheRoot.ShouldBeADirectory(fileSystem).WithFile("mapping.dat");
this.LocalCacheRoot.ShouldBeADirectory(fileSystem).WithNoFilesExcept("mapping.dat", "mapping.dat.lock");

FileInfo[] files = localCacheRootItems.Where(info => info is FileInfo).Cast<FileInfo>().ToArray();
files.Where(f => string.Equals(f.Name, "mapping.dat", StringComparison.OrdinalIgnoreCase))
.Count()
.ShouldEqual(1, "Local cache root should contain a single 'mapping.dat' file, actual files: " + string.Join<FileInfo>(",", files));
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: use a tab size of 4 spaces

.ShouldEqual(1, "Local cache root should contain a single 'mapping.dat' file, actual files: " + string.Join<FileInfo>(",", files));

IEnumerable<FileInfo> unexpectedFiles = files.Where(
f => !string.Equals(f.Name, "mapping.dat", StringComparison.OrdinalIgnoreCase) &&
Copy link
Contributor

Choose a reason for hiding this comment

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

The spacing here is a bit hard to parse. Maybe move the f => to the previous line, so the two parts of the condition can line up better?

FileInfo[] files = localCacheRootItems.Where(info => info is FileInfo).Cast<FileInfo>().ToArray();
files.Where(f => string.Equals(f.Name, "mapping.dat", StringComparison.OrdinalIgnoreCase))
.Count()
.ShouldEqual(1, "Local cache root should contain a single 'mapping.dat' file, actual files: " + string.Join<FileInfo>(",", files));
Copy link
Contributor

Choose a reason for hiding this comment

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

This count can only ever be 1 or 0 right? This looks like an indirect way to check fileSystem.FileExists(Path.Combine(this.LocalCacheRoot, "mapping.dat")).ShouldBeTrue(). Or we could stick to the other pattern with this.LocalCacheRoot.ShouldBeADirectory(fileSystem).WithFile("mapping.dat"). I think either of those would make the intent of the test more clear.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, I got caught up in how the check was working before. It's much cleaner to use the helpers we already have.

f => !string.Equals(f.Name, "mapping.dat", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(f.Name, "mapping.dat.lock", StringComparison.OrdinalIgnoreCase));

unexpectedFiles.Any().ShouldBeFalse("Local cache root contains unexpected files: " + string.Join(",", unexpectedFiles));
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand how this solves the test failure. The original code was failing because we found 3 items in the cache root instead of 2, but this code is still going to fail if there are more than the 2 expected items.

IEnumerable<FileSystemInfo> localCacheRootItems = this.LocalCacheRoot.ShouldBeADirectory(fileSystem).WithItems();
localCacheRootItems.Count().ShouldEqual(2, "Expected local cache root to contain 2 items. Actual items: " + string.Join(",", localCacheRootItems));

FileInfo[] files = localCacheRootItems.Where(info => info is FileInfo).Cast<FileInfo>().ToArray();
Copy link
Contributor

Choose a reason for hiding this comment

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

Ah I missed that you were filtering down to just files here. You could consider changing line 117 above to this.LocalCacheRoot.ShouldBeADirectory(fileSystem).WithFiles() and have that return just FileInfos to begin with.

.Count()
.ShouldEqual(1, "Local cache root should contain a single 'mapping.dat' file, actual files: " + string.Join<FileInfo>(",", files));

IEnumerable<FileInfo> unexpectedFiles = files.Where(
Copy link
Member

Choose a reason for hiding this comment

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

This looks fine to me, but you might consider creating an array (or maybe HashSet<string>, although it doesn't make a practical difference) of the list of expected file names up front, then you could simplify the LINQ statement to something like:

HashSet<string> expectedFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {"expected_file1", "expectedFile2"};
unexpectedFiles.Where(f => expectedFile.Contains(f.Name));

@wilbaker
Copy link
Member Author

@jamill @sanoursa thanks for the great feedback! With the latest push the PR is ready for another look.

{
T item = group.SingleOrDefault(predicate);
item.ShouldEqual(default(T));
item.ShouldEqual(default(T), "Unexpected matching entry found in {" + string.Join(",", group) + "}");
Copy link
Member

Choose a reason for hiding this comment

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

I think the body of this method could be simplified to:

group.Any(predicate).ShouldEqual(false, "Unexpected matching entry found in {" + string.Join(",", group) + "}");

Copy link
Member Author

Choose a reason for hiding this comment

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

I think I'll leave this as-is for now and keep the changes focused on fixing the functional test reliability issue (I do agree this is a nice potential cleanup, but would rather not use this PR for refactoring\cleaning the Should helpers)

@wilbaker wilbaker merged commit c838635 into microsoft:master Aug 16, 2018
@jrbriggs jrbriggs added this to the M139 milestone Aug 20, 2018
@jrbriggs
Copy link
Member

@wilbaker could we also port this back to milestones/M139?

@wilbaker
Copy link
Member Author

@jrbriggs sure I'll port this over

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: test-reliability Issues that contribute to test failures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants