-
Notifications
You must be signed in to change notification settings - Fork 373
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
Improve the UT coverage for portcache #4284
Improve the UT coverage for portcache #4284
Conversation
Codecov Report
@@ Coverage Diff @@
## main #4284 +/- ##
==========================================
+ Coverage 64.52% 65.36% +0.84%
==========================================
Files 402 402
Lines 57238 57238
==========================================
+ Hits 36932 37416 +484
+ Misses 17594 17117 -477
+ Partials 2712 2705 -7
|
6e208dd
to
f98c6b8
Compare
40f41ad
to
0071fcb
Compare
e781b7b
to
51039c4
Compare
portTable := newPortTable(mockIPTables, mockPortOpener) | ||
podIP := "10.0.0.1" | ||
|
||
portTable.DeleteRule(podIP, 1001, "tcp") |
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.
What's the point to call the function without checking anything? The main purpose of unit test is to verify that the function works as we expected. You should check the returned error and comparing it.
I think you can try to call portTable.addPortTableCache()
to add a few data on the cache, then test the function with the cache.
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.
+1
mockIPTables.EXPECT().AddRule(nodePort2, podIP, 1002, "udp") | ||
|
||
syncedCh := make(chan struct{}) | ||
const timeout = 1 * time.Second |
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.
You can define this const on line 32
endPort = 65000 | ||
) | ||
|
||
func newPortTable(mockIPTables rules.PodPortRules, mockPortOpener LocalPortOpener) *PortTable { |
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.
windows doesn't have IPTable, which may be the reason why the type is named generically. Using mockIPTables as argument name doesn't make sense.
func TestRestoreRules(t *testing.T) { | ||
mockCtrl := gomock.NewController(t) | ||
defer mockCtrl.Finish() | ||
mockIPTables := rulestesting.NewMockPodPortRules(mockCtrl) |
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.
ditto
select { | ||
case <-syncedCh: | ||
break | ||
case <-time.After(timeout): | ||
// this will not kill the goroutine created by RestoreRules, | ||
// which should be acceptable. | ||
t.Fatalf("Rule restoration not complete after %v", timeout) | ||
} |
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.
RestoreRules's windows version has nothing to do with goroutine, this shouldn't be copied.
portTable := newPortTable(mockIPTables, mockPortOpener) | ||
podIP := "10.0.0.1" | ||
|
||
portTable.DeleteRule(podIP, 1001, "tcp") |
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.
+1
3163eaa
to
c6b39cc
Compare
e39d51d
to
a1aaa1c
Compare
} else { | ||
mockPortRules.EXPECT().AddRule(tc.nodePort, tc.podIP, tc.podPort, tc.protocol) | ||
nodePort, err := portTable.AddRule(tc.podIP, tc.podPort, tc.protocol) | ||
assert.NoError(t, err) |
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.
require.NoError(t, err)
is a good practice to ensure that the test will abort earlier.
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.
But in this case there are no follow-up function calls that will panic, so isn't this fine to use assert 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.
also the rest of the cases for the current test are skipped if we use require instead of assert.
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.
That is expected, I think there is no point to check the other values when there is an unexpected error.
startPort = 61000 | ||
endPort = 65000 | ||
) | ||
|
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.
Functions like addRuleforFreePort and addRuleForPort needs to called from test file by mocking the top level function and hence internal functions will be considered for percentage UT coverage, this can substantialy increase the coverage, the UT tool does not consider the coverage if the functions are internal, Suggest to follow this approach to increase UT coverage.
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.
Functions like addRuleforFreePort and addRuleForPort needs to called from test file by mocking the top level function and hence internal functions will be considered for percentage UT coverage, this can substantialy increase the coverage, the UT tool does not consider the coverage if the functions are internal, Suggest to follow this approach to increase UT coverage.
222730c
to
ff7901a
Compare
b8f9a0c
to
895204a
Compare
const ( | ||
startPort = 61000 | ||
endPort = 65000 | ||
) |
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.
Can these variables and newPortTable be shared for linux and windows test? I didn't see any differences.
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.
yes they can be, but we do not have any shared file for test purpose, or you want me to declare these variables in the actual code file.
podIP := "10.0.0.1" | ||
nodePort1 := startPort | ||
nodePort2 := startPort + 1 |
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.
You probably can also define shared test data for these.
podIP: "10.0.0.1", | ||
podPort: 1001, |
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 suppose these two fields can be removed if their values are all the same in all cases. If they should change to cover more, then you may need to add more different cases.
895204a
to
ffa209b
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.
LGTM overall, two nits.
ffa209b
to
26643e1
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.
LGTM
@@ -0,0 +1,44 @@ | |||
// Copyright 2022 Antrea Authors |
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.
Not having test.go suffix will make the file be compiled into antrea-agent binary, increasing its size unnecessarily. Normally the platform-generic code could be put under foo_test.go, then both foo_linux_test.go and foo_windows_test.go can use it. We should rename the current port_table_test.go to port_table_linux_test.go if it's supposed to run on linux only or port_table_other_test.go.
if tc.testForPod { | ||
err = portTable.DeleteRule(podIP, 1001, "tcp") | ||
} else { | ||
err = portTable.DeleteRulesForPod(podIP) | ||
} |
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.
- When testForPod is true, it tests DeleteRule, which looks confusing.
- It's actually not common to test two different functions in one test, orginazing them as table driven style, which is usually about expected input and output for same function. To share code for multiple tests, extracting common code to a shared function is more common.
- However, I think the point of
DeleteRulesForPod
is it can delete all rules for the specific pod IP, unlikeDeleteRule
can only delete one specified rule. So an appropriate test for the former is to construct multiple rules first, then make sure a single call can clean up all of them.
26643e1
to
502f590
Compare
for _, tc := range tcs { | ||
t.Run(tc.name, func(t *testing.T) { | ||
if tc.expectedErr == "" { | ||
mockPortRules.EXPECT().AddRule(tc.nodePort, podIP, podPort, tc.protocol) | ||
} | ||
nodePort, err := portTable.AddRule(podIP, podPort, tc.protocol) | ||
if tc.expectedErr != "" { | ||
assert.ErrorContains(t, err, tc.expectedErr) | ||
} else { | ||
require.NoError(t, err) | ||
assert.Equal(t, tc.nodePort, nodePort) | ||
} | ||
}) | ||
} |
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.
table driven test's cases should be individual, so it's easy to add new cases and adjust existing ones with worrying about the others, and it makes it possible to run specific sub test only.
I assume in this test the 2nd subtest relies on the 1st one.
It should just do it sequentially to make the dependency clear:
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
mockPortRules := rulestesting.NewMockPodPortRules(mockCtrl)
mockPortOpener := portcachetesting.NewMockLocalPortOpener(mockCtrl)
portTable := newPortTable(mockPortRules, mockPortOpener)
podPort := 1001
// Adding the rule the first time should succeed.
mockPortRules.EXPECT().AddRule(tc.nodePort, podIP, podPort, tc.protocol)
gotNodePort, err := portTable.AddRule(podIP, podPort, tc.protocol)
require.NoError(t, err)
assert.Equal(t, startPort, gotNodePort)
// Add the same rule the second time should fail.
_, err := portTable.AddRule(podIP, podPort, tc.protocol)
assert.ErrorContains(t, err, "existing Windows Nodeport entry for")
@@ -0,0 +1,44 @@ | |||
// Copyright 2022 Antrea Authors |
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.
It's not common to have a test file whose name's prefix doesn't map to a file.
Since this is for the test code of port_table.go, it could just be named port_table_test.go.
Signed-off-by: Pulkit Jain <jainpu@vmware.com>
502f590
to
2686845
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.
LGTM
/skip-all |
Signed-off-by: Pulkit Jain <jainpu@vmware.com>
Added unit test for pkg/agent/nodeportlocal/portcahce package.
For #4142
Signed-off-by: Pulkit Jain jainpu@vmware.com