-
Notifications
You must be signed in to change notification settings - Fork 24
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
#734: Improve Process Result Model #773
base: main
Are you sure you want to change the base?
Conversation
…om/alfeilex/IDEasy into devonfw#734-improve-process-result
cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java
Outdated
Show resolved
Hide resolved
cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java
Outdated
Show resolved
Hide resolved
…rocess-result # Conflicts: # cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java # cli/src/main/java/com/devonfw/tools/ide/process/ProcessResultImpl.java
…age and refactor ProcessContextGitMock
Pull Request Test Coverage Report for Build 12377261111Details
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alfeilex thanks for your PR. Already on its way and looking good. Thanks 👍
I left some review comments for improvement. Please have a look.
|
||
String line; | ||
while ((line = br.readLine()) != null) { | ||
synchronized (outputMessages) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may create a DeadLock. Java default synchronization is very inefficient and we will have two threads continuously blocking each other.
Consider using a concurrent collection.
I would suggest to use ConcurrentLinkedQueue
for collecting the OutputMessage
s and when completed, convert to a regular List
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried using ConcurrentLinkedQueue
, and at first, it worked fine. However, after running my test case several times, I noticed that the order sometimes changed, causing err1
to appear first in the list of output messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OMG. Sorry, if I caused a mess here.
Lets have a look at this in January after vacation time.
Maybe we can resolve the problem and make it work deterministic and reliable...
In the worst case, we have to revert the Concurrent-Collection approach and need to go back to your original solution using synchronized
(or better using ReentrantLock
).
this.out = this.outputMessages.stream().filter(outputMessage -> !outputMessage.error()).map(OutputMessage::message).collect(Collectors.toList()); | ||
this.err = this.outputMessages.stream().filter(OutputMessage::error).map(OutputMessage::message).collect(Collectors.toList()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small simplification:
this.out = this.outputMessages.stream().filter(outputMessage -> !outputMessage.error()).map(OutputMessage::message).collect(Collectors.toList()); | |
this.err = this.outputMessages.stream().filter(OutputMessage::error).map(OutputMessage::message).collect(Collectors.toList()); | |
this.out = this.outputMessages.stream().filter(outputMessage -> !outputMessage.error()).map(OutputMessage::message).toList(); | |
this.err = this.outputMessages.stream().filter(OutputMessage::error).map(OutputMessage::message).toList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
*/ | ||
public List<String> getErrors() { | ||
|
||
return errors; | ||
return this.outputMessages.stream().filter(OutputMessage::error).map(OutputMessage::message).collect(Collectors.toList()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will not work. Current code is calling getErrors()
or getOuts()
in order to add mocked messages to such List
.
Streams will create immutable collections that cannot be modified after creation.
In case we leave it like this, also use toList()
simplification in all places.
Could you discuss with @jan-vcapgemini who IMHO created this class about the design?
Wouldn't it be smarter to create a regular ProcessResult
here and make it accessible so instead of tests calling
processContextGitMock.getErrors().add("fake error message")
we could call processContextGitMock.getProcessResult().getOutputMessages().add(new OutputMessage("fake error message", true))
to make it work.
There is a lot of pointless redundancy in this class related to ProcessResultImpl
that can be avoided this way.
BTW: I just noticed that you already found this problem and had to update an existing test-case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want a convenience method for adding faked out or err messages, you could still provide them here but delegating to the ProcessResult. Also this way we could even allow a setter for the mocked ProcessResult
that also gives us more flexibility (resetting the result for a next faked git call, etc.).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -181,7 +182,8 @@ public ProcessResult run(ProcessMode processMode) { | |||
} | |||
} | |||
this.arguments.clear(); | |||
return new ProcessResultImpl("git", command.toString(), this.exitCode, this.outs, this.errors); | |||
setProcessResult(getExitCode(), this.processResult.getOutputMessages()); | |||
return this.processResult; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setExitCode()
and getExitCode()
are currently not used in the implementation. The exitcode is set differently in conditions with e.g. this.exitcode = 0
What is here the best solution? Is it the right way to use getters and setters inside a class, also for mock classes or can we just assign the attributes with values directly?
// assert | ||
assertThat(context).log(IdeLogLevel.INFO).hasEntries("out1", "err1", "out2", "err2"); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After several runs, you will see that the order is not deterministic, probably because the choice of the first stdout
or stderr
thread is not fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If synchronized
is to inefficent and we stick to ConcurrentLinkedQueue
then we have to accept the compromise that the order is not 100% guaranteed. Also, the test-case doesn't work and testing the order like in the implemented test-case isn't possible and has to be removed before merging
OutputMessage outputMessage = new OutputMessage(errorStream, line); | ||
outputMessages.add(outputMessage); | ||
} | ||
|
||
return br.lines().toList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we try to read the lines again from the buffered reader after it has already been consumed to the end?
IMHO you should change the signature from CompletableFuture<List<String>>
to CompletableFuture<Void>
and simply return null
here.
Fixes: #734
This PR improves the process result capturing of
stdout
andstderr
output by preserving the order of the output.