Skip to content

Conversation

@marek-szews
Copy link
Contributor

@marek-szews marek-szews commented Dec 19, 2025

Add more unit tests to increase test coverage of 'randomsubsetting' package.

RELEASE NOTES:

  • balancer/randomsubsetting: Implementation of additional UT.

@codecov
Copy link

codecov bot commented Dec 19, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.28%. Comparing base (2d51986) to head (ae6f169).
⚠️ Report is 59 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8781      +/-   ##
==========================================
- Coverage   83.47%   83.28%   -0.19%     
==========================================
  Files         419      414       -5     
  Lines       32595    32805     +210     
==========================================
+ Hits        27208    27321     +113     
- Misses       4017     4076      +59     
- Partials     1370     1408      +38     

see 67 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@marek-szews marek-szews changed the title balancer/randomsubsetting: Extend the test coverage of randomsubsetting package. balancer/randomsubsetting: Extend the unit tests in the randomsubsetting package. Dec 19, 2025
_ "google.golang.org/grpc/balancer/roundrobin" // For round_robin LB policy in tests
)

func (s) TestSubsettingEndpointsDomain(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is similar to the existing test TestCalculateSubset_Simple.
You can remove this and add more test cases in TestCalculateSubset_Simple if you think of any.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Yes, the only possible case that could be added to TestCalculateSubset_Simple is the case where the number of endpoints is strictly greater than the subset size.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed it is only one test case missing, i had renamed function and added to test set.

}
}

func (s) TestUniformDistributionOfEndpoints(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a descriptive comment for this test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this test to the existing test file randomsubsetting_test.go.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to both the above comments. Please add comments about why the math is the way it is in the test.

Copy link
Contributor Author

@marek-szews marek-szews Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Appropriate commentary added.

}
}

