1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.validator.routines.checkdigit;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24 import static org.junit.jupiter.api.Assertions.fail;
25 import static org.junit.jupiter.api.Assumptions.assumeTrue;
26
27 import java.io.ByteArrayInputStream;
28 import java.io.ByteArrayOutputStream;
29 import java.io.ObjectInputStream;
30 import java.io.ObjectOutputStream;
31 import java.io.Serializable;
32 import java.util.ArrayList;
33 import java.util.List;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.junit.jupiter.api.AfterEach;
38 import org.junit.jupiter.api.Test;
39
40
41
42
43 public abstract class AbstractCheckDigitTest {
44
45 private static final String POSSIBLE_CHECK_DIGITS = "0123456789 ABCDEFHIJKLMNOPQRSTUVWXYZ\tabcdefghijklmnopqrstuvwxyz!@£$%^&*()_+";
46
47
48 protected Log log = LogFactory.getLog(getClass());
49
50
51 protected int checkDigitLth = 1;
52
53
54 protected CheckDigit routine;
55
56
57
58
59
60
61
62
63 protected String[] valid;
64
65
66
67
68
69
70
71
72
73 protected String[] invalid = { "12345678A" };
74
75
76 protected String zeroSum = "0000000000";
77
78
79 protected String missingMessage = "Code is missing";
80
81
82
83
84
85
86
87 protected String checkDigit(final String code) {
88 if (code == null || code.length() <= checkDigitLth) {
89 return "";
90 }
91 final int start = code.length() - checkDigitLth;
92 return code.substring(start);
93 }
94
95
96
97
98
99
100
101
102 protected String[] createInvalidCodes(final String[] codes) {
103 final List<String> list = new ArrayList<>();
104
105
106 for (final String fullCode : codes) {
107 final String code = removeCheckDigit(fullCode);
108 final String check = checkDigit(fullCode);
109 for (int j = 0; j < POSSIBLE_CHECK_DIGITS.length(); j++) {
110 final String curr = POSSIBLE_CHECK_DIGITS.substring(j, j + 1);
111 if (!curr.equals(check)) {
112 list.add(code + curr);
113 }
114 }
115 }
116
117 return list.toArray(new String[0]);
118 }
119
120
121
122
123
124
125
126 protected String removeCheckDigit(final String code) {
127 if (code == null || code.length() <= checkDigitLth) {
128 return null;
129 }
130 return code.substring(0, code.length() - checkDigitLth);
131 }
132
133
134
135
136 @AfterEach
137 protected void tearDown() {
138 valid = null;
139 routine = null;
140 }
141
142
143
144
145 @Test
146 void testCalculateInvalid() {
147
148 if (log.isDebugEnabled()) {
149 log.debug("testCalculateInvalid() for " + routine.getClass().getName());
150 }
151
152
153 for (int i = 0; i < invalid.length; i++) {
154 try {
155 final String code = invalid[i];
156 if (log.isDebugEnabled()) {
157 log.debug(" " + i + " Testing Invalid Check Digit, Code=[" + code + "]");
158 }
159 final String expected = checkDigit(code);
160 final String codeWithNoCheckDigit = removeCheckDigit(code);
161 if (codeWithNoCheckDigit == null) {
162 throw new CheckDigitException("Invalid Code=[" + code + "]");
163 }
164 final String actual = routine.calculate(codeWithNoCheckDigit);
165
166 if (expected.equals(actual)) {
167 fail("Expected mismatch for " + code + " expected " + expected + " actual " + actual);
168 }
169 } catch (final CheckDigitException e) {
170
171
172
173
174 assertTrue(e.getMessage().startsWith("Invalid "), "Invalid Character[" + i + "]=" + e.getMessage());
175
176 }
177 }
178 }
179
180
181
182
183 @Test
184 void testCalculateValid() {
185 if (log.isDebugEnabled()) {
186 log.debug("testCalculateValid() for " + routine.getClass().getName());
187 }
188
189
190 for (int i = 0; i < valid.length; i++) {
191 final String code = removeCheckDigit(valid[i]);
192 final String expected = checkDigit(valid[i]);
193 try {
194 if (log.isDebugEnabled()) {
195 log.debug(" " + i + " Testing Valid Check Digit, Code=[" + code + "] expected=[" + expected + "]");
196 }
197 assertEquals(expected, routine.calculate(code), "valid[" + i + "]: " + valid[i]);
198 } catch (final Exception e) {
199 fail("valid[" + i + "]=" + valid[i] + " threw " + e);
200 }
201 }
202
203 }
204
205
206
207
208 @Test
209 void testIsValidFalse() {
210 if (log.isDebugEnabled()) {
211 log.debug("testIsValidFalse() for " + routine.getClass().getName());
212 }
213
214
215 for (int i = 0; i < invalid.length; i++) {
216 if (log.isDebugEnabled()) {
217 log.debug(" " + i + " Testing Invalid Code=[" + invalid[i] + "]");
218 }
219 assertFalse(routine.isValid(invalid[i]), "invalid[" + i + "]: " + invalid[i]);
220 }
221
222
223 final String[] invalidCheckDigits = createInvalidCodes(valid);
224 for (int i = 0; i < invalidCheckDigits.length; i++) {
225 if (log.isDebugEnabled()) {
226 log.debug(" " + i + " Testing Invalid Check Digit, Code=[" + invalidCheckDigits[i] + "]");
227 }
228 assertFalse(routine.isValid(invalidCheckDigits[i]), "invalid check digit[" + i + "]: " + invalidCheckDigits[i]);
229 }
230 }
231
232
233
234
235 @Test
236 void testIsValidTrue() {
237 if (log.isDebugEnabled()) {
238 log.debug("testIsValidTrue() for " + routine.getClass().getName());
239 }
240
241
242 for (int i = 0; i < valid.length; i++) {
243 if (log.isDebugEnabled()) {
244 log.debug(" " + i + " Testing Valid Code=[" + valid[i] + "]");
245 }
246 assertTrue(routine.isValid(valid[i]), "valid[" + i + "]: " + valid[i]);
247 }
248 }
249
250
251
252
253 @Test
254 void testMissingCode() {
255
256
257 assertFalse(routine.isValid(null), "isValid() Null");
258
259
260 assertFalse(routine.isValid(""), "isValid() Zero Length");
261
262
263
264 assertFalse(routine.isValid("9"), "isValid() Length 1");
265
266
267 Exception e = assertThrows(Exception.class, () -> routine.calculate(null), "calculate() Null");
268 assertEquals(missingMessage, e.getMessage(), "calculate() Null");
269
270
271 e = assertThrows(Exception.class, () -> routine.calculate(""), "calculate() Zero Length");
272 assertEquals(missingMessage, e.getMessage(), "calculate() Zero Length");
273 }
274
275
276
277
278 @Test
279 void testSerialization() {
280 assumeTrue(routine instanceof Serializable);
281
282 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
283 try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
284 oos.writeObject(routine);
285 oos.flush();
286 } catch (final Exception e) {
287 fail(routine.getClass().getName() + " error during serialization: " + e);
288 }
289
290
291 Object result = null;
292 try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray())) {
293 final ObjectInputStream ois = new ObjectInputStream(bais);
294 result = ois.readObject();
295 } catch (final Exception e) {
296 fail(routine.getClass().getName() + " error during deserialization: " + e);
297 }
298 assertNotNull(result);
299 }
300
301
302
303
304 @Test
305 void testZeroSum() {
306 assertFalse(routine.isValid(zeroSum), "isValid() Zero Sum");
307 final Exception e = assertThrows(Exception.class, () -> routine.calculate(zeroSum), "Zero Sum");
308 assertEquals("Invalid code, sum is zero", e.getMessage(), "isValid() Zero Sum");
309 }
310
311 }