-
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
*/ | ||
public record LogEvent(boolean error, String message) { | ||
|
||
} |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java
Outdated
Show resolved
Hide resolved
synchronized (logs) { | ||
LogEvent logEvent = new LogEvent(errorStream, line); | ||
logs.add(logEvent); | ||
} |
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 am not sure, if I am using LogEvent
correctly. In this implementation, a single LogEvent
instance represents one line from either stout
or sterr
. I don't see another solution to capture the entire log while keeping the order of the log streams.
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.
Now its OutputMessage
instead of LogEvent
. We can still discuss the name
…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 12293812522Details
💛 - Coveralls |
this.err = Objects.requireNonNullElse(err, Collections.emptyList()); | ||
this.outputMessages = Objects.requireNonNullElse(outputMessages, Collections.emptyList()); | ||
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.
Should we fill the out
and err
lists in the constructor or elsewhere?
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.
Good point:
We could compute out and err lazily in the getters so if these getters are never called and we have a larger output, this may be pointless waste otherwise...
if (!this.err.isEmpty()) { | ||
doLog(errorLevel, this.err, context); | ||
if (!this.outputMessages.isEmpty()) { | ||
doLog(outLevel, getOutputMessages().stream().map(OutputMessage::message).collect(Collectors.toList()), context); | ||
} |
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.
Are two separate log levels still necessary with a single lists of captures?
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.
There are special cases at least Eclipse for where we want to make err visible to the user but have out on debug that is hidden by default but can be enabled.
I would therefore recommend to:
- first check if
outLevel == errorLevel
and then do as you suggest/implemented - otherwise simply use the old approach but then also first check if the log-level is actually enabled. So if it is disabled, we do nothing (not even calling
getOut()
orgetErr()
what would compute the extraList
).
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
.
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(); |
this.err = Objects.requireNonNullElse(err, Collections.emptyList()); | ||
this.outputMessages = Objects.requireNonNullElse(outputMessages, Collections.emptyList()); | ||
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.
Good point:
We could compute out and err lazily in the getters so if these getters are never called and we have a larger output, this may be pointless waste otherwise...
if (!this.err.isEmpty()) { | ||
doLog(errorLevel, this.err, context); | ||
if (!this.outputMessages.isEmpty()) { | ||
doLog(outLevel, getOutputMessages().stream().map(OutputMessage::message).collect(Collectors.toList()), context); | ||
} |
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.
There are special cases at least Eclipse for where we want to make err visible to the user but have out on debug that is hidden by default but can be enabled.
I would therefore recommend to:
- first check if
outLevel == errorLevel
and then do as you suggest/implemented - otherwise simply use the old approach but then also first check if the log-level is actually enabled. So if it is disabled, we do nothing (not even calling
getOut()
orgetErr()
what would compute the extraList
).
*/ | ||
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.).
Fixes: #734
This PR improves the process result capturing of
stdout
andstderr
output by preserving the order of the output.