func (s) TestUniformDistributionOfEndpoints(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you run the test ~10k times on your local to verify that the test is not flaky.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often does this flake currently? We do not want to add tests that can flake. We want a test that is guaranteed to pass every time that it runs, and when it fails, it should really mean that there is a bug in the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added two examples of tests for which the uniform distribution verification is negative.


endpoints := makeEndpoints(16)
expected := iteration / len(endpoints) * subsetSize
diff = expected / 7 // allow ~14% difference
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain the reasoning behind 14% error-rate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you look at this and modify the test to calculate how many iterations are needed for statistical significance, instead of arbitrarily adding huge error rate to satisfy small range of iterations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I carefully analyzed the test acceptance criteria and came to the conclusion that it was too naive. Finally I use the Chi-square goodness-of-fit test, standard statistical method used to validate whether a dataset follows a uniform distribution. It assesses if the observed frequencies in your data align with the expected frequencies of a theoretical distribution where every outcome has an equal chance of occurring.

Copy link
Contributor Author

@marek-szews marek-szews left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All comments were addressed. Additionally, each test of distribution concludes with a short report.
=== NAME Test/UniformDistributionOfEndpoints
randomsubsetting_test.go:404: Test Case: Endpoints=16, SubsetSize=4, Iterations=10
randomsubsetting_test.go:409: Distribution check passed:
Endpoint | ExpValue | Diff from E | Status
---------------------------------------------------------------------------
endpoint-5 | 2.50 | 1.50 | ! > E ± σ (Noticeable)
endpoint-9 | 2.50 | 1.50 | ! > E ± σ (Noticeable)
endpoint-12 | 2.50 | 1.50 | ! > E ± σ (Noticeable)
endpoint-13 | 2.50 | 1.50 | ! > E ± σ (Noticeable)
endpoint-0 | 2.50 | 2.50 | ! > E ± σ (Noticeable)
endpoint-4 | 2.50 | 0.50 | E ± σ (Normal)
endpoint-10 | 2.50 | 0.50 | E ± σ (Normal)
endpoint-11 | 2.50 | 0.50 | E ± σ (Normal)
endpoint-1 | 2.50 | 1.50 | ! > E ± σ (Noticeable)
endpoint-6 | 2.50 | 0.50 | E ± σ (Normal)
endpoint-2 | 2.50 | 2.50 | ! > E ± σ (Noticeable)
endpoint-7 | 2.50 | 0.50 | E ± σ (Normal)
endpoint-8 | 2.50 | 0.50 | E ± σ (Normal)
endpoint-14 | 2.50 | 0.50 | E ± σ (Normal)
endpoint-15 | 2.50 | 1.50 | ! > E ± σ (Noticeable)
endpoint-3 | 2.50 | 1.50 | ! > E ± σ (Noticeable)
---------------------------------------------------------------------------
Distribution is uniform (χ²=12.00 <= critical value=24.99)

@emil10001
Copy link
Member

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds valuable unit tests for the randomsubsetting balancer, significantly increasing test coverage. The new tests include simple subset size validation and a statistical test for uniform endpoint distribution. My review focuses on improving the implementation of these tests for better clarity, correctness, and adherence to idiomatic Go practices. The suggestions include simplifying comparison logic, correcting the use of t.Run in a statistical test loop, refactoring complex boolean logic, and using idiomatic function signatures for maps.

Comment on lines +390 to +402
t.Run(fmt.Sprint(L), func(t *testing.T) {
t.Helper()
lb := &subsettingBalancer{
cfg: &lbConfig{SubsetSize: uint32(L)},
hashSeed: uint64(i ^ 3 + K*i + L),
hashDigest: xxhash.New(),
}
subsetOfEndpoints := lb.calculateSubset(endpoints)

for _, ep := range subsetOfEndpoints {
EndpointCount[ep.Addresses[0].Addr]++
}
})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The use of t.Run inside this for loop is incorrect. The purpose of this loop is to perform multiple iterations to gather statistics for a single test, not to define many sub-tests. This approach creates a large number of sub-tests (equal to K) with identical names, which clutters the test output and is not the intended use of t.Run. The subset calculation logic should be executed directly within the loop.

				lb := &subsettingBalancer{
					cfg:        &lbConfig{SubsetSize: uint32(L)},
					hashSeed:   uint64(i ^ 3 + K*i + L),
					hashDigest: xxhash.New(),
				}
				subsetOfEndpoints := lb.calculateSubset(endpoints)

				for _, ep := range subsetOfEndpoints {
					EndpointCount[ep.Addresses[0].Addr]++
				}

Comment on lines +297 to +299
if fmt.Sprint(got) != fmt.Sprint(tc.want) {
t.Fatalf("subset size=%v; endpoints(%v) = %v; want %v", tc.want, tc.endpoints, got, tc.want)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using fmt.Sprint for numeric comparison is inefficient and not idiomatic. A direct integer comparison is clearer and more performant. Additionally, the failure message can be improved to be more concise and avoid printing a potentially large list of endpoints.

Suggested change
if fmt.Sprint(got) != fmt.Sprint(tc.want) {
t.Fatalf("subset size=%v; endpoints(%v) = %v; want %v", tc.want, tc.endpoints, got, tc.want)
}
if got != int(tc.want) {
t.Fatalf("len(subsetOfEndpoints) = %d; want %d", got, tc.want)
}

Comment on lines +452 to +464
if chi2 > criticalValue {
if dd.expectSuccess {
testPassed = false
} else {
testPassed = true
}
reportBuilder.WriteString(fmt.Sprintf("Distribution is not uniform (χ²=%.2f > critical value=%.2f)", chi2, criticalValue))
} else {
if !dd.expectSuccess {
testPassed = false
}
reportBuilder.WriteString(fmt.Sprintf("Distribution is uniform (χ²=%.2f <= critical value=%.2f)", chi2, criticalValue))
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for setting testPassed is correct but unnecessarily complex and verbose. It can be simplified to a single boolean expression, which makes the code more readable and maintainable.

	isUniform := chi2 <= criticalValue
	testPassed = isUniform == dd.expectSuccess

	if isUniform {
		reportBuilder.WriteString(fmt.Sprintf("Distribution is uniform (χ²=%.2f <= critical value=%.2f)", chi2, criticalValue))
	} else {
		reportBuilder.WriteString(fmt.Sprintf("Distribution is not uniform (χ²=%.2f > critical value=%.2f)", chi2, criticalValue))
	}

Comment on lines +469 to +473
func initEndpointCount(epCount *map[string]int, eps []resolver.Endpoint) {
for _, ep := range eps {
(*epCount)[ep.Addresses[0].Addr] = 0
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The function initEndpointCount takes a pointer to a map (*map[string]int). In Go, maps are reference types, so passing them by value is sufficient to allow the function to modify their contents. Using a pointer is unidiomatic unless the function needs to re-assign the map variable itself. Please change the signature to accept a map by value. Note that the call to this function on line 387 will also need to be updated from initEndpointCount(&EndpointCount, endpoints) to initEndpointCount(EndpointCount, endpoints).

func initEndpointCount(epCount map[string]int, eps []resolver.Endpoint) {
	for _, ep := range eps {
		epCount[ep.Addresses[0].Addr] = 0
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants