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
KAFKA-14132: Replace PowerMock and EasyMock with Mockito in streams tests #12821
Conversation
streams/src/test/java/org/apache/kafka/streams/processor/internals/RepartitionTopicsTest.java
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/RepartitionTopicsTest.java
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/RepartitionTopicsTest.java
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Show resolved
Hide resolved
} | ||
|
||
@Test | ||
public void shouldNotWipeStateStoresIfUnableToLockTaskDirectory() throws IOException { |
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 test appears to be just wrong, unless I am reading the code incorrectly. Because we use expect(stateDirectory.lock(taskId)).andReturn(false);
we never reach the code path which would be using Utils.delete
. As such, I just removed the test.
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.
As far as I understand the test, the author wanted to fail the test with an AssertionError
if Utils.delete
is called after the lock could not be acquired. With Mockito, you can verify that Utils.delete
is never called.
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.
Sorry, I do not understand. I think this test and the one above can be collapsed in the test I have written. I still verify that deletion did not happen by checking that a few methods were never interacted with.
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.
As in, it appears a bit strange to mock everything on a path to the Utils.delete when this logic short-circuits if we use expect(stateDirectory.lock(taskId)).andReturn(false);
.
try {
if (stateDirectory.lock(id)) { <---- WE JUMP FROM HERE...
try {
stateMgr.close(); <---- WE VERIFY THIS IS NOT CALLED
} catch (final ProcessorStateException e) {
firstException.compareAndSet(null, e);
} finally {
try {
if (wipeStateStore) {
log.debug("Wiping state stores for {} task {}", taskType, id);
// we can just delete the whole dir of the task, including the state store images and the checkpoint files,
// and then we write an empty checkpoint file indicating that the previous close is graceful and we just
// need to re-bootstrap the restoration from the beginning
Utils.delete(stateMgr.baseDir() <---- WE VERIFY THIS IS NOT CALLED);
}
} finally {
stateDirectory.unlock(id); <---- WE VERIFY THIS IS NOT CALLED
}
}
}
} catch (final IOException e) {
final ProcessorStateException exception = new ProcessorStateException(
String.format("%sFatal error while trying to close the state manager for task %s", logPrefix, id), e
);
firstException.compareAndSet(null, exception);
} <---- ...ALL THE WAY TO HERE
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 see what you mean, but unit tests should also document the behavior of production code and be robust against code changes. In this case, we have indeed to separate cases:
- When exactly-once is enabled and the state manager is closed dirty and the lock is not owned then the state manager should not be closed, the directory should not be wiped and the directory should not be unlocked.
- When exactly-once is disabled and the lock is not owned then the state manager should not be closed and the directory should not be unlocked.
So, I think what you did is fine, although would not verify that stateManager.baseDir()
is never called but that Utils.delete()
is never called. However, you need to run that test once with:
StateManagerUtil.closeStateManager(
logger, "logPrefix:", false, true, stateManager, stateDirectory, TaskType.ACTIVE);
and once with
StateManagerUtil.closeStateManager(
logger, "logPrefix:", true, false, stateManager, stateDirectory, TaskType.ACTIVE);
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.
Okay, I agree, I will create a new commit testing both scenarios.
Hey @cadonna 👋! Adding you for visibility as these are Streams-related tests. |
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.
@clolov Thanks for the PR!
Here my feedback!
} | ||
|
||
@Test | ||
public void shouldNotWipeStateStoresIfUnableToLockTaskDirectory() throws IOException { |
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.
As far as I understand the test, the author wanted to fail the test with an AssertionError
if Utils.delete
is called after the lock could not be acquired. With Mockito, you can verify that Utils.delete
is never called.
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Show resolved
Hide resolved
Thank you for the review @cadonna. I will aim to address it today! |
e648e8a
to
7fdbf82
Compare
@clolov please fix the checkstyle failures in this PR! |
7fdbf82
to
09ea80f
Compare
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.
@clolov Thanks for the updates and sorry for the long silence on this PR!
I had one major comment and some nits.
} | ||
|
||
@Test | ||
public void shouldNotWipeStateStoresIfUnableToLockTaskDirectory() throws IOException { |
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 see what you mean, but unit tests should also document the behavior of production code and be robust against code changes. In this case, we have indeed to separate cases:
- When exactly-once is enabled and the state manager is closed dirty and the lock is not owned then the state manager should not be closed, the directory should not be wiped and the directory should not be unlocked.
- When exactly-once is disabled and the lock is not owned then the state manager should not be closed and the directory should not be unlocked.
So, I think what you did is fine, although would not verify that stateManager.baseDir()
is never called but that Utils.delete()
is never called. However, you need to run that test once with:
StateManagerUtil.closeStateManager(
logger, "logPrefix:", false, true, stateManager, stateDirectory, TaskType.ACTIVE);
and once with
StateManagerUtil.closeStateManager(
logger, "logPrefix:", true, false, stateManager, stateDirectory, TaskType.ACTIVE);
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Outdated
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Outdated
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Outdated
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Outdated
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Outdated
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Outdated
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Outdated
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Outdated
Show resolved
Hide resolved
streams/src/test/java/org/apache/kafka/streams/processor/internals/StateManagerUtilTest.java
Show resolved
Hide resolved
Hello @cadonna and thanks for the review! I will aim to address the comments tomorrow. |
99cc9ae
to
687730d
Compare
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.
@clolov Thanks for the update and as always for your patience!
LGTM!
I have just minor comment! I will leave it up to you, if you want to change it. Just let me know so that I can merge.
inOrder.verify(stateManager, never()).close(); | ||
inOrder.verify(stateManager, never()).baseDir(); | ||
inOrder.verify(stateDirectory, never()).unlock(taskId); |
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 do not know how much it makes sense to verify the call order of methods that should not be called.
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 is a fair point. I used inOrder
because the original test wanted to be strict. This being said it wanted to be strict on the actual calls rather than the calls which weren't made. Does
@Test
public void shouldNotCloseStateManagerIfUnableToLockTaskDirectory() {
final InOrder inOrder = inOrder(stateManager, stateDirectory);
when(stateManager.taskId()).thenReturn(taskId);
when(stateDirectory.lock(taskId)).thenReturn(false);
StateManagerUtil.closeStateManager(
logger, "logPrefix:", true, false, stateManager, stateDirectory, TaskType.ACTIVE);
inOrder.verify(stateManager).taskId(); <- ENSURE ORDER
inOrder.verify(stateDirectory).lock(taskId); <- ENSURE ORDER
verify(stateManager, never()).close();
verify(stateManager, never()).baseDir();
verify(stateDirectory, never()).unlock(taskId);
verifyNoMoreInteractions(stateManager, stateDirectory);
}
@Test
public void shouldNotWipeStateStoresIfUnableToLockTaskDirectory() {
final InOrder inOrder = inOrder(stateManager, stateDirectory);
when(stateManager.taskId()).thenReturn(taskId);
when(stateDirectory.lock(taskId)).thenReturn(false);
StateManagerUtil.closeStateManager(
logger, "logPrefix:", false, true, stateManager, stateDirectory, TaskType.ACTIVE);
inOrder.verify(stateManager).taskId(); <- ENSURE ORDER
inOrder.verify(stateDirectory).lock(taskId); <- ENSURE ORDER
verify(stateManager, never()).close();
verify(stateManager, never()).baseDir();
verify(stateDirectory, never()).unlock(taskId);
verifyNoMoreInteractions(stateManager, stateDirectory);
}
look better to you?
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.
Looks great!
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.
Okay, I pushed the latest rebased version!
inOrder.verify(stateManager, never()).close(); | ||
inOrder.verify(stateManager, never()).baseDir(); | ||
inOrder.verify(stateDirectory, never()).unlock(taskId); |
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.
See my comment above about call order.
Co-authored-by: Bruno Cadonna <cadonna@apache.org>
687730d
to
5183cbe
Compare
Build failures are unrelated:
|
Thank you very much for the review and merge! |
…ests (apache#12821) Batch 1 of the tests detailed in https://issues.apache.org/jira/browse/KAFKA-14132 which use PowerMock/EasyMock and need to be moved to Mockito. Reviewer: Bruno Cadonna <cadonna@apache.org>
Batch 1 of the tests detailed in https://issues.apache.org/jira/browse/KAFKA-14132 which use PowerMock/EasyMock and need to be moved to Mockito.