Skip to content

Add TestFileUtils.deleteDirWithRetry method.#2946

Open
labkey-danield wants to merge 13 commits intorelease26.3-SNAPSHOTfrom
26.3_fb_moreDirDeleteOnWindowsFun
Open

Add TestFileUtils.deleteDirWithRetry method.#2946
labkey-danield wants to merge 13 commits intorelease26.3-SNAPSHOTfrom
26.3_fb_moreDirDeleteOnWindowsFun

Conversation

@labkey-danield
Copy link
Copy Markdown
Contributor

@labkey-danield labkey-danield commented Apr 10, 2026

Rationale

Revert AssayTransformMissingParentDirTest.testMissingParentDirectoryRegression to (mostly) its original state.
Move the delete code into TestFileUtils.deleteDirWithRetry, this might be useful to help debug failures in the future.

Related Pull Requests

Changes

  • Revert changes to the test.
  • Move the delete and retry code into TestFileUtils.

labkey-jeckels added a commit to LabKey/platform that referenced this pull request Apr 14, 2026
…#7578)

#### Rationale
We read transform scripts during assay design save. We open but don't
close the file. This is impolite, and Windows automated tests have been
noticing.


https://teamcity.labkey.org/buildConfiguration/LabKey_263Release_Premium_CommunitySqlserver_DailyCSqlserver/3929893?buildTab=tests&status=failed&expandedTest=build%3A%28id%3A3929893%29%2Cid%3A2000000048

#### Related Pull Requests
- LabKey/testAutomation#2946

#### Changes
- try-with-resources is the polite way to handle InputStreams

<!-- list of standard tasks (remove this comment to enable)
#### Tasks 📍
- [ ] Manual Testing
- [ ] Needs Automation
- [ ] Verify Fix
-->
…egression to what it was originally.

Move the delete and retry code in to TestFileUtils.deleteDirWithRetry
@labkey-danield labkey-danield changed the title Debug Folder Delete Failure on Windows Add TestFileUtils.deleteDirWithRetry method. Apr 15, 2026
Dump the heap if running on TeamCity.
Get the list of running processes if running on Linux as well.
pb.redirectErrorStream(true);

// Tried to use "try (Process p = pb.start()) {" without the finally block but our build
// system didn't like that and complained that Process doesn't implement closeable (it does).
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.

Process started implementing Closeable in Java 26. We're using Java 25.


FileUtils.deleteDirectory(dir);
LOG.info(String.format("Deletion of directory %s was successful.", dir));
break;
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.

Consider making this return. If you do that, you can eliminate the if (attempt == 10) statement below and move its body outside of the loop.

FileUtils.deleteDirectory(dir);
LOG.info(String.format("Deletion of directory %s was successful.", dir));
break;
} catch (IOException ioException) {
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.

File.walk().forEach() can throw UncheckedIOException. Probably good to catch that here too.

Copy link
Copy Markdown
Member

@labkey-tchad labkey-tchad left a comment

Choose a reason for hiding this comment

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

I don't think we need to cast too wide of a net here. File locks on Windows are the only thing we've had trouble with (unless I'm forgetting something). We shouldn't add workarounds for hypothetical file permission issues that we don't understand.

Comment on lines +508 to +511
try {
String output = new String(p.getInputStream().readAllBytes(), StringUtilsLabKey.DEFAULT_CHARSET);
LOG.info("Running processes:\n" + output);
}
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.

Consider try-with-resources for the stream

Suggested change
try {
String output = new String(p.getInputStream().readAllBytes(), StringUtilsLabKey.DEFAULT_CHARSET);
LOG.info("Running processes:\n" + output);
}
try (InputStream is = p.getInputStream()) {
String output = new String(is.readAllBytes(), StringUtilsLabKey.DEFAULT_CHARSET);
LOG.info("Running processes:\n" + output);
}

FileUtils.deleteDirectory(dir);
LOG.info(String.format("Deletion of directory %s was successful.", dir));
break;
} catch (IOException ioException) {
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.

We should just rethrow permissions exceptions. We haven't actually run into errors with those.

Suggested change
} catch (IOException ioException) {
} catch (AccessDeniedException adException) {
throw adException;
} catch (IOException ioException) {

}

} catch (IOException diagnosticException) {
LOG.warn("Failed to run lock diagnostic: " + diagnosticException.getMessage());
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.

Loggers can include the entire stack-trace and I think it would be useful to do so.

Suggested change
LOG.warn("Failed to run lock diagnostic: " + diagnosticException.getMessage());
LOG.warn("Failed to run lock diagnostic: " + diagnosticException.getMessage(), diagnosticException);

Comment on lines +473 to +478
dir.setWritable(true, false);

// Wrap in a try to close the stream.
try (Stream<Path> files = Files.walk(dir.toPath())) {
files.forEach(p -> p.toFile().setWritable(true, false));
}
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.

I don't think the tests should be in the business of changing file permissions (unless that's something we are explicitly testing at some point).
If the server (or other process) is leaving a file in an unwritable state, that is something we probably wouldn't want to ignore.

} catch (IOException ioException) {
LOG.warn(String.format("IOException trying to delete directory %s. Error: %s. Waiting 10s and retrying. Attempt %d of 10.",
dir, ioException.getMessage(), attempt));
if (attempt == 10) {
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.

Put the max attempt count in a constant to ensure that this stays in sync with the for loop.
e.g.

Suggested change
if (attempt == 10) {
if (attempt == MAX_ATTEMPTS) {

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