Skip to content

Commit cb6f438

Browse files
authored
test: add white-box unit tests for Luhn.luhnCheck (#7641)
* test: add white-box unit tests for Luhn.luhnCheck Cover the loop (zero, one and many iterations), both outcomes of the `i % 2 == 0` and `temp > 9` decisions, boundary values (8 / 10 after doubling), valid and invalid checksums, input immutability and null input. * test: drop null input case flagged by Infer Infer reports NULLPTR_DEREFERENCE for passing null to luhnCheck on purpose. The method does not document null handling, so the case is removed.
1 parent 9d9e348 commit cb6f438

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.thealgorithms.others;
2+
3+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import org.junit.jupiter.api.Test;
8+
9+
/**
10+
* White-box tests for {@link Luhn#luhnCheck(int[])}.
11+
*
12+
* <p>The method has one loop and two decisions:
13+
* <ul>
14+
* <li>D1: {@code i % 2 == 0} - whether the digit at index i is doubled</li>
15+
* <li>D2: {@code temp > 9} - whether 9 is subtracted from the doubled digit</li>
16+
* </ul>
17+
* The tests below cover the loop executed zero, one and many times, both
18+
* outcomes of each decision, and both outcomes of the final {@code sum % 10 == 0}.
19+
*/
20+
class LuhnTest {
21+
22+
@Test
23+
void testEmptyArraySkipsLoopAndIsValid() {
24+
// Loop body never executes, sum stays 0
25+
assertTrue(Luhn.luhnCheck(new int[] {}));
26+
}
27+
28+
@Test
29+
void testSingleZeroDigitIsValid() {
30+
// One iteration: D1 true, D2 false (0 * 2 = 0)
31+
assertTrue(Luhn.luhnCheck(new int[] {0}));
32+
}
33+
34+
@Test
35+
void testSingleDigitDoubledWithoutSubtractionIsInvalid() {
36+
// One iteration: D1 true, D2 false (4 * 2 = 8, upper boundary of temp <= 9)
37+
assertFalse(Luhn.luhnCheck(new int[] {4}));
38+
}
39+
40+
@Test
41+
void testSingleDigitDoubledWithSubtractionIsInvalid() {
42+
// One iteration: D1 true, D2 true (5 * 2 = 10, lower boundary of temp > 9 -> 1)
43+
assertFalse(Luhn.luhnCheck(new int[] {5}));
44+
}
45+
46+
@Test
47+
void testTwoDigitsWithoutSubtraction() {
48+
// i = 1: D1 false -> 2; i = 0: D1 true, D2 false -> 8; sum = 10
49+
assertTrue(Luhn.luhnCheck(new int[] {4, 2}));
50+
}
51+
52+
@Test
53+
void testTwoDigitsWithSubtraction() {
54+
// i = 1: D1 false -> 9; i = 0: D1 true, D2 true -> 10 - 9 = 1; sum = 10
55+
assertTrue(Luhn.luhnCheck(new int[] {5, 9}));
56+
}
57+
58+
@Test
59+
void testTwoDigitsInvalidChecksum() {
60+
// i = 1: D1 false -> 3; i = 0: D1 true, D2 false -> 8; sum = 11
61+
assertFalse(Luhn.luhnCheck(new int[] {4, 3}));
62+
}
63+
64+
@Test
65+
void testMaxDigitDoubled() {
66+
// i = 1: D1 false -> 0; i = 0: D1 true, D2 true -> 18 - 9 = 9; sum = 9
67+
assertFalse(Luhn.luhnCheck(new int[] {9, 0}));
68+
// i = 1: D1 false -> 1; i = 0: D1 true, D2 true -> 9; sum = 10
69+
assertTrue(Luhn.luhnCheck(new int[] {9, 1}));
70+
}
71+
72+
@Test
73+
void testValidCardNumbers() {
74+
// Many iterations, all branch combinations exercised
75+
assertTrue(Luhn.luhnCheck(new int[] {4, 5, 6, 1, 2, 6, 1, 2, 1, 2, 3, 4, 5, 4, 6, 7}));
76+
assertTrue(Luhn.luhnCheck(new int[] {5, 2, 6, 5, 9, 2, 5, 1, 6, 1, 5, 1, 1, 4, 1, 2}));
77+
assertTrue(Luhn.luhnCheck(new int[] {4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}));
78+
}
79+
80+
@Test
81+
void testCardNumberWithTypoInLastDigitIsInvalid() {
82+
assertFalse(Luhn.luhnCheck(new int[] {4, 5, 6, 1, 2, 6, 1, 2, 1, 2, 3, 4, 5, 4, 6, 4}));
83+
}
84+
85+
@Test
86+
void testCardNumberWithSingleDigitErrorIsInvalid() {
87+
// Error in a doubled position (index 0)
88+
assertFalse(Luhn.luhnCheck(new int[] {3, 5, 6, 1, 2, 6, 1, 2, 1, 2, 3, 4, 5, 4, 6, 7}));
89+
// Error in a non-doubled position (index 1)
90+
assertFalse(Luhn.luhnCheck(new int[] {4, 6, 6, 1, 2, 6, 1, 2, 1, 2, 3, 4, 5, 4, 6, 7}));
91+
}
92+
93+
@Test
94+
void testAllZerosIsValid() {
95+
assertTrue(Luhn.luhnCheck(new int[16]));
96+
}
97+
98+
@Test
99+
void testInputArrayIsNotModified() {
100+
int[] digits = {5, 2, 6, 5, 9, 2, 5, 1, 6, 1, 5, 1, 1, 4, 1, 2};
101+
int[] copy = digits.clone();
102+
Luhn.luhnCheck(digits);
103+
assertArrayEquals(copy, digits);
104+
}
105+
}

0 commit comments

Comments
 (0)