1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.lang3.builder;
19
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21 import static org.junit.jupiter.api.Assertions.assertTrue;
22
23 import java.time.Duration;
24 import java.time.Instant;
25 import java.time.LocalDate;
26 import java.time.LocalDateTime;
27 import java.time.LocalTime;
28 import java.time.OffsetDateTime;
29 import java.time.OffsetTime;
30 import java.time.Period;
31 import java.time.Year;
32 import java.time.YearMonth;
33 import java.time.ZoneId;
34 import java.time.ZoneOffset;
35 import java.time.ZonedDateTime;
36 import java.time.chrono.HijrahDate;
37 import java.time.chrono.JapaneseDate;
38 import java.time.chrono.MinguoDate;
39 import java.time.chrono.ThaiBuddhistDate;
40 import java.time.temporal.Temporal;
41 import java.time.temporal.TemporalAccessor;
42 import java.time.temporal.TemporalAmount;
43 import java.time.temporal.TemporalField;
44 import java.time.temporal.TemporalUnit;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.List;
48 import java.util.function.Supplier;
49
50 import org.apache.commons.lang3.AbstractLangTest;
51 import org.apache.commons.lang3.stream.IntStreams;
52 import org.junit.jupiter.api.Test;
53
54
55
56
57 class EqualsBuilderReflectJreImplementationTest extends AbstractLangTest {
58
59 static class MyCharSequence implements CharSequence {
60
61 private final char[] chars;
62
63 MyCharSequence(final char[] chars) {
64 this.chars = Arrays.copyOf(chars, chars.length);
65 }
66
67 MyCharSequence(final char[] chars, final int start, final int end) {
68 this.chars = Arrays.copyOfRange(chars, start, end);
69 }
70
71 MyCharSequence(final String string) {
72 this.chars = string.toCharArray();
73 }
74
75 @Override
76 public char charAt(final int index) {
77 return chars[index];
78 }
79
80 @Override
81 public int length() {
82 return chars.length;
83 }
84
85 @Override
86 public CharSequence subSequence(final int start, final int end) {
87 return new MyCharSequence(chars, start, end);
88 }
89
90 @Override
91 public String toString() {
92 return new String(chars);
93 }
94 }
95
96 static class MyClass implements Cloneable {
97
98 private final MyCharSequence charSequence;
99 private final MyTemporal temporal;
100 private final MyTemporalAccessor temporalAccessor;
101 private final MyTemporalAmount temporalAmount;
102 private final Object[] objects;
103 private final List<Supplier<?>> list = new ArrayList<>();
104
105 MyClass(final MyCharSequence charSequence, final MyTemporal temporal, final MyTemporalAccessor temporalAccessor,
106 final MyTemporalAmount temporalAmount) {
107 this.charSequence = charSequence;
108 this.temporal = temporal;
109 this.temporalAccessor = temporalAccessor;
110 this.temporalAmount = temporalAmount;
111 final int value = Integer.parseInt(charSequence.toString());
112 final LocalDate localDate = LocalDate.ofEpochDay(value);
113 final LocalTime localTime = LocalTime.of(value, value);
114 final LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
115 final OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime, ZoneOffset.UTC);
116 final ZoneOffset zoneOffset = ZoneOffset.ofHours(value);
117 this.objects = new Object[] {
118
119 value,
120
121 localDate, HijrahDate.from(localDate), JapaneseDate.from(localDate), MinguoDate.from(localDate), ThaiBuddhistDate.from(localDate),
122 localDate, localTime, localDateTime, offsetDateTime, OffsetTime.of(localTime, zoneOffset), Year.of(value), YearMonth.of(value, value),
123 ZonedDateTime.of(localDateTime, zoneOffset), zoneOffset, ZoneId.of(zoneOffset.getId()) };
124 IntStreams.range(100).forEach(i -> list.add(() -> charSequence));
125 }
126
127 @Override
128 public String toString() {
129 return String.format("%s[%s, %s, %s, %s, %s]", getClass().getSimpleName(), charSequence, temporal, temporalAccessor, temporalAmount,
130 Arrays.toString(objects));
131 }
132 }
133
134 static class MyTemporal implements Temporal {
135
136 private final String string;
137 private final int value;
138 private final Duration duration;
139 private final Instant instant;
140 private final Period period;
141
142 MyTemporal(final String string) {
143 this.string = string;
144 this.value = Integer.parseInt(string);
145 this.instant = Instant.ofEpochMilli(value);
146 this.duration = Duration.between(instant, instant.plusMillis(value));
147 this.period = Period.ofDays(value);
148 }
149
150 @Override
151 public long getLong(final TemporalField field) {
152 return instant.get(field);
153 }
154
155 @Override
156 public boolean isSupported(final TemporalField field) {
157 return instant.isSupported(field);
158 }
159
160 @Override
161 public boolean isSupported(final TemporalUnit unit) {
162 return instant.isSupported(unit);
163 }
164
165 @Override
166 public Temporal plus(final long amountToAdd, final TemporalUnit unit) {
167 return instant.plus(amountToAdd, unit);
168 }
169
170 @Override
171 public String toString() {
172 return String.format("%s[%s, %s, %s, %s]", getClass().getSimpleName(), string, instant, duration, period);
173 }
174
175 @Override
176 public long until(final Temporal endExclusive, final TemporalUnit unit) {
177 return instant.until(endExclusive, unit);
178 }
179
180 @Override
181 public Temporal with(final TemporalField field, final long newValue) {
182 return instant.with(field, newValue);
183 }
184
185 }
186
187 static class MyTemporalAccessor implements TemporalAccessor {
188
189 private final String string;
190 private final int value;
191 private final Instant instant;
192 private final Duration duration;
193 private final Period period;
194
195 MyTemporalAccessor(final String string) {
196 this.string = string;
197 this.value = Integer.parseInt(string);
198 this.instant = Instant.ofEpochMilli(value);
199 this.duration = Duration.between(instant, instant.plusMillis(value));
200 this.period = Period.ofDays(value);
201 }
202
203 @Override
204 public long getLong(final TemporalField field) {
205 return instant.get(field);
206 }
207
208 @Override
209 public boolean isSupported(final TemporalField field) {
210 return instant.isSupported(field);
211 }
212
213 @Override
214 public String toString() {
215 return String.format("%s[%s, %s, %s, %s]", getClass().getSimpleName(), string, instant, duration, period);
216 }
217
218 }
219
220 static class MyTemporalAmount implements TemporalAmount {
221
222 private final String string;
223 private final int value;
224 private final Instant instant;
225 private final Duration duration;
226 private final Period period;
227
228 MyTemporalAmount(final String string) {
229 this.string = string;
230 this.value = Integer.parseInt(string);
231 this.instant = Instant.ofEpochMilli(value);
232 this.duration = Duration.between(instant, instant.plusMillis(value));
233 this.period = Period.ofDays(value);
234 }
235
236 @Override
237 public Temporal addTo(final Temporal temporal) {
238 return duration.addTo(temporal);
239 }
240
241 @Override
242 public long get(final TemporalUnit unit) {
243 return duration.get(unit);
244 }
245
246 @Override
247 public List<TemporalUnit> getUnits() {
248 return duration.getUnits();
249 }
250
251 @Override
252 public Temporal subtractFrom(final Temporal temporal) {
253 return duration.subtractFrom(temporal);
254 }
255
256 @Override
257 public String toString() {
258 return String.format("%s[%s - %s - %s - %s]", getClass().getSimpleName(), string, instant, duration, period);
259 }
260
261 }
262
263 @Test
264 void testRecursive() {
265 final MyClass o1 = new MyClass(new MyCharSequence("1"), new MyTemporal("2"), new MyTemporalAccessor("3"), new MyTemporalAmount("4"));
266
267 final MyClass o1Bis = new MyClass(new MyCharSequence("1"), new MyTemporal("2"), new MyTemporalAccessor("3"), new MyTemporalAmount("4"));
268 final MyClass o2 = new MyClass(new MyCharSequence("5"), new MyTemporal("6"), new MyTemporalAccessor("7"), new MyTemporalAmount("8"));
269 final MyClass o2Bis = new MyClass(new MyCharSequence("5"), new MyTemporal("6"), new MyTemporalAccessor("7"), new MyTemporalAmount("8"));
270
271 assertTrue(new EqualsBuilder().setTestRecursive(true).append(new MyTemporal("1"), new MyTemporal("1")).isEquals());
272
273 assertTrue(new EqualsBuilder().setTestRecursive(true).append(new MyTemporalAccessor("1"), new MyTemporalAccessor("1")).isEquals());
274
275 assertTrue(new EqualsBuilder().setTestRecursive(true).append(new MyCharSequence("1"), new MyCharSequence("1")).isEquals());
276
277 assertTrue(new EqualsBuilder().setTestRecursive(true).append(o1, o1).isEquals(), o1::toString);
278 assertTrue(new EqualsBuilder().setTestRecursive(true).append(o1, o1Bis).isEquals(), o1::toString);
279 assertTrue(new EqualsBuilder().setTestRecursive(true).append(o2, o2).isEquals(), o2::toString);
280 assertTrue(new EqualsBuilder().setTestRecursive(true).append(o2, o2Bis).isEquals(), o2::toString);
281 assertFalse(new EqualsBuilder().setTestRecursive(true).append(o1, o2).isEquals());
282 assertFalse(new EqualsBuilder().setTestRecursive(true).append(o2, o1).isEquals());
283 }
284
285 @Test
286 void testRetention() throws Exception {
287
288 for (int i = 0; i < Integer.getInteger("testRetention", 10_000); i++) {
289 final Class<?> clazz = TestClassBuilder.defineSimpleClass(getClass().getPackage().getName(), i);
290 assertTrue(new EqualsBuilder().setTestRecursive(true).append(clazz.newInstance(), clazz.newInstance()).isEquals());
291 }
292
293 }
294
295 }