Conversation
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
|
/assign @htuch |
htuch
left a comment
There was a problem hiding this comment.
LGTM, @mergeconflict do you have any thoughts?
mergeconflict
left a comment
There was a problem hiding this comment.
Implementation looks good. I'd like to spend a bit more time focusing on cleaning up the tests; see comments below. Let me know when you're ready and I'll take another look to try and figure out whether we're covering all the possible interesting interaction sequences.
lambdai
left a comment
There was a problem hiding this comment.
Thank @mergeconflict for your advises.
I will revert the WrapUnique and leave the potential actions in other PR/Issue
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
htuch
left a comment
There was a problem hiding this comment.
LGTM; will give @mergeconflict a chance to look before merging.
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
mergeconflict
left a comment
There was a problem hiding this comment.
I think you're almost there, thanks for working on this! Just a couple more bits of cleanup.
| class ExpectableSharedTargetImpl : public SharedTargetImpl { | ||
| public: | ||
| ExpectableSharedTargetImpl(absl::string_view name = "test"); | ||
| ExpectableSharedTargetImpl(absl::string_view name, InitializeFn fn); |
There was a problem hiding this comment.
This ctor defeats the purpose of the mock. The idea of this class is that it's wired up to always call the mocked initialize method, that you can set expectations on using expectInitialize. I don't think you actually use this anyway, so it should be easy to delete.
There was a problem hiding this comment.
Right. Didn't use it in this PR. I will delete it for simplicity
test/common/init/target_impl_test.cc
Outdated
| // SharedTarget acts as TargetImpl if single watcher is provided. | ||
| TEST(InitSharedTargetTestImpl, InitializeSingleWatcher) { | ||
| InSequence s; | ||
|
|
||
| ExpectableSharedTargetImpl target; | ||
| ExpectableWatcherImpl watcher; | ||
|
|
||
| // initializing the target through its handle should invoke initialize()... | ||
| target.expectInitialize(); | ||
| EXPECT_TRUE(target.createHandle("test")->initialize(watcher)); | ||
|
|
||
| // calling ready() on the target should invoke the saved watcher handle... | ||
| watcher.expectReady(); | ||
| EXPECT_TRUE(target.ready()); | ||
|
|
||
| // calling ready() a second time should have no effect. | ||
| watcher.expectReady().Times(0); | ||
| target.ready(); | ||
| } | ||
|
|
||
| // Initializing TargetHandle return false if uninitialized SharedTarget is destroyed. | ||
| TEST(InitSharedTargetTestImpl, InitializeWhenUnavailable) { | ||
| InSequence s; | ||
| ExpectableWatcherImpl watcher; | ||
| TargetHandlePtr handle; | ||
| { | ||
| ExpectableSharedTargetImpl target; | ||
|
|
||
| // initializing the target after it's been destroyed should do nothing. | ||
| handle = target.createHandle("test"); | ||
| target.expectInitialize().Times(0); | ||
| } | ||
| EXPECT_FALSE(handle->initialize(watcher)); | ||
| } | ||
|
|
||
| // Initializing TargetHandle return false if initialized SharedTarget is destroyed. | ||
| TEST(InitSharedTargetTestImpl, ReInitializeWhenUnavailable) { | ||
| InSequence s; | ||
| ExpectableWatcherImpl w; | ||
| TargetHandlePtr handle; | ||
| { | ||
| ExpectableSharedTargetImpl target; | ||
|
|
||
| target.expectInitialize(); | ||
| TargetHandlePtr handle1 = target.createHandle("m1"); | ||
| ExpectableWatcherImpl w1; | ||
| EXPECT_TRUE(handle1->initialize(w1)); | ||
|
|
||
| // initializing the target after it's been destroyed should do nothing. | ||
| handle = target.createHandle("m2"); | ||
| target.expectInitialize().Times(0); | ||
| // target destroyed | ||
| } | ||
| EXPECT_FALSE(handle->initialize(w)); | ||
| } |
There was a problem hiding this comment.
Rather than copy-pasting these tests from above, can you use https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#typed-tests to refactor? I think all the invariants held by TargetImpl must also be held by SharedTargetImpl.
There was a problem hiding this comment.
You caught me :)
I delete the expectation from target.ready()
Will recover and use typed test
| EXPECT_FALSE(handle->initialize(w)); | ||
| } | ||
|
|
||
| // SharedTarget notifies multiple watchers. |
There was a problem hiding this comment.
So these two new tests capture these two sequences, if I'm reading correctly:
initialize(w1)
initialize(w2)
ready
and
initialize(w1)
ready
initialize(w2)
I guess you could also write one for
ready
initialize(w1)
initialize(w2)
But that's probably not very interesting... I can't think of any more interesting cases, so I think you're good. Can you think of anything?
There was a problem hiding this comment.
It's not hard to support a ready() prior to initialize any watcher.
The confusing part what are the expected consequences.
MUST: notify the following watchers
Question: Should the initialization function defined in SharedTarget should be called?
I am going to leave the questionable behavior as undefined and set no expectation.
Let me know what you think
|
@mergeconflict Thank you! I am updating the PR |
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
mergeconflict
left a comment
There was a problem hiding this comment.
Looking good, just a few last nits now. Thanks!
| bool SharedTargetImpl::ready() { | ||
| initialized_ = true; | ||
| bool all_notified = true; | ||
| bool all_notified = !watcher_handles_.empty(); |
There was a problem hiding this comment.
Interesting... I guess this is for consistency with TargetImpl::ready returning false if there's no watcher handle. But now I'm wondering if I made the right choice there. What do you think?
There was a problem hiding this comment.
Yes it is.
It seems no production code is relying on this return value.
It's OK to leave the return value here since so far it is easy to maintain.
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
|
@mergeconflict So the latest tests are |
mergeconflict
left a comment
There was a problem hiding this comment.
LGTM, thanks for all the work on this!
htuch
left a comment
There was a problem hiding this comment.
Thanks @lambdai! Also thanks @mergeconflict for the deep dive.
| template <typename T> class TargetImplTest : public ::testing::Test {}; | ||
| TYPED_TEST_SUITE_P(TargetImplTest); | ||
|
|
||
| template <typename T> std::string getName() { return ""; } |
There was a problem hiding this comment.
FWIW, you could avoid templates here and do something similar to what was done in https://github.com/envoyproxy/envoy/blob/master/test/common/config/subscription_test_harness.h, but I think this isn't too bad and makes things nice and clean.
Full context is here The above PR is split into #9008 and this one. In this PR envoy will maintain only one copy of rds config for each resource name. Also fixed a the bug of early listener notified ready describe in #8781 The test case is included in this PR. Signed-off-by: Yuchen Dai <silentdai@gmail.com>
Full context is here The above PR is split into envoyproxy#9008 and this one. In this PR envoy will maintain only one copy of rds config for each resource name. Also fixed a the bug of early listener notified ready describe in envoyproxy#8781 The test case is included in this PR. Signed-off-by: Yuchen Dai <silentdai@gmail.com> Signed-off-by: Prakhar <prakhar_au@yahoo.com>
Signed-off-by: Yuchen Dai silentdai@gmail.com
Extracted from #8523
@mergeconflict mentioned we can also maintain a collection of
TargetImpl(and a initialize_ flag, std::once_flag) at the target owner.I agree it works but a SharedTarget provides stronger cohesion.
Follow up: