From 1a76f00b6a5ce4bcfe8081538ff60d49c173e0b2 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Fri, 27 Dec 2024 14:52:00 +0800 Subject: [PATCH 01/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=20Quarter=20=E5=92=8C?= =?UTF-8?q?=20YearQuarter=20=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 6 +- .../zhouxy/plusone/commons/time/Quarter.java | 4 +- .../plusone/commons/time/YearQuarter.java | 51 +- .../plusone/commons/time/QuarterTests.java | 5 +- .../commons/time/YearQuarterTests.java | 1114 ++++++++++++++++- 5 files changed, 1110 insertions(+), 70 deletions(-) diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index 2f81ee2..e757a12 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,6 +1,6 @@ [ ] 未开始测试 - 15 (21.43%) -[-] 测试未完成 - 11 (15.71%) -[Y] 测试完成 - 23 (32.86%) +[-] 测试未完成 - 10 (14.29%) +[Y] 测试完成 - 24 (34.29%) [x] 无需测试 - 21 (30.00%) xyz.zhouxy.plusone.commons @@ -80,7 +80,7 @@ xyz.zhouxy.plusone.commons │ ├───time │ Quarter.java [Y] - │ YearQuarter.java [-] + │ YearQuarter.java [Y] │ └───util ArrayTools.java [-] diff --git a/src/main/java/xyz/zhouxy/plusone/commons/time/Quarter.java b/src/main/java/xyz/zhouxy/plusone/commons/time/Quarter.java index 71efd48..279ca23 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/time/Quarter.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/time/Quarter.java @@ -16,6 +16,7 @@ package xyz.zhouxy.plusone.commons.time; +import java.time.DateTimeException; import java.time.Month; import java.time.MonthDay; import java.time.temporal.ChronoField; @@ -171,7 +172,8 @@ public enum Quarter implements IWithIntCode { // Getters end public static int checkValidIntValue(int value) { - AssertTools.checkArgument(value >= 1 && value <= 4, () -> "Invalid value for Quarter: " + value); + AssertTools.checkCondition(value >= 1 && value <= 4, + () -> new DateTimeException("Invalid value for Quarter: " + value)); return value; } diff --git a/src/main/java/xyz/zhouxy/plusone/commons/time/YearQuarter.java b/src/main/java/xyz/zhouxy/plusone/commons/time/YearQuarter.java index 7fd4885..947b065 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/time/YearQuarter.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/time/YearQuarter.java @@ -22,6 +22,7 @@ import java.io.Serializable; import java.time.LocalDate; import java.time.Month; import java.time.YearMonth; +import java.time.temporal.ChronoField; import java.util.Calendar; import java.util.Date; import java.util.Objects; @@ -52,7 +53,6 @@ public final class YearQuarter implements Comparable, Serializable private final LocalDate lastDate; private YearQuarter(int year, @Nonnull Quarter quarter) { - AssertTools.checkNotNull(quarter, "Quarter can not be null."); this.year = year; this.quarter = quarter; this.firstDate = quarter.firstMonthDay().atYear(year); @@ -70,7 +70,7 @@ public final class YearQuarter implements Comparable, Serializable */ @StaticFactoryMethod(YearQuarter.class) public static YearQuarter of(int year, int quarter) { - return of(year, Quarter.of(quarter)); + return new YearQuarter(YEAR.checkValidIntValue(year), Quarter.of(quarter)); } /** @@ -81,8 +81,8 @@ public final class YearQuarter implements Comparable, Serializable * @return {@link YearQuarter} 实例 */ @StaticFactoryMethod(YearQuarter.class) - public static YearQuarter of(int year, @Nonnull Quarter quarter) { - return new YearQuarter(year, quarter); + public static YearQuarter of(int year, Quarter quarter) { + return new YearQuarter(YEAR.checkValidIntValue(year), Objects.requireNonNull(quarter)); } /** @@ -92,8 +92,9 @@ public final class YearQuarter implements Comparable, Serializable * @return {@link YearQuarter} 实例 */ @StaticFactoryMethod(YearQuarter.class) - public static YearQuarter of(@Nonnull LocalDate date) { - return of(date.getYear(), Quarter.fromMonth(date.getMonth())); + public static YearQuarter of(LocalDate date) { + AssertTools.checkNotNull(date); + return new YearQuarter(date.getYear(), Quarter.fromMonth(date.getMonth())); } /** @@ -103,12 +104,13 @@ public final class YearQuarter implements Comparable, Serializable * @return {@link YearQuarter} 实例 */ @StaticFactoryMethod(YearQuarter.class) - public static YearQuarter of(@Nonnull Date date) { + public static YearQuarter of(Date date) { + AssertTools.checkNotNull(date); @SuppressWarnings("deprecation") - final int year = date.getYear() + 1900; + final int yearValue = YEAR.checkValidIntValue(date.getYear() + 1900L); @SuppressWarnings("deprecation") - final int month = date.getMonth() + 1; - return of(year, Quarter.fromMonth(month)); + final int monthValue = date.getMonth() + 1; + return new YearQuarter(yearValue, Quarter.fromMonth(monthValue)); } /** @@ -119,7 +121,10 @@ public final class YearQuarter implements Comparable, Serializable */ @StaticFactoryMethod(YearQuarter.class) public static YearQuarter of(Calendar date) { - return of(date.get(Calendar.YEAR), Quarter.fromMonth(date.get(Calendar.MONTH) + 1)); + AssertTools.checkNotNull(date); + final int yearValue = ChronoField.YEAR.checkValidIntValue(date.get(Calendar.YEAR)); + final int monthValue = date.get(Calendar.MONTH) + 1; + return new YearQuarter(yearValue, Quarter.fromMonth(monthValue)); } /** @@ -130,9 +135,15 @@ public final class YearQuarter implements Comparable, Serializable */ @StaticFactoryMethod(YearQuarter.class) public static YearQuarter of(YearMonth yearMonth) { + AssertTools.checkNotNull(yearMonth); return of(yearMonth.getYear(), Quarter.fromMonth(yearMonth.getMonth())); } + @StaticFactoryMethod(YearQuarter.class) + public static YearQuarter now() { + return of(LocalDate.now()); + } + // #endregion // #region - Getters @@ -185,7 +196,7 @@ public final class YearQuarter implements Comparable, Serializable // #region - computes - public YearQuarter plusQuarters(long quartersToAdd) { // TODO 单元测试 + public YearQuarter plusQuarters(long quartersToAdd) { if (quartersToAdd == 0L) { return this; } @@ -193,27 +204,27 @@ public final class YearQuarter implements Comparable, Serializable long calcQuarters = quarterCount + quartersToAdd; // safe overflow int newYear = YEAR.checkValidIntValue(Math.floorDiv(calcQuarters, 4)); int newQuarter = (int) Math.floorMod(calcQuarters, 4) + 1; - return of(newYear, Quarter.of(newQuarter)); + return new YearQuarter(newYear, Quarter.of(newQuarter)); } - public YearQuarter minusQuarters(long quartersToAdd) { // TODO 单元测试 + public YearQuarter minusQuarters(long quartersToAdd) { return plusQuarters(-quartersToAdd); } - public YearQuarter nextQuarter() { // TODO 单元测试 + public YearQuarter nextQuarter() { return plusQuarters(1L); } - public YearQuarter lastQuarter() { // TODO 单元测试 + public YearQuarter lastQuarter() { return minusQuarters(1L); } - public YearQuarter plusYears(long yearsToAdd) { // TODO 单元测试 + public YearQuarter plusYears(long yearsToAdd) { if (yearsToAdd == 0L) { return this; } int newYear = YEAR.checkValidIntValue(this.year + yearsToAdd); // safe overflow - return of(newYear, this.quarter); + return new YearQuarter(newYear, this.quarter); } public YearQuarter minusYears(long yearsToAdd) { @@ -270,11 +281,11 @@ public final class YearQuarter implements Comparable, Serializable return this.compareTo(other) > 0; } - public static YearQuarter min(YearQuarter yearQuarter1, YearQuarter yearQuarter2) { // TODO 单元测试 + public static YearQuarter min(YearQuarter yearQuarter1, YearQuarter yearQuarter2) { return yearQuarter1.compareTo(yearQuarter2) <= 0 ? yearQuarter1 : yearQuarter2; } - public static YearQuarter max(YearQuarter yearQuarter1, YearQuarter yearQuarter2) { // TODO 单元测试 + public static YearQuarter max(YearQuarter yearQuarter1, YearQuarter yearQuarter2) { return yearQuarter1.compareTo(yearQuarter2) >= 0 ? yearQuarter1 : yearQuarter2; } diff --git a/src/test/java/xyz/zhouxy/plusone/commons/time/QuarterTests.java b/src/test/java/xyz/zhouxy/plusone/commons/time/QuarterTests.java index 75f583f..976ccc7 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/time/QuarterTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/time/QuarterTests.java @@ -18,6 +18,7 @@ package xyz.zhouxy.plusone.commons.time; import static org.junit.jupiter.api.Assertions.*; +import java.time.DateTimeException; import java.time.Month; import java.time.MonthDay; @@ -37,7 +38,7 @@ class QuarterTests { assertEquals(1, quarter.getValue()); assertEquals("Q1", quarter.name()); - assertThrows(IllegalArgumentException.class, () -> { + assertThrows(DateTimeException.class, () -> { Quarter.of(0); }); @@ -84,7 +85,7 @@ class QuarterTests { assertEquals(2, quarter.getValue()); assertEquals("Q2", quarter.name()); - assertThrows(IllegalArgumentException.class, () -> { + assertThrows(DateTimeException.class, () -> { Quarter.of(5); }); diff --git a/src/test/java/xyz/zhouxy/plusone/commons/time/YearQuarterTests.java b/src/test/java/xyz/zhouxy/plusone/commons/time/YearQuarterTests.java index 0e63de8..fcb14a6 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/time/YearQuarterTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/time/YearQuarterTests.java @@ -21,66 +21,775 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import lombok.extern.slf4j.Slf4j; +import java.time.DateTimeException; import java.time.LocalDate; +import java.time.Month; +import java.time.Year; import java.time.YearMonth; - +import java.util.Calendar; +import java.util.Date; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @Slf4j public class YearQuarterTests { - @Test - void of_ValidYearAndQuarter_CreatesYearQuarter() { - int year = 2023; - Quarter quarter = Quarter.Q1; + // ================================ + // #region - of(int year, int quarter) + // ================================ - YearQuarter expected = YearQuarter.of(year, quarter); - YearQuarter actual = YearQuarter.of(LocalDate.of(year, 2, 28)); + @ParameterizedTest + @ValueSource(ints = { 1, 2, 3, 4 }) + void of_ValidYearAndQuarterValue_CreatesYearQuarter(int quarter) { + { + int year = 2024; + YearQuarter yearQuarter = YearQuarter.of(year, quarter); + log.info("{}", yearQuarter); + assertEquals(year, yearQuarter.getYear()); + assertEquals(Quarter.of(quarter), yearQuarter.getQuarter()); + assertEquals(quarter, yearQuarter.getQuarterValue()); + } + { + int year = Year.MIN_VALUE; + YearQuarter yearQuarter = YearQuarter.of(year, quarter); + assertEquals(year, yearQuarter.getYear()); + assertEquals(Quarter.of(quarter), yearQuarter.getQuarter()); + assertEquals(quarter, yearQuarter.getQuarterValue()); + } + { + int year = Year.MAX_VALUE; + YearQuarter yearQuarter = YearQuarter.of(year, quarter); + assertEquals(year, yearQuarter.getYear()); + assertEquals(Quarter.of(quarter), yearQuarter.getQuarter()); + assertEquals(quarter, yearQuarter.getQuarterValue()); + } + } - assertEquals(expected, actual); + @ParameterizedTest + @ValueSource(ints = { -1, 0, 5, 108 }) + void of_ValidYearAndInvalidQuarterValue_DateTimeException(int quarter) { + int year = 2024; + assertThrows(DateTimeException.class, () -> { + YearQuarter.of(year, quarter); + }); + } - assertEquals("2023 Q1", actual.toString()); + @ParameterizedTest + @ValueSource(ints = { Year.MIN_VALUE - 1, Year.MAX_VALUE + 1 }) + void of_InvalidYearAndValidQuarterValue_DateTimeException(int year) { + assertThrows(DateTimeException.class, () -> { + YearQuarter.of(year, 1); + }); + assertThrows(DateTimeException.class, () -> { + YearQuarter.of(year, 2); + }); + assertThrows(DateTimeException.class, () -> { + YearQuarter.of(year, 3); + }); + assertThrows(DateTimeException.class, () -> { + YearQuarter.of(year, 4); + }); } @Test - @SuppressWarnings("null") - void of_InvalidQuarter_ThrowsNullPointerException() { - int year = 2023; - Quarter quarter = null; - - assertThrows(NullPointerException.class, () -> YearQuarter.of(year, quarter)); - } - - @Test - void of_ValidYearQuarter_GetsCorrectStartAndEndDate() { - - for (int year = 1990; year <= 2024; year++) { - for (int quarterValue = 1; quarterValue <= 4; quarterValue++) { - Quarter quarter = Quarter.of(quarterValue); - YearQuarter yearQuarter = YearQuarter.of(year, quarter); - - LocalDate expectedStartDate = quarter.firstMonthDay().atYear(year); - log.info("{} - expectedStartDate: {}", yearQuarter, expectedStartDate); - LocalDate expectedEndDate = quarter.lastMonthDay().atYear(year); - log.info("{} - expectedEndDate: {}", yearQuarter, expectedEndDate); - - assertEquals(expectedStartDate, yearQuarter.firstDate()); - assertEquals(expectedEndDate, yearQuarter.lastDate()); + void of_InvalidYearAndInvalidQuarterValue_DateTimeException() { + final int[] years = { Year.MIN_VALUE - 1, Year.MAX_VALUE + 1 }; + final int[] quarters = { -1, 0, 5, 108 }; + for (int year : years) { + final int yearValue = year; + for (int quarter : quarters) { + final int quarterValue = quarter; + assertThrows(DateTimeException.class, + () -> YearQuarter.of(yearValue, quarterValue)); } } } + // ================================ + // #endregion - of(int year, int quarter) + // ================================ + + // ================================ + // #region - of(int year, Quarter quarter) + // ================================ + + @ParameterizedTest + @ValueSource(ints = { 1, 2, 3, 4 }) + void of_ValidYearAndQuarter_CreatesYearQuarter(int quarterValue) { + { + int year = 2024; + Quarter quarter = Quarter.of(quarterValue); + YearQuarter yearQuarter = YearQuarter.of(year, quarter); + log.info("{}", yearQuarter); + assertEquals(year, yearQuarter.getYear()); + assertEquals(quarter, yearQuarter.getQuarter()); + assertEquals(quarterValue, yearQuarter.getQuarterValue()); + } + { + int year = Year.MIN_VALUE; + Quarter quarter = Quarter.of(quarterValue); + YearQuarter yearQuarter = YearQuarter.of(year, quarter); + assertEquals(year, yearQuarter.getYear()); + assertEquals(quarter, yearQuarter.getQuarter()); + assertEquals(quarterValue, yearQuarter.getQuarterValue()); + } + { + int year = Year.MAX_VALUE; + Quarter quarter = Quarter.of(quarterValue); + YearQuarter yearQuarter = YearQuarter.of(year, quarter); + assertEquals(year, yearQuarter.getYear()); + assertEquals(quarter, yearQuarter.getQuarter()); + assertEquals(quarterValue, yearQuarter.getQuarterValue()); + } + } + + @Test + void of_ValidYearAndNullQuarter_NullPointerException() { + int year = 2024; + assertThrows(NullPointerException.class, () -> { + YearQuarter.of(year, null); + }); + } + + @ParameterizedTest + @ValueSource(ints = { Year.MIN_VALUE - 1, Year.MAX_VALUE + 1 }) + void of_InvalidYearAndValidQuarter_DateTimeException(int year) { + assertThrows(DateTimeException.class, () -> { + YearQuarter.of(year, Quarter.Q1); + }); + assertThrows(DateTimeException.class, () -> { + YearQuarter.of(year, Quarter.Q2); + }); + assertThrows(DateTimeException.class, () -> { + YearQuarter.of(year, Quarter.Q3); + }); + assertThrows(DateTimeException.class, () -> { + YearQuarter.of(year, Quarter.Q4); + }); + } + + @Test + void of_InvalidYearAndNullQuarter_DateTimeException() { + final int[] years = { Year.MIN_VALUE - 1, Year.MAX_VALUE + 1 }; + for (int year : years) { + final int yearValue = year; + assertThrows(DateTimeException.class, + () -> YearQuarter.of(yearValue, null)); + + } + } + + // ================================ + // #endregion - of(int year, Quarter quarter) + // ================================ + + // ================================ + // #region - of(LocalDate date) + // ================================ + + @ParameterizedTest + @ValueSource(ints = { + 2023, // 非闰年 + 2024, // 闰年 + Year.MIN_VALUE, + Year.MAX_VALUE, + }) + void of_ValidLocalDate_CreatesYearQuarter_Q1(int year) { + { + LocalDate date = YearMonth.of(year, 1).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(1, yq.getQuarterValue()); + assertSame(Quarter.Q1, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 1).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(1, yq.getQuarterValue()); + assertSame(Quarter.Q1, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 2).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(1, yq.getQuarterValue()); + assertSame(Quarter.Q1, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 2).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(1, yq.getQuarterValue()); + assertSame(Quarter.Q1, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 3).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(1, yq.getQuarterValue()); + assertSame(Quarter.Q1, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 3).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(1, yq.getQuarterValue()); + assertSame(Quarter.Q1, yq.getQuarter()); + } + } + @ParameterizedTest @ValueSource(ints = { - 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, - 2020, 2021, 2022, 2023, 2024 + 2023, // 非闰年 + 2024, // 闰年 + Year.MIN_VALUE, + Year.MAX_VALUE, }) - void testFirstYearMonth(int year) { + void of_ValidLocalDate_CreatesYearQuarter_Q2(int year) { + { + LocalDate date = YearMonth.of(year, 4).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(2, yq.getQuarterValue()); + assertSame(Quarter.Q2, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 4).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(2, yq.getQuarterValue()); + assertSame(Quarter.Q2, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 5).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(2, yq.getQuarterValue()); + assertSame(Quarter.Q2, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 5).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(2, yq.getQuarterValue()); + assertSame(Quarter.Q2, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 6).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(2, yq.getQuarterValue()); + assertSame(Quarter.Q2, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 6).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(2, yq.getQuarterValue()); + assertSame(Quarter.Q2, yq.getQuarter()); + } + } + + @ParameterizedTest + @ValueSource(ints = { + 2023, // 非闰年 + 2024, // 闰年 + Year.MIN_VALUE, + Year.MAX_VALUE, + }) + void of_ValidLocalDate_CreatesYearQuarter_Q3(int year) { + { + LocalDate date = YearMonth.of(year, 7).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(3, yq.getQuarterValue()); + assertSame(Quarter.Q3, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 7).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(3, yq.getQuarterValue()); + assertSame(Quarter.Q3, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 8).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(3, yq.getQuarterValue()); + assertSame(Quarter.Q3, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 8).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(3, yq.getQuarterValue()); + assertSame(Quarter.Q3, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 9).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(3, yq.getQuarterValue()); + assertSame(Quarter.Q3, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 9).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(3, yq.getQuarterValue()); + assertSame(Quarter.Q3, yq.getQuarter()); + } + } + + @ParameterizedTest + @ValueSource(ints = { + 2023, // 非闰年 + 2024, // 闰年 + Year.MIN_VALUE, + Year.MAX_VALUE, + }) + void of_ValidLocalDate_CreatesYearQuarter_Q4(int year) { + { + LocalDate date = YearMonth.of(year, 10).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(4, yq.getQuarterValue()); + assertSame(Quarter.Q4, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 10).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(4, yq.getQuarterValue()); + assertSame(Quarter.Q4, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 11).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(4, yq.getQuarterValue()); + assertSame(Quarter.Q4, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 11).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(4, yq.getQuarterValue()); + assertSame(Quarter.Q4, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 12).atDay(1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(4, yq.getQuarterValue()); + assertSame(Quarter.Q4, yq.getQuarter()); + } + { + LocalDate date = YearMonth.of(year, 12).atEndOfMonth(); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(4, yq.getQuarterValue()); + assertSame(Quarter.Q4, yq.getQuarter()); + } + } + + @Test + void of_NullLocalDate_NullPointerException() { + LocalDate date = null; + assertThrows(NullPointerException.class, () -> { + YearQuarter.of(date); + }); + } + + // ================================ + // #endregion - of(LocalDate date) + // ================================ + + // ================================ + // #region - of(Date date) + // ================================ + + @SuppressWarnings("deprecation") + @ParameterizedTest + @ValueSource(ints = { + 2023, // 非闰年 + 2024, // 闰年 + 1, + 999999, + }) + void of_ValidDate_CreatesYearQuarter(int year) { + { + Date date = new Date(year - 1900, 1 - 1, 1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(1, yq.getQuarterValue()); + assertSame(Quarter.Q1, yq.getQuarter()); + } + { + Date date = new Date(year - 1900, 3 - 1, 31, 23, 59, 59); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(1, yq.getQuarterValue()); + assertSame(Quarter.Q1, yq.getQuarter()); + } + { + Date date = new Date(year - 1900, 4 - 1, 1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(2, yq.getQuarterValue()); + assertSame(Quarter.Q2, yq.getQuarter()); + } + { + Date date = new Date(year - 1900, 6 - 1, 30, 23, 59, 59); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(2, yq.getQuarterValue()); + assertSame(Quarter.Q2, yq.getQuarter()); + } + { + Date date = new Date(year - 1900, 7 - 1, 1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(3, yq.getQuarterValue()); + assertSame(Quarter.Q3, yq.getQuarter()); + } + { + Date date = new Date(year - 1900, 9 - 1, 30, 23, 59, 59); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(3, yq.getQuarterValue()); + assertSame(Quarter.Q3, yq.getQuarter()); + } + { + Date date = new Date(year - 1900, 10 - 1, 1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(4, yq.getQuarterValue()); + assertSame(Quarter.Q4, yq.getQuarter()); + } + { + Date date = new Date(year - 1900, 12 - 1, 31, 23, 59, 59); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(4, yq.getQuarterValue()); + assertSame(Quarter.Q4, yq.getQuarter()); + } + } + + @Test + void of_NullDate_NullPointerException() { + Date date = null; + assertThrows(NullPointerException.class, () -> { + YearQuarter.of(date); + }); + } + + // ================================ + // #endregion - of(Date date) + // ================================ + + // ================================ + // #region - of(Calendar date) + // ================================ + + @ParameterizedTest + @ValueSource(ints = { + 2023, // 非闰年 + 2024, // 闰年 + 1, + 999999, + }) + void of_ValidCalendar_CreatesYearQuarter(int year) { + Calendar date = Calendar.getInstance(); + { + date.set(year, 1 - 1, 1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(1, yq.getQuarterValue()); + assertSame(Quarter.Q1, yq.getQuarter()); + } + { + date.set(year, 3 - 1, 31, 23, 59, 59); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(1, yq.getQuarterValue()); + assertSame(Quarter.Q1, yq.getQuarter()); + } + { + date.set(year, 4 - 1, 1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(2, yq.getQuarterValue()); + assertSame(Quarter.Q2, yq.getQuarter()); + } + { + date.set(year, 6 - 1, 30, 23, 59, 59); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(2, yq.getQuarterValue()); + assertSame(Quarter.Q2, yq.getQuarter()); + } + { + date.set(year, 7 - 1, 1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(3, yq.getQuarterValue()); + assertSame(Quarter.Q3, yq.getQuarter()); + } + { + date.set(year, 9 - 1, 30, 23, 59, 59); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(3, yq.getQuarterValue()); + assertSame(Quarter.Q3, yq.getQuarter()); + } + { + date.set(year, 10 - 1, 1); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(4, yq.getQuarterValue()); + assertSame(Quarter.Q4, yq.getQuarter()); + } + { + date.set(year, 12 - 1, 31, 23, 59, 59); + YearQuarter yq = YearQuarter.of(date); + assertEquals(year, yq.getYear()); + assertEquals(4, yq.getQuarterValue()); + assertSame(Quarter.Q4, yq.getQuarter()); + } + } + + @Test + void of_NullCalendar_NullPointerException() { + Calendar date = null; + assertThrows(NullPointerException.class, () -> { + YearQuarter.of(date); + }); + } + + // ================================ + // #endregion - of(Calendar date) + // ================================ + + // ================================ + // #region - of(YearMonth yearMonth) + // ================================ + + @ParameterizedTest + @ValueSource(ints = { + 2023, // 非闰年 + 2024, // 闰年 + Year.MIN_VALUE, + Year.MAX_VALUE, + }) + void of_ValidYearMonth_CreatesYearMnoth_Q1(int year) { + { + YearMonth yearMonth = YearMonth.of(year, 1); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(1, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q1, yearQuarter.getQuarter()); + } + { + YearMonth yearMonth = YearMonth.of(year, 2); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(1, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q1, yearQuarter.getQuarter()); + } + { + YearMonth yearMonth = YearMonth.of(year, 3); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(1, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q1, yearQuarter.getQuarter()); + } + } + + @ParameterizedTest + @ValueSource(ints = { + 2023, // 非闰年 + 2024, // 闰年 + Year.MIN_VALUE, + Year.MAX_VALUE, + }) + void of_ValidYearMonth_CreatesYearMnoth_Q2(int year) { + { + YearMonth yearMonth = YearMonth.of(year, 4); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(2, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q2, yearQuarter.getQuarter()); + } + { + YearMonth yearMonth = YearMonth.of(year, 5); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(2, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q2, yearQuarter.getQuarter()); + } + { + YearMonth yearMonth = YearMonth.of(year, 6); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(2, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q2, yearQuarter.getQuarter()); + } + } + + @ParameterizedTest + @ValueSource(ints = { + 2023, // 非闰年 + 2024, // 闰年 + Year.MIN_VALUE, + Year.MAX_VALUE, + }) + void of_ValidYearMonth_CreatesYearMnoth_Q3(int year) { + { + YearMonth yearMonth = YearMonth.of(year, 7); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(3, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q3, yearQuarter.getQuarter()); + } + { + YearMonth yearMonth = YearMonth.of(year, 8); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(3, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q3, yearQuarter.getQuarter()); + } + { + YearMonth yearMonth = YearMonth.of(year, 9); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(3, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q3, yearQuarter.getQuarter()); + } + } + + @ParameterizedTest + @ValueSource(ints = { + 2023, // 非闰年 + 2024, // 闰年 + Year.MIN_VALUE, + Year.MAX_VALUE, + }) + void of_ValidYearMonth_CreatesYearMnoth_Q4(int year) { + { + YearMonth yearMonth = YearMonth.of(year, 10); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(4, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q4, yearQuarter.getQuarter()); + } + { + YearMonth yearMonth = YearMonth.of(year, 11); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(4, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q4, yearQuarter.getQuarter()); + } + { + YearMonth yearMonth = YearMonth.of(year, 12); + YearQuarter yearQuarter = YearQuarter.of(yearMonth); + assertEquals(year, yearQuarter.getYear()); + assertEquals(4, yearQuarter.getQuarterValue()); + assertSame(Quarter.Q4, yearQuarter.getQuarter()); + } + } + + @ParameterizedTest + @ValueSource(ints = { + 2023, // 非闰年 + 2024, // 闰年 + Year.MIN_VALUE, + Year.MAX_VALUE, + }) + void of_NullYearMonth_CreatesYearMnoth_Q4(int year) { + YearMonth yearMonth = null; + assertThrows(NullPointerException.class, + () -> YearQuarter.of(yearMonth)); + } + + // ================================ + // #endregion - of(YearMonth yearMonth) + // ================================ + + // ================================ + // #region - firstDate & lastDate + // ================================ + + @ParameterizedTest + @ValueSource(ints = { 1949, 1990, 2000, 2008, 2023, 2024, Year.MIN_VALUE, Year.MAX_VALUE }) + void test_getFirstDate_And_getLastDate(int year) { + { + final int quarterValue = 1; + YearQuarter yearQuarter = YearQuarter.of(year, quarterValue); + + LocalDate expectedFirstDate = LocalDate.of(year, 1, 1); + log.info("{} - expectedFirstDate: {}", yearQuarter, expectedFirstDate); + LocalDate expectedLastDate = LocalDate.of(year, 3, 31); + log.info("{} - expectedLastDate: {}", yearQuarter, expectedLastDate); + + assertEquals(expectedFirstDate, yearQuarter.firstDate()); + assertEquals(expectedLastDate, yearQuarter.lastDate()); + } + { + final int quarterValue = 2; + YearQuarter yearQuarter = YearQuarter.of(year, quarterValue); + + LocalDate expectedFirstDate = LocalDate.of(year, 4, 1); + log.info("{} - expectedFirstDate: {}", yearQuarter, expectedFirstDate); + LocalDate expectedLastDate = LocalDate.of(year, 6, 30); + log.info("{} - expectedLastDate: {}", yearQuarter, expectedLastDate); + + assertEquals(expectedFirstDate, yearQuarter.firstDate()); + assertEquals(expectedLastDate, yearQuarter.lastDate()); + } + { + final int quarterValue = 3; + YearQuarter yearQuarter = YearQuarter.of(year, quarterValue); + + LocalDate expectedFirstDate = LocalDate.of(year, 7, 1); + log.info("{} - expectedFirstDate: {}", yearQuarter, expectedFirstDate); + LocalDate expectedLastDate = LocalDate.of(year, 9, 30); + log.info("{} - expectedLastDate: {}", yearQuarter, expectedLastDate); + + assertEquals(expectedFirstDate, yearQuarter.firstDate()); + assertEquals(expectedLastDate, yearQuarter.lastDate()); + } + { + final int quarterValue = 4; + YearQuarter yearQuarter = YearQuarter.of(year, quarterValue); + + LocalDate expectedFirstDate = LocalDate.of(year, 10, 1); + log.info("{} - expectedFirstDate: {}", yearQuarter, expectedFirstDate); + LocalDate expectedLastDate = LocalDate.of(year, 12, 31); + log.info("{} - expectedLastDate: {}", yearQuarter, expectedLastDate); + + assertEquals(expectedFirstDate, yearQuarter.firstDate()); + assertEquals(expectedLastDate, yearQuarter.lastDate()); + } + } + + // ================================ + // #endregion - firstDate & lastDate + // ================================ + + // ================================ + // #region - firstYearMonth & lastYearMonth + // ================================ + + @ParameterizedTest + @ValueSource(ints = { 1949, 1990, 2000, 2008, 2023, 2024, Year.MIN_VALUE, Year.MAX_VALUE }) + void test_firstYearMonth_And_lastYearMonth(int year) { YearQuarter yq; yq = YearQuarter.of(year, Quarter.Q1); @@ -102,6 +811,50 @@ public class YearQuarterTests { assertEquals(YearMonth.of(year, 12), yq.lastYearMonth()); } + // ================================ + // #endregion - firstYearMonth & lastYearMonth + // ================================ + + // ================================ + // #region - firstMonth & lastMonth + // ================================ + + @ParameterizedTest + @ValueSource(ints = { 1949, 1990, 2000, 2008, 2023, 2024, Year.MIN_VALUE, Year.MAX_VALUE }) + void testFirstMonthAndLastMonth(int year) { + YearQuarter q1 = YearQuarter.of(year, 1); + assertEquals(1, q1.firstMonthValue()); + assertEquals(Month.JANUARY, q1.firstMonth()); + assertEquals(3, q1.lastMonthValue()); + assertEquals(Month.MARCH, q1.lastMonth()); + + YearQuarter q2 = YearQuarter.of(year, 2); + assertEquals(4, q2.firstMonthValue()); + assertEquals(Month.APRIL, q2.firstMonth()); + assertEquals(6, q2.lastMonthValue()); + assertEquals(Month.JUNE, q2.lastMonth()); + + YearQuarter q3 = YearQuarter.of(year, 3); + assertEquals(7, q3.firstMonthValue()); + assertEquals(Month.JULY, q3.firstMonth()); + assertEquals(9, q3.lastMonthValue()); + assertEquals(Month.SEPTEMBER, q3.lastMonth()); + + YearQuarter q4 = YearQuarter.of(year, 4); + assertEquals(10, q4.firstMonthValue()); + assertEquals(Month.OCTOBER, q4.firstMonth()); + assertEquals(12, q4.lastMonthValue()); + assertEquals(Month.DECEMBER, q4.lastMonth()); + } + + // ================================ + // #endregion - firstMonth & lastMonth + // ================================ + + // ================================ + // #region - compareTo + // ================================ + @Test void testCompareTo() { int year1; @@ -124,23 +877,20 @@ public class YearQuarterTests { // 同年同季度 assertEquals(yearQuarter1, yearQuarter2); assertEquals(0, yearQuarter1.compareTo(yearQuarter2)); - } - else if (quarter1 < quarter2) { + } else if (quarter1 < quarter2) { assertNotEquals(yearQuarter1, yearQuarter2); assertTrue(yearQuarter1.isBefore(yearQuarter2)); assertFalse(yearQuarter1.isAfter(yearQuarter2)); assertFalse(yearQuarter2.isBefore(yearQuarter1)); assertTrue(yearQuarter2.isAfter(yearQuarter1)); - } - else if (quarter1 > quarter2) { + } else if (quarter1 > quarter2) { assertNotEquals(yearQuarter1, yearQuarter2); assertFalse(yearQuarter1.isBefore(yearQuarter2)); assertTrue(yearQuarter1.isAfter(yearQuarter2)); assertTrue(yearQuarter2.isBefore(yearQuarter1)); assertFalse(yearQuarter2.isAfter(yearQuarter1)); } - } - else { + } else { // 不同年 assertEquals(year1 - year2, yearQuarter1.compareTo(yearQuarter2)); assertNotEquals(0, yearQuarter1.compareTo(yearQuarter2)); @@ -150,8 +900,7 @@ public class YearQuarterTests { assertFalse(yearQuarter1.isAfter(yearQuarter2)); assertFalse(yearQuarter2.isBefore(yearQuarter1)); assertTrue(yearQuarter2.isAfter(yearQuarter1)); - } - else if (year1 > year2) { + } else if (year1 > year2) { assertNotEquals(yearQuarter1, yearQuarter2); assertFalse(yearQuarter1.isBefore(yearQuarter2)); assertTrue(yearQuarter1.isAfter(yearQuarter2)); @@ -162,4 +911,281 @@ public class YearQuarterTests { } } } + + // ================================ + // #endregion - compareTo + // ================================ + + @ParameterizedTest + @ValueSource(ints = { Year.MIN_VALUE + 25, Year.MAX_VALUE - 25, -1, 0, 1, 1949, 1990, 2000, 2008, 2023, 2024 }) + void testPlusQuartersAndMinusQuarters(int year) { + for (int quarter = 1; quarter <= 4; quarter++) { + YearQuarter yq1 = YearQuarter.of(year, quarter); + for (int quartersToAdd = -100; quartersToAdd <= 100; quartersToAdd++) { + YearQuarter plus = yq1.plusQuarters(quartersToAdd); + YearQuarter minus = yq1.minusQuarters(-quartersToAdd); + assertEquals(plus, minus); + + // offset: 表示自 公元 0000年以来,经历了多少季度。所以 0 表示 -0001,Q4; 1 表示 0000 Q1 + long offset = (year * 4L + quarter) + quartersToAdd; + if (offset > 0) { + assertEquals((offset - 1) / 4, plus.getYear()); + assertEquals(((offset - 1) % 4) + 1, plus.getQuarterValue()); + } else { + assertEquals((offset / 4 - 1), plus.getYear()); + assertEquals((4 + offset % 4), plus.getQuarterValue()); + } + } + } + } + + @ParameterizedTest + @ValueSource(ints = { Year.MIN_VALUE + 1, Year.MAX_VALUE - 1, -1, 0, 1, 1900, 1990, 2000, 2023, 2024 }) + void test_nextQuarter_And_lastQuarter(int year) { + int quarter; + + YearQuarter yq; + YearQuarter next; + YearQuarter last; + + quarter = 1; + yq = YearQuarter.of(year, quarter); + next = yq.nextQuarter(); + assertEquals(year, next.getYear()); + assertEquals(2, next.getQuarterValue()); + last = yq.lastQuarter(); + assertEquals(year - 1, last.getYear()); + assertEquals(4, last.getQuarterValue()); + + quarter = 2; + yq = YearQuarter.of(year, quarter); + next = yq.nextQuarter(); + assertEquals(year, next.getYear()); + assertEquals(3, next.getQuarterValue()); + last = yq.lastQuarter(); + assertEquals(year, last.getYear()); + assertEquals(1, last.getQuarterValue()); + + quarter = 3; + yq = YearQuarter.of(year, quarter); + next = yq.nextQuarter(); + assertEquals(year, next.getYear()); + assertEquals(4, next.getQuarterValue()); + last = yq.lastQuarter(); + assertEquals(year, last.getYear()); + assertEquals(2, last.getQuarterValue()); + + quarter = 4; + yq = YearQuarter.of(year, quarter); + next = yq.nextQuarter(); + assertEquals(year + 1, next.getYear()); + assertEquals(1, next.getQuarterValue()); + last = yq.lastQuarter(); + assertEquals(year, last.getYear()); + assertEquals(3, last.getQuarterValue()); + } + + @ParameterizedTest + @ValueSource(ints = { Year.MIN_VALUE + 100, Year.MAX_VALUE - 100, -1, 0, 1, 1949, 1990, 2000, 2008, 2023, 2024 }) + void test_PlusYearsAndMinusYears(int year) { + for (int yearToAdd = -100; yearToAdd <= 100; yearToAdd++) { + YearQuarter q1 = YearQuarter.of(year, Quarter.Q1); + YearQuarter plus = q1.plusYears(yearToAdd); + assertEquals(year + yearToAdd, plus.getYear()); + assertEquals(Quarter.Q1, plus.getQuarter()); + YearQuarter minus = q1.minusYears(yearToAdd); + assertEquals(Quarter.Q1, minus.getQuarter()); + assertEquals(year - yearToAdd, minus.getYear()); + + assertEquals(q1.plusYears(yearToAdd), q1.minusYears(-yearToAdd)); + } + } + + @ParameterizedTest + @ValueSource(ints = { Year.MIN_VALUE + 1, Year.MAX_VALUE - 1, -1, 0, 1, 1900, 1990, 2000, 2023, 2024 }) + void test_nextYear_And_lastYear(int year) { + int quarter; + + YearQuarter yq; + YearQuarter next; + YearQuarter last; + + quarter = 1; + yq = YearQuarter.of(year, quarter); + next = yq.nextYear(); + assertSame(Quarter.Q1, yq.getQuarter()); + assertEquals(year + 1, next.getYear()); + assertSame(Quarter.Q1, next.getQuarter()); + last = yq.lastYear(); + assertEquals(year - 1, last.getYear()); + assertSame(Quarter.Q1, last.getQuarter()); + + quarter = 2; + yq = YearQuarter.of(year, quarter); + next = yq.nextYear(); + assertSame(Quarter.Q2, yq.getQuarter()); + assertEquals(year + 1, next.getYear()); + assertSame(Quarter.Q2, next.getQuarter()); + last = yq.lastYear(); + assertEquals(year - 1, last.getYear()); + assertSame(Quarter.Q2, last.getQuarter()); + + quarter = 3; + yq = YearQuarter.of(year, quarter); + next = yq.nextYear(); + assertSame(Quarter.Q3, yq.getQuarter()); + assertEquals(year + 1, next.getYear()); + assertSame(Quarter.Q3, next.getQuarter()); + last = yq.lastYear(); + assertEquals(year - 1, last.getYear()); + assertSame(Quarter.Q3, last.getQuarter()); + + quarter = 4; + yq = YearQuarter.of(year, quarter); + next = yq.nextYear(); + assertSame(Quarter.Q4, yq.getQuarter()); + assertEquals(year + 1, next.getYear()); + assertSame(Quarter.Q4, next.getQuarter()); + last = yq.lastYear(); + assertEquals(year - 1, last.getYear()); + assertSame(Quarter.Q4, last.getQuarter()); + } + + @ParameterizedTest + @ValueSource(ints = { -1, 0, 1, 1900, 2000, 2023, 2024, Year.MAX_VALUE, Year.MIN_VALUE }) + void test_compareTo_sameYear(int year) { + YearQuarter yq1 = YearQuarter.of(year, 1); + YearQuarter yq2 = YearQuarter.of(year, 2); + YearQuarter yq3 = YearQuarter.of(year, 3); + YearQuarter yq4 = YearQuarter.of(year, 4); + + assertTrue(yq1.equals(YearQuarter.of(year, Quarter.Q1))); // NOSONAR + assertTrue(yq1.compareTo(yq1) == 0); // NOSONAR + assertTrue(yq1.compareTo(yq2) < 0); + assertTrue(yq1.compareTo(yq3) < 0); + assertTrue(yq1.compareTo(yq4) < 0); + + assertTrue(yq2.equals(YearQuarter.of(year, Quarter.Q2))); // NOSONAR + assertTrue(yq2.compareTo(yq1) > 0); + assertTrue(yq2.compareTo(yq2) == 0); // NOSONAR + assertTrue(yq2.compareTo(yq3) < 0); + assertTrue(yq2.compareTo(yq4) < 0); + + assertTrue(yq3.equals(YearQuarter.of(year, Quarter.Q3))); // NOSONAR + assertTrue(yq3.compareTo(yq1) > 0); + assertTrue(yq3.compareTo(yq2) > 0); + assertTrue(yq3.compareTo(yq3) == 0); // NOSONAR + assertTrue(yq3.compareTo(yq4) < 0); + + assertTrue(yq4.equals(YearQuarter.of(year, Quarter.Q4))); // NOSONAR + assertTrue(yq4.compareTo(yq1) > 0); + assertTrue(yq4.compareTo(yq2) > 0); + assertTrue(yq4.compareTo(yq3) > 0); + assertTrue(yq4.compareTo(yq4) == 0); // NOSONAR + } + + @ParameterizedTest + @ValueSource(ints = { -1, 0, 1, 1900, 2000, 2023, 2024, Year.MAX_VALUE, Year.MIN_VALUE }) + void test_isBefore_sameYear(int year) { + YearQuarter yq1 = YearQuarter.of(year, 1); + YearQuarter yq2 = YearQuarter.of(year, 2); + YearQuarter yq3 = YearQuarter.of(year, 3); + YearQuarter yq4 = YearQuarter.of(year, 4); + + assertFalse(yq1.isBefore(YearQuarter.of(year, Quarter.Q1))); + assertTrue(yq1.isBefore(yq2)); + assertTrue(yq1.isBefore(yq3)); + assertTrue(yq1.isBefore(yq4)); + + assertFalse(yq2.isBefore(yq1)); + assertFalse(yq2.isBefore(YearQuarter.of(year, Quarter.Q2))); + assertTrue(yq2.isBefore(yq3)); + assertTrue(yq2.isBefore(yq4)); + + assertFalse(yq3.isBefore(yq1)); + assertFalse(yq3.isBefore(yq2)); + assertFalse(yq3.isBefore(YearQuarter.of(year, Quarter.Q3))); + assertTrue(yq3.isBefore(yq4)); + + assertFalse(yq4.isBefore(yq1)); + assertFalse(yq4.isBefore(yq2)); + assertFalse(yq4.isBefore(yq3)); + assertFalse(yq4.isBefore(YearQuarter.of(year, Quarter.Q4))); + } + + @ParameterizedTest + @ValueSource(ints = { -1, 0, 1, 1900, 2000, 2023, 2024, Year.MAX_VALUE, Year.MIN_VALUE }) + void test_isAfter_sameYear(int year) { + YearQuarter yq1 = YearQuarter.of(year, 1); + YearQuarter yq2 = YearQuarter.of(year, 2); + YearQuarter yq3 = YearQuarter.of(year, 3); + YearQuarter yq4 = YearQuarter.of(year, 4); + + assertFalse(yq1.isAfter(YearQuarter.of(year, Quarter.Q1))); + assertFalse(yq1.isAfter(yq2)); + assertFalse(yq1.isAfter(yq3)); + assertFalse(yq1.isAfter(yq4)); + + assertTrue(yq2.isAfter(yq1)); + assertFalse(yq2.isAfter(YearQuarter.of(year, Quarter.Q2))); + assertFalse(yq2.isAfter(yq3)); + assertFalse(yq2.isAfter(yq4)); + + assertTrue(yq3.isAfter(yq1)); + assertTrue(yq3.isAfter(yq2)); + assertFalse(yq3.isAfter(YearQuarter.of(year, Quarter.Q3))); + assertFalse(yq3.isAfter(yq4)); + + assertTrue(yq4.isAfter(yq1)); + assertTrue(yq4.isAfter(yq2)); + assertTrue(yq4.isAfter(yq3)); + assertFalse(yq4.isAfter(YearQuarter.of(year, Quarter.Q4))); + } + + @Test + void test_compareTo_null() { + YearQuarter yq = YearQuarter.of(2024, 4); + assertThrows(NullPointerException.class, + () -> yq.compareTo(null)); + assertThrows(NullPointerException.class, + () -> yq.isBefore(null)); + assertThrows(NullPointerException.class, + () -> yq.isAfter(null)); + assertNotEquals(null, yq); + } + + @ParameterizedTest + @ValueSource(ints = { -1, 0, 1, 1900, 2000, 2023, 2024, Year.MAX_VALUE - 1, Year.MIN_VALUE + 1 }) + void test_compareTo_differentYear(int year) { + for (int quarter1 = 1; quarter1 <= 4; quarter1++) { + YearQuarter yq = YearQuarter.of(year, quarter1); + for (int quarter2 = 1; quarter2 <= 4; quarter2++) { + // gt + assertTrue(yq.compareTo(YearQuarter.of(year + 1, quarter2)) < 0); + assertTrue(yq.isBefore(YearQuarter.of(year + 1, quarter2))); + assertTrue(YearQuarter.of(year + 1, quarter2).compareTo(yq) > 0); + assertTrue(YearQuarter.of(year + 1, quarter2).isAfter(yq)); + // lt + assertTrue(yq.compareTo(YearQuarter.of(year - 1, quarter2)) > 0); + assertTrue(yq.isAfter(YearQuarter.of(year - 1, quarter2))); + assertTrue(YearQuarter.of(year - 1, quarter2).compareTo(yq) < 0); + assertTrue(YearQuarter.of(year - 1, quarter2).isBefore(yq)); + } + } + } + + @ParameterizedTest + @ValueSource(ints = { -1, 0, 1, 1900, 2000, 2023, 2024, Year.MAX_VALUE, Year.MIN_VALUE }) + void test_min_And_max_sameYear(int year) { + YearQuarter yq1 = YearQuarter.of(year, 1); + YearQuarter anotherYq1 = YearQuarter.of(year, 1); + + assertEquals(yq1, YearQuarter.max(yq1, anotherYq1)); + assertEquals(yq1, YearQuarter.min(yq1, anotherYq1)); + + YearQuarter yq2 = YearQuarter.of(year, 2); + assertEquals(yq2, YearQuarter.max(yq1, yq2)); + assertEquals(yq1, YearQuarter.min(yq1, yq2)); + + } } From 1b2978fb060caa4f0b3e73c1cf4a87e932d07c49 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Fri, 27 Dec 2024 15:24:19 +0800 Subject: [PATCH 02/24] =?UTF-8?q?=E8=A1=A5=E5=85=85=20ID=20=E7=94=9F?= =?UTF-8?q?=E6=88=90=E5=99=A8=E7=9A=84=20Javadoc=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plusone/commons/util/IdGenerator.java | 14 ++++++++++--- .../zhouxy/plusone/commons/util/IdWorker.java | 21 ++++++++++++++++++- .../commons/util/SnowflakeIdGenerator.java | 5 +---- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/IdGenerator.java b/src/main/java/xyz/zhouxy/plusone/commons/util/IdGenerator.java index 66051a9..2bf8276 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/IdGenerator.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/IdGenerator.java @@ -21,9 +21,17 @@ import java.util.Objects; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import com.google.common.annotations.Beta; - -@Beta +/** + * ID 生成器 + * + *

+ * 生成 UUID 和 修改版雪花ID(Seata 版本) + *

+ * + * @see UUID + * @see IdWorker + * @author ZhouXY + */ public class IdGenerator { // ===== UUID ===== diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/IdWorker.java b/src/main/java/xyz/zhouxy/plusone/commons/util/IdWorker.java index e806cb2..20b59bc 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/IdWorker.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/IdWorker.java @@ -24,7 +24,26 @@ import java.util.concurrent.atomic.AtomicLong; import xyz.zhouxy.plusone.commons.exception.system.NoAvailableMacFoundException; - +/** + * Seata 提供的修改版雪花ID。 + *

+ * 大体思路为: + *

    + *
  1. 每个机器线程安全地生成序列,前面加上机器的id,这样就不会与其它机器的id相冲突。
  2. + *
  3. 时间戳作为序列的“预留位”,它更像是应用启动时最开始的序列的一部分,在一个时间戳里生成 4096 个 id 之后,直接生成下一个时间戳的 id。
  4. + *
+ *

+ *

+ * 详情见以下介绍: + *

+ *

+ * @author ZhouXY + */ public class IdWorker { /** diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java b/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java index c89a4bc..da9f71e 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java @@ -18,12 +18,9 @@ package xyz.zhouxy.plusone.commons.util; import java.util.concurrent.TimeUnit; -import com.google.common.annotations.Beta; - /** - * Twitter_Snowflake + * Twitter 版雪花算法 */ -@Beta public class SnowflakeIdGenerator { // ==============================Fields=========================================== From cd9a9da7ba3c2f69b77d11d6301640a5c81064da Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Fri, 27 Dec 2024 15:47:31 +0800 Subject: [PATCH 03/24] =?UTF-8?q?=E5=88=A0=E9=99=A4=20SafeConcurrentHashMa?= =?UTF-8?q?p=E3=80=82=E6=B3=A8=E6=84=8F=E4=BD=BF=E7=94=A8=20ConcurrentHash?= =?UTF-8?q?Map#computeIfAbsent=20=E6=96=B9=E6=B3=95=E6=97=B6=EF=BC=8Cmappi?= =?UTF-8?q?ngFunction=20=E9=87=8C=E4=B8=8D=E8=A6=81=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E8=AF=A5=20map=20=E7=9A=84=20computeIfAbsent=20=E5=8D=B3?= =?UTF-8?q?=E5=8F=AF=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xyz/zhouxy/plusone/commons/base/JRE.java | 63 --------- .../collection/AbstractMapWrapper.java | 9 +- .../collection/SafeConcurrentHashMap.java | 121 ------------------ .../commons/util/ConcurrentHashMapTools.java | 74 ----------- 4 files changed, 1 insertion(+), 266 deletions(-) delete mode 100644 src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java delete mode 100644 src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java delete mode 100644 src/main/java/xyz/zhouxy/plusone/commons/util/ConcurrentHashMapTools.java diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java b/src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java deleted file mode 100644 index 957d309..0000000 --- a/src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package xyz.zhouxy.plusone.commons.base; - -import xyz.zhouxy.plusone.commons.util.StringTools; - -/** - * JRE version - */ -public class JRE { - - private static final int JAVA_8 = 8; - - public static final int CURRENT_VERSION = getJre(); - - public static boolean isJava8() { - return CURRENT_VERSION == JAVA_8; - } - - private static int getJre() { - String version = System.getProperty("java.version"); - boolean isNotBlank = StringTools.isNotBlank(version); - if (isNotBlank && version.startsWith("1.8")) { - return JAVA_8; - } - // if JRE version is 9 or above, we can get version from - // java.lang.Runtime.version() - try { - return getMajorVersion(version); - } catch (Exception e) { - // assuming that JRE version is 8. - } - // default java 8 - return JAVA_8; - } - - private static int getMajorVersion(String version) { - if (version.startsWith("1.")) { - return Integer.parseInt(version.substring(2, 3)); - } else { - int dotIndex = version.indexOf("."); - return (dotIndex != -1) ? Integer.parseInt(version.substring(0, dotIndex)) : Integer.parseInt(version); - } - } - - private JRE() { - throw new IllegalStateException("Utility class"); - } -} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java b/src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java index 93b4242..04a974b 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java @@ -23,7 +23,6 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Function; @@ -33,7 +32,6 @@ import javax.annotation.Nullable; import com.google.common.annotations.Beta; import xyz.zhouxy.plusone.commons.util.AssertTools; -import xyz.zhouxy.plusone.commons.util.ConcurrentHashMapTools; /** * AbstractMapWrapper @@ -159,12 +157,7 @@ public abstract class AbstractMapWrapper) this.map, key, func); - } else { - return this.map.computeIfAbsent(key, func); - } + return this.map.computeIfAbsent(key, func); } public final Map exportMap() { diff --git a/src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java b/src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java deleted file mode 100644 index 0b60882..0000000 --- a/src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package xyz.zhouxy.plusone.commons.collection; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; - -import javax.annotation.concurrent.ThreadSafe; - -import xyz.zhouxy.plusone.commons.base.JRE; -import xyz.zhouxy.plusone.commons.util.ConcurrentHashMapTools; - -/** - * SafeConcurrentHashMap - * - *

- * Java 8 的 {@link ConcurrentHashMap#computeIfAbsent(Object, Function)} 方法有 bug, - * 使用 Java 8 时,可使用这个类进行替换。 - * - * @author ZhouXY - * @since 1.0 - * @see ConcurrentHashMap - * @see ConcurrentHashMapTools#computeIfAbsentForJava8(ConcurrentHashMap, Object, Function) - */ -@ThreadSafe -public class SafeConcurrentHashMap extends ConcurrentHashMap { - - private static final long serialVersionUID = 4352954948768449595L; - - /** - * Creates a new, empty map with the default initial table size (16). - */ - public SafeConcurrentHashMap() { - } - - /** - * Creates a new, empty map with an initial table size - * accommodating the specified number of elements without the need - * to dynamically resize. - * - * @param initialCapacity The implementation performs internal - * sizing to accommodate this many elements. - * @throws IllegalArgumentException if the initial capacity of - * elements is negative - */ - public SafeConcurrentHashMap(int initialCapacity) { - super(initialCapacity); - } - - /** - * Creates a new map with the same mappings as the given map. - * - * @param m the map - */ - public SafeConcurrentHashMap(Map m) { - super(m); - } - - /** - * Creates a new, empty map with an initial table size based on - * the given number of elements ({@code initialCapacity}) and - * initial table density ({@code loadFactor}). - * - * @param initialCapacity the initial capacity. The implementation - * performs internal sizing to accommodate this many elements, - * given the specified load factor. - * @param loadFactor the load factor (table density) for - * establishing the initial table size - * @throws IllegalArgumentException if the initial capacity of - * elements is negative or the load factor is nonpositive - * @since 1.6 - */ - public SafeConcurrentHashMap(int initialCapacity, float loadFactor) { - super(initialCapacity, loadFactor); - } - - /** - * Creates a new, empty map with an initial table size based on - * the given number of elements ({@code initialCapacity}), table - * density ({@code loadFactor}), and number of concurrently - * updating threads ({@code concurrencyLevel}). - * - * @param initialCapacity the initial capacity. The implementation - * performs internal sizing to accommodate this many elements, - * given the specified load factor. - * @param loadFactor the load factor (table density) for - * establishing the initial table size - * @param concurrencyLevel the estimated number of concurrently - * updating threads. The implementation may use this value as - * a sizing hint. - * @throws IllegalArgumentException if the initial capacity is - * negative or the load factor or concurrencyLevel are - * nonpositive - */ - public SafeConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { - super(initialCapacity, loadFactor, concurrencyLevel); - } - - /** {@inheritDoc} */ - @Override - public V computeIfAbsent(K key, Function mappingFunction) { - return JRE.isJava8() - ? ConcurrentHashMapTools.computeIfAbsentForJava8(this, key, mappingFunction) - : super.computeIfAbsent(key, mappingFunction); - } -} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/ConcurrentHashMapTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/ConcurrentHashMapTools.java deleted file mode 100644 index c36ecf0..0000000 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/ConcurrentHashMapTools.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package xyz.zhouxy.plusone.commons.util; - -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; - -import xyz.zhouxy.plusone.commons.base.JRE; -import xyz.zhouxy.plusone.commons.collection.SafeConcurrentHashMap; - -/** - * ConcurrentHashMapTools - * - *

- * Java 8 的 {@link ConcurrentHashMap#computeIfAbsent(Object, Function)} 方法有 bug, - * 可使用这个工具类的 {@link computeIfAbsentForJava8} 进行替换。 - * - *

- * NOTE: 方法来自Dubbo,见:issues#2349 - * - * @author ZhouXY - * @since 1.0 - * @see ConcurrentHashMap - * @see SafeConcurrentHashMap - */ -public class ConcurrentHashMapTools { - - public static V computeIfAbsent( - ConcurrentHashMap map, final K key, // NOSONAR - final Function mappingFunction) { - Objects.requireNonNull(map, "map"); - return JRE.isJava8() - ? computeIfAbsentForJava8(map, key, mappingFunction) - : map.computeIfAbsent(key, mappingFunction); - } - - public static V computeIfAbsentForJava8( - ConcurrentHashMap map, final K key, // NOSONAR - final Function mappingFunction) { - Objects.requireNonNull(key); - Objects.requireNonNull(mappingFunction); - V v = map.get(key); - if (null == v) { - v = mappingFunction.apply(key); - if (null == v) { - return null; - } - final V res = map.putIfAbsent(key, v); - if (null != res) { - return res; - } - } - return v; - } - - private ConcurrentHashMapTools() { - throw new IllegalStateException("Utility class"); - } -} From fb5ff43ed66f546988ff89b4b91bfacb6eb75e38 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Fri, 27 Dec 2024 15:50:45 +0800 Subject: [PATCH 04/24] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20ProgressOfTesting.tx?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index e757a12..fe401c4 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,7 +1,7 @@ -[ ] 未开始测试 - 15 (21.43%) -[-] 测试未完成 - 10 (14.29%) -[Y] 测试完成 - 24 (34.29%) -[x] 无需测试 - 21 (30.00%) +[ ] 未开始测试 - 13 (19.40%) +[-] 测试未完成 - 9 (13.43%) +[Y] 测试完成 - 24 (35.82%) +[x] 无需测试 - 21 (31.34%) xyz.zhouxy.plusone.commons ├───annotation @@ -20,7 +20,6 @@ xyz.zhouxy.plusone.commons │ IWithCode.java [Y] │ IWithIntCode.java [Y] │ IWithLongCode.java [Y] - │ JRE.java [ ] │ LongRef.java [Y] │ Ref.java [Y] │ @@ -28,7 +27,6 @@ xyz.zhouxy.plusone.commons │ AbstractMapWrapper.java [ ] │ CollectionTools.java [Y] │ MapWrapper.java [ ] - │ SafeConcurrentHashMap.java [ ] │ ├───constant │ PatternConsts.java [ ] @@ -86,7 +84,6 @@ xyz.zhouxy.plusone.commons ArrayTools.java [-] AssertTools.java [Y] BigDecimals.java [Y] - ConcurrentHashMapTools.java [-] DateTimeTools.java [-] Enumeration.java [Y] EnumTools.java [Y] From a887771565ca9c9e91c295561942256c5d7bfa39 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Fri, 27 Dec 2024 16:32:10 +0800 Subject: [PATCH 05/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=20ID=20=E7=94=9F?= =?UTF-8?q?=E6=88=90=E5=99=A8=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 10 +-- .../commons/util/SnowflakeIdGenerator.java | 77 +++++++++---------- .../commons/util/AssertToolsTests.java | 3 +- .../commons/util/IdGeneratorTests.java | 65 ++++++++++++++++ 4 files changed, 107 insertions(+), 48 deletions(-) create mode 100644 src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index fe401c4..7223d75 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,6 +1,6 @@ -[ ] 未开始测试 - 13 (19.40%) +[ ] 未开始测试 - 10 (14.93%) [-] 测试未完成 - 9 (13.43%) -[Y] 测试完成 - 24 (35.82%) +[Y] 测试完成 - 27 (40.30%) [x] 无需测试 - 21 (31.34%) xyz.zhouxy.plusone.commons @@ -87,12 +87,12 @@ xyz.zhouxy.plusone.commons DateTimeTools.java [-] Enumeration.java [Y] EnumTools.java [Y] - IdGenerator.java [ ] - IdWorker.java [ ] + IdGenerator.java [Y] + IdWorker.java [Y] Numbers.java [Y] OptionalTools.java [Y] RandomTools.java [ ] RegexTools.java [ ] - SnowflakeIdGenerator.java [ ] + SnowflakeIdGenerator.java [Y] StringTools.java [Y] TreeBuilder.java [Y] diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java b/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java index da9f71e..c8af751 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java @@ -64,9 +64,6 @@ public class SnowflakeIdGenerator { /** 上次生成 ID 的时间截 */ private long lastTimestamp = -1L; - /** 锁对象 */ - private final Object lock = new Object(); - // ==============================Constructors===================================== /** @@ -90,51 +87,47 @@ public class SnowflakeIdGenerator { * * @return SnowflakeId */ - public long nextId() { - long timestamp; - synchronized (lock) { - timestamp = timeGen(); + public synchronized long nextId() { + long timestamp = timeGen(); - // 发生了回拨,此刻时间小于上次发号时间 - if (timestamp < lastTimestamp) { - long offset = lastTimestamp - timestamp; - if (offset <= 5) { - // 时间偏差大小小于5ms,则等待两倍时间 - try { - TimeUnit.MILLISECONDS.sleep(offset << 1); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException(e); - } - timestamp = timeGen(); - if (timestamp < lastTimestamp) { - // 还是小于,抛异常上报 - throwClockBackwardsEx(lastTimestamp, timestamp); - } - } else { + // 发生了回拨,此刻时间小于上次发号时间 + if (timestamp < lastTimestamp) { + long offset = lastTimestamp - timestamp; + if (offset <= 5) { + // 时间偏差大小小于5ms,则等待两倍时间 + try { + TimeUnit.MILLISECONDS.sleep(offset << 1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException(e); + } + timestamp = timeGen(); + if (timestamp < lastTimestamp) { + // 还是小于,抛异常上报 throwClockBackwardsEx(lastTimestamp, timestamp); } + } else { + throwClockBackwardsEx(lastTimestamp, timestamp); } - - // 如果是同一时间生成的,则进行毫秒内序列 - if (lastTimestamp == timestamp) { - sequence = (sequence + 1) & SEQUENCE_MASK; - // 毫秒内序列溢出 - if (sequence == 0) { - // 阻塞到下一个毫秒,获得新的时间戳 - timestamp = tilNextMillis(lastTimestamp); - } - } - // 时间戳改变,毫秒内序列重置 - else { - sequence = 0L; - } - - // 上次生成 ID 的时间截 - lastTimestamp = timestamp; - } + // 如果是同一时间生成的,则进行毫秒内序列 + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & SEQUENCE_MASK; + // 毫秒内序列溢出 + if (sequence == 0) { + // 阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } + // 时间戳改变,毫秒内序列重置 + else { + sequence = 0L; + } + + // 上次生成 ID 的时间截 + lastTimestamp = timestamp; + // 移位并通过或运算拼到一起组成64位的ID return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | datacenterIdAndWorkerId | sequence; } diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java index 6623b9c..b6ea20e 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java @@ -925,9 +925,10 @@ public class AssertToolsTests { // #region - Condition + static final class MyException extends RuntimeException {} + @Test void testCheckCondition() { - class MyException extends RuntimeException {} AssertTools.checkCondition(true, MyException::new); diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java new file mode 100644 index 0000000..55edba9 --- /dev/null +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java @@ -0,0 +1,65 @@ +package xyz.zhouxy.plusone.commons.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; + +import cn.hutool.core.collection.ConcurrentHashSet; + +public class IdGeneratorTests { + + final ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); + + @Test + void testSnowflakeIdGenerator() { // NOSONAR + final SnowflakeIdGenerator snowflake = new SnowflakeIdGenerator(0, 0); + final Set ids = new ConcurrentHashSet<>(); + for (int i = 0; i < 10000; i++) { + executor.execute(() -> { + for (int j = 0; j < 50000; j++) { + if (false == ids.add(snowflake.nextId())) { + throw new RuntimeException("重复ID!"); + } + } + }); + } + } + + @Test + void testIdWorker() { // NOSONAR + final IdWorker idWorker = new IdWorker(0L); + final Set ids = new ConcurrentHashSet<>(); + for (int i = 0; i < 10000; i++) { + executor.execute(() -> { + for (int j = 0; j < 50000; j++) { + if (false == ids.add(idWorker.nextId())) { + throw new RuntimeException("重复ID!"); + } + } + }); + executor.execute(() -> { + for (int j = 0; j < 50000; j++) { + if (false == ids.add(IdGenerator.nextSnowflakeId(0))) { + throw new RuntimeException("重复ID!"); + } + } + }); + } + } + + @Test + void testToSimpleString() { + UUID id = UUID.randomUUID(); + assertEquals(id.toString().replaceAll("-", ""), + IdGenerator.toSimpleString(id)); + } + +} From 76f612f2ccdd9e8b9fe6df6922f4c81425dd4510 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Fri, 27 Dec 2024 17:07:11 +0800 Subject: [PATCH 06/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=E8=BA=AB=E4=BB=BD?= =?UTF-8?q?=E8=AF=81=E5=8F=B7=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/Chinese2ndGenIDCardNumber.java | 2 +- .../plusone/commons/model/IDCardNumber.java | 2 +- .../model/Chinese2ndGenIDCardNumberTests.java | 59 +++++++++++-------- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java b/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java index d43fa6f..4e1fed2 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java @@ -226,5 +226,5 @@ public class Chinese2ndGenIDCardNumber implements IDCardNumber, Serializable { return Objects.equals(value, other.value); } - private static final long serialVersionUID = 20241202095400L; + private static final long serialVersionUID = 8390082242712103716L; } diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/IDCardNumber.java b/src/main/java/xyz/zhouxy/plusone/commons/model/IDCardNumber.java index 76dcf1a..3e7c56d 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/model/IDCardNumber.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/model/IDCardNumber.java @@ -43,7 +43,7 @@ public interface IDCardNumber { LocalDate getBirthDate(); /** 计算年龄 */ - default int calculateAge() { + default int getAge() { LocalDate now = LocalDate.now(); return Period.between(getBirthDate(), now).getYears(); } diff --git a/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java b/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java index 40bd5dc..6e68cff 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java @@ -17,35 +17,27 @@ package xyz.zhouxy.plusone.commons.model; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.LocalDate; import java.time.format.DateTimeParseException; -import java.util.regex.Matcher; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import lombok.extern.slf4j.Slf4j; -import xyz.zhouxy.plusone.commons.constant.PatternConsts; @Slf4j public class Chinese2ndGenIDCardNumberTests { @Test - void testPattern() { - Matcher matcher = PatternConsts.CHINESE_2ND_ID_CARD_NUMBER.matcher("11010520000101111X"); - assertTrue(matcher.matches()); - for (int i = 0; i < matcher.groupCount(); i++) { - log.info("{}: {}", i, matcher.group(i)); - } - } - - @Test - void test() { + void testOf_success() { Chinese2ndGenIDCardNumber idCardNumber = Chinese2ndGenIDCardNumber.of("11010520000101111X"); assertEquals("11010520000101111X", idCardNumber.value()); assertEquals(LocalDate.of(2000, 1, 1), idCardNumber.getBirthDate()); - assertEquals(Gender.MALE, idCardNumber.getGender()); + assertSame(Gender.MALE, idCardNumber.getGender()); assertEquals("110105", idCardNumber.getCountyCode()); assertEquals("110105000000", idCardNumber.getFullCountyCode()); @@ -57,17 +49,38 @@ public class Chinese2ndGenIDCardNumberTests { assertEquals("北京", idCardNumber.getProvinceName()); - assertThrows(IllegalArgumentException.class, - () -> Chinese2ndGenIDCardNumber.of("1101520000101111")); + assertEquals("1***************1X", idCardNumber.toDesensitizedString()); + assertEquals("110***********111X", idCardNumber.toDesensitizedString(3, 4)); + assertEquals("11############111X", idCardNumber.toDesensitizedString('#', 2, 4)); + } - assertThrows(IllegalArgumentException.class, - () -> Chinese2ndGenIDCardNumber.of("11010520002101111X")); - - try { - Chinese2ndGenIDCardNumber.of("11010520002101111X"); - } - catch (IllegalArgumentException e) { - assertTrue(e.getCause() instanceof DateTimeParseException); + @Test + void testOf_blankValue() { + String[] strings = { null, "", " " }; + for (String value : strings) { + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, + () -> Chinese2ndGenIDCardNumber.of(value)); + assertEquals("二代居民身份证校验失败:号码为空", e.getMessage()); } } + @ParameterizedTest + @ValueSource(strings = { "1101520000101111", "110A0520000101111X", "110105220000101111X" }) + void testOf_mismatched(String value) { + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, + () -> Chinese2ndGenIDCardNumber.of(value)); + assertEquals("二代居民身份证校验失败:" + value, e.getMessage()); + } + + @Test + void testOf_wrongBirthDate() { + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, + () -> Chinese2ndGenIDCardNumber.of("11010520002101111X")); + assertTrue(e.getCause() instanceof DateTimeParseException); + } + + @Test + void testOf_wrongProvince() { + assertThrows(IllegalArgumentException.class, + () -> Chinese2ndGenIDCardNumber.of("99010520000101111X")); + } } From 36823c11818d3961e710a83ec8cdb92cc67b6ff2 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Fri, 27 Dec 2024 17:09:28 +0800 Subject: [PATCH 07/24] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20ProgressOfTesting.tx?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index 7223d75..1895c4d 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,7 +1,7 @@ -[ ] 未开始测试 - 10 (14.93%) -[-] 测试未完成 - 9 (13.43%) -[Y] 测试完成 - 27 (40.30%) -[x] 无需测试 - 21 (31.34%) +[ ] 未开始测试 - 8 (11.94%) +[-] 测试未完成 - 8 (11.94%) +[Y] 测试完成 - 29 (43.28%) +[x] 无需测试 - 22 (32.84%) xyz.zhouxy.plusone.commons ├───annotation @@ -60,9 +60,9 @@ xyz.zhouxy.plusone.commons │ ToOptionalFunction.java [x] │ ├───model - │ │ Chinese2ndGenIDCardNumber.java [-] - │ │ Gender.java [ ] - │ │ IDCardNumber.java [ ] + │ │ Chinese2ndGenIDCardNumber.java [Y] + │ │ Gender.java [x] + │ │ IDCardNumber.java [Y] │ │ ValidatableStringRecord.java [-] │ │ │ └───dto From f1491117de8a91a70da5239a285150138604d1c4 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Fri, 27 Dec 2024 18:05:59 +0800 Subject: [PATCH 08/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=20RandomTools=20?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 6 +- .../plusone/commons/util/RandomTools.java | 29 +++++- .../commons/util/RandomToolsTests.java | 89 +++++++++++++++++++ 3 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index 1895c4d..0af1f01 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,6 +1,6 @@ -[ ] 未开始测试 - 8 (11.94%) +[ ] 未开始测试 - 7 (10.45%) [-] 测试未完成 - 8 (11.94%) -[Y] 测试完成 - 29 (43.28%) +[Y] 测试完成 - 30 (44.78%) [x] 无需测试 - 22 (32.84%) xyz.zhouxy.plusone.commons @@ -91,7 +91,7 @@ xyz.zhouxy.plusone.commons IdWorker.java [Y] Numbers.java [Y] OptionalTools.java [Y] - RandomTools.java [ ] + RandomTools.java [Y] RegexTools.java [ ] SnowflakeIdGenerator.java [Y] StringTools.java [Y] diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/RandomTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/RandomTools.java index f9bc4a2..07f54bf 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/RandomTools.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/RandomTools.java @@ -16,6 +16,7 @@ package xyz.zhouxy.plusone.commons.util; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Objects; import java.util.Random; @@ -23,14 +24,40 @@ import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; +/** + * 随机工具类 + *

+ * 建议调用方自行维护 Random 对象 + *

+ * @author ZhouXY + */ public final class RandomTools { - public static final SecureRandom DEFAULT_SECURE_RANDOM = new SecureRandom(); + private static final SecureRandom DEFAULT_SECURE_RANDOM; + + static { + SecureRandom secureRandom = null; + try { + secureRandom = SecureRandom.getInstanceStrong(); // 获取高强度安全随机数生成器 + } + catch (NoSuchAlgorithmException e) { + secureRandom = new SecureRandom(); // 获取普通的安全随机数生成器 + } + DEFAULT_SECURE_RANDOM = secureRandom; + } public static final String CAPITAL_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static final String LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz"; public static final String NUMBERS = "0123456789"; + public static SecureRandom defaultSecureRandom() { + return DEFAULT_SECURE_RANDOM; + } + + public static ThreadLocalRandom currentThreadLocalRandom() { + return ThreadLocalRandom.current(); + } + /** * 使用传入的随机数生成器,生成指定长度的字符串 * diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java new file mode 100644 index 0000000..fcf5f44 --- /dev/null +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java @@ -0,0 +1,89 @@ +package xyz.zhouxy.plusone.commons.util; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.security.SecureRandom; +import java.util.Random; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("null") +public class RandomToolsTests { + + private static Random random; + private static SecureRandom secureRandom; + private static char[] sourceCharactersArray; + private static String sourceCharactersString; + + @BeforeAll + public static void setUp() { + random = new Random(); + secureRandom = new SecureRandom(); + sourceCharactersArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); + sourceCharactersString = "abcdefghijklmnopqrstuvwxyz"; + } + + @Test + public void randomStr_NullRandom_ThrowsException() { + assertThrows(IllegalArgumentException.class, + () -> RandomTools.randomStr(null, sourceCharactersArray, 5)); + assertThrows(IllegalArgumentException.class, + () -> RandomTools.randomStr(null, sourceCharactersString, 5)); + } + + @Test + public void randomStr_NullSourceCharacters_ThrowsException() { + assertThrows(IllegalArgumentException.class, () -> RandomTools.randomStr(random, (char[]) null, 5)); + assertThrows(IllegalArgumentException.class, () -> RandomTools.randomStr(random, (String) null, 5)); + } + + @Test + public void randomStr_NegativeLength_ThrowsException() { + assertThrows(IllegalArgumentException.class, () -> RandomTools.randomStr(random, sourceCharactersArray, -1)); + } + + @Test + public void randomStr_ZeroLength_ReturnsEmptyString() { + assertAll( + () -> assertEquals("", RandomTools.randomStr(random, sourceCharactersArray, 0)), + () -> assertEquals("", RandomTools.randomStr(random, sourceCharactersString, 0)), + () -> assertEquals("", RandomTools.randomStr(secureRandom, sourceCharactersArray, 0)), + () -> assertEquals("", RandomTools.randomStr(secureRandom, sourceCharactersString, 0))); + } + + @Test + public void randomStr_PositiveLength_ReturnsRandomString() { + assertAll( + () -> assertEquals(5, RandomTools.randomStr(random, sourceCharactersArray, 5).length()), + () -> assertEquals(5, RandomTools.randomStr(random, sourceCharactersString, 5).length()), + () -> assertEquals(5, RandomTools.randomStr(secureRandom, sourceCharactersArray, 5).length()), + () -> assertEquals(5, RandomTools.randomStr(secureRandom, sourceCharactersString, 5).length())); + } + + @Test + public void randomStr_ReturnsRandomString() { + String result = RandomTools.randomStr(sourceCharactersArray, 5); + assertEquals(5, result.length()); + } + + @Test + public void randomStr_StringSourceCharacters_ReturnsRandomString() { + String result = RandomTools.randomStr(sourceCharactersString, 5); + assertEquals(5, result.length()); + } + + @Test + public void secureRandomStr_ReturnsRandomString() { + String result = RandomTools.secureRandomStr(sourceCharactersArray, 5); + assertEquals(5, result.length()); + } + + @Test + public void secureRandomStr_StringSourceCharacters_ReturnsRandomString() { + String result = RandomTools.secureRandomStr(sourceCharactersString, 5); + assertEquals(5, result.length()); + } +} From d72a5d32556a97783b24dea46fd691343b2bc284 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sat, 28 Dec 2024 10:35:34 +0800 Subject: [PATCH 09/24] =?UTF-8?q?Chinese2ndGenIDCardNumber=20=E7=BB=A7?= =?UTF-8?q?=E6=89=BF=E8=87=AA=20ValidatableStringRecord=EF=BC=9B=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=20ValidatableStringRecord=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/Chinese2ndGenIDCardNumber.java | 69 +++++++------------ .../model/ValidatableStringRecord.java | 17 +++-- .../model/Chinese2ndGenIDCardNumberTests.java | 7 +- .../model/ValidatableStringRecordTests.java | 4 +- 4 files changed, 44 insertions(+), 53 deletions(-) diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java b/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java index 4e1fed2..f802468 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java @@ -16,11 +16,9 @@ package xyz.zhouxy.plusone.commons.model; -import java.io.Serializable; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Map; -import java.util.Objects; import java.util.regex.Matcher; import com.google.common.base.Strings; @@ -46,9 +44,9 @@ import xyz.zhouxy.plusone.commons.util.StringTools; */ @ValueObject @Immutable -public class Chinese2ndGenIDCardNumber implements IDCardNumber, Serializable { - - private final String value; +public class Chinese2ndGenIDCardNumber + extends ValidatableStringRecord + implements IDCardNumber { /** 省份编码 */ private final String provinceCode; @@ -63,57 +61,49 @@ public class Chinese2ndGenIDCardNumber implements IDCardNumber, Serializable { private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); - private Chinese2ndGenIDCardNumber(String value, String provinceCode, String cityCode, String countyCode, - Gender gender, LocalDate birthDate) { - this.value = value; - this.provinceCode = provinceCode; - this.cityCode = cityCode; - this.countyCode = countyCode; - this.gender = gender; - this.birthDate = birthDate; - } + private Chinese2ndGenIDCardNumber(String value) { + super(value.toUpperCase(), PatternConsts.CHINESE_2ND_ID_CARD_NUMBER, () -> "二代居民身份证校验失败:" + value); - public static Chinese2ndGenIDCardNumber of(final String value) { - AssertTools.checkArgument(StringTools.isNotBlank(value), "二代居民身份证校验失败:号码为空"); - final String idNumber = value.toUpperCase(); + final Matcher matcher = getMatcher(); - final Matcher matcher = PatternConsts.CHINESE_2ND_ID_CARD_NUMBER.matcher(idNumber); - AssertTools.checkArgument(matcher.matches(), () -> "二代居民身份证校验失败:" + value); + final String provinceCodeValue = matcher.group("province"); + AssertTools.checkArgument(Chinese2ndGenIDCardNumber.PROVINCE_CODES.containsKey(provinceCodeValue)); - final String provinceCode = matcher.group("province"); - AssertTools.checkArgument(Chinese2ndGenIDCardNumber.PROVINCE_CODES.containsKey(provinceCode)); + final String cityCodeValue = matcher.group("city"); + final String countyCodeValue = matcher.group("county"); - final String cityCode = matcher.group("city"); - final String countyCode = matcher.group("county"); - - final Gender gender; - final LocalDate birthDate; + final Gender genderValue; + final LocalDate birthDateValue; try { // 出生日期 final String birthDateStr = matcher.group("birthDate"); - birthDate = LocalDate.parse(birthDateStr, DATE_FORMATTER); + birthDateValue = LocalDate.parse(birthDateStr, DATE_FORMATTER); // 性别 final int genderCode = Integer.parseInt(matcher.group("gender")); - gender = genderCode % 2 == 0 ? Gender.FEMALE : Gender.MALE; + genderValue = genderCode % 2 == 0 ? Gender.FEMALE : Gender.MALE; } catch (Exception e) { throw new IllegalArgumentException(e); } - return new Chinese2ndGenIDCardNumber(idNumber, provinceCode, cityCode, countyCode, gender, birthDate); + this.provinceCode = provinceCodeValue; + this.cityCode = cityCodeValue; + this.countyCode = countyCodeValue; + this.gender = genderValue; + this.birthDate = birthDateValue; + } + + public static Chinese2ndGenIDCardNumber of(final String value) { + AssertTools.checkArgument(StringTools.isNotBlank(value), "二代居民身份证校验失败:号码为空"); + return new Chinese2ndGenIDCardNumber(value); } // ================================ // #region - reader methods // ================================ - @ReaderMethod - public String value() { - return value; - } - @ReaderMethod public String getProvinceCode() { return provinceCode; @@ -213,18 +203,11 @@ public class Chinese2ndGenIDCardNumber implements IDCardNumber, Serializable { @Override public int hashCode() { - return Objects.hashCode(value); + return super.hashCode(); } @Override public boolean equals(Object obj) { - if (this == obj) - return true; - if (!(obj instanceof Chinese2ndGenIDCardNumber)) - return false; - Chinese2ndGenIDCardNumber other = (Chinese2ndGenIDCardNumber) obj; - return Objects.equals(value, other.value); + return super.equals(obj); } - - private static final long serialVersionUID = 8390082242712103716L; } diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecord.java b/src/main/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecord.java index ce89ef5..c679a14 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecord.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecord.java @@ -23,6 +23,7 @@ import java.util.regex.Pattern; import javax.annotation.Nonnull; +import xyz.zhouxy.plusone.commons.annotation.ReaderMethod; import xyz.zhouxy.plusone.commons.util.AssertTools; /** @@ -31,8 +32,8 @@ import xyz.zhouxy.plusone.commons.util.AssertTools; * @author ZhouXY * @since 0.1.0 */ -public abstract class ValidatableStringRecord - implements Comparable { +public abstract class ValidatableStringRecord> + implements Comparable { @Nonnull private final String value; @@ -62,18 +63,19 @@ public abstract class ValidatableStringRecord * * @return 字符串(不为空) */ + @ReaderMethod public final String value() { return this.value; } @Override - public int compareTo(ValidatableStringRecord o) { - return this.value.compareTo(o.value); + public int compareTo(T o) { + return this.value.compareTo(o.value()); } @Override public int hashCode() { - return Objects.hash(value); + return Objects.hash(getClass(), value); } @Override @@ -84,8 +86,9 @@ public abstract class ValidatableStringRecord return false; if (getClass() != obj.getClass()) return false; - ValidatableStringRecord other = (ValidatableStringRecord) obj; - return Objects.equals(value, other.value); + @SuppressWarnings("unchecked") + T other = (T) obj; + return Objects.equals(value, other.value()); } @Override diff --git a/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java b/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java index 6e68cff..07421fe 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java @@ -51,7 +51,12 @@ public class Chinese2ndGenIDCardNumberTests { assertEquals("1***************1X", idCardNumber.toDesensitizedString()); assertEquals("110***********111X", idCardNumber.toDesensitizedString(3, 4)); - assertEquals("11############111X", idCardNumber.toDesensitizedString('#', 2, 4)); + assertEquals("110###############", idCardNumber.toDesensitizedString('#', 3, 0)); + assertEquals("11010520000101111X", idCardNumber.toDesensitizedString(10, 8)); + + assertThrows(IllegalArgumentException.class, () -> idCardNumber.toDesensitizedString(-1, 5)); + assertThrows(IllegalArgumentException.class, () -> idCardNumber.toDesensitizedString(5, -1)); + assertThrows(IllegalArgumentException.class, () -> idCardNumber.toDesensitizedString(10, 9)); } @Test diff --git a/src/test/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecordTests.java b/src/test/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecordTests.java index 6fd5209..be3eb22 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecordTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecordTests.java @@ -84,7 +84,7 @@ class User { } @ValueObject -class Email extends ValidatableStringRecord { +class Email extends ValidatableStringRecord { private Email(String value) { super(value, PatternConsts.EMAIL); } @@ -96,7 +96,7 @@ class Email extends ValidatableStringRecord { } @ValueObject -class Username extends ValidatableStringRecord { +class Username extends ValidatableStringRecord { private Username(String username) { super(username, PatternConsts.USERNAME); } From 9ccaa2d1d64ae8a0f705c6f32529a64e5ee8e9b7 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sat, 28 Dec 2024 10:41:37 +0800 Subject: [PATCH 10/24] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20ProgressOfTesting.tx?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index 0af1f01..e022231 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,6 +1,6 @@ [ ] 未开始测试 - 7 (10.45%) -[-] 测试未完成 - 8 (11.94%) -[Y] 测试完成 - 30 (44.78%) +[-] 测试未完成 - 7 (10.45%) +[Y] 测试完成 - 31 (46.27%) [x] 无需测试 - 22 (32.84%) xyz.zhouxy.plusone.commons @@ -63,7 +63,7 @@ xyz.zhouxy.plusone.commons │ │ Chinese2ndGenIDCardNumber.java [Y] │ │ Gender.java [x] │ │ IDCardNumber.java [Y] - │ │ ValidatableStringRecord.java [-] + │ │ ValidatableStringRecord.java [Y] │ │ │ └───dto │ PageResult.java [-] From 40302d83cfefd3b1118625bce0cf2049de86097e Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sat, 28 Dec 2024 22:27:00 +0800 Subject: [PATCH 11/24] =?UTF-8?q?=E5=88=A0=E9=99=A4=20SQL=20=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E5=99=A8=E3=80=82=E6=9A=82=E4=B8=8D=E8=80=83=E8=99=91?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E5=B7=A5=E5=85=B7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 14 +- .../org/apache/ibatis/jdbc/AbstractSQL.java | 740 ------------------ .../zhouxy/plusone/commons/sql/JdbcSql.java | 72 -- .../plusone/commons/sql/MyBatisSql.java | 73 -- .../xyz/zhouxy/plusone/commons/sql/SQL.java | 47 -- .../commons/sql/MyBatisSqlBuilderTests.java | 37 - 6 files changed, 4 insertions(+), 979 deletions(-) delete mode 100644 src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java delete mode 100644 src/main/java/xyz/zhouxy/plusone/commons/sql/JdbcSql.java delete mode 100644 src/main/java/xyz/zhouxy/plusone/commons/sql/MyBatisSql.java delete mode 100644 src/main/java/xyz/zhouxy/plusone/commons/sql/SQL.java delete mode 100644 src/test/java/xyz/zhouxy/plusone/commons/sql/MyBatisSqlBuilderTests.java diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index e022231..0db7d21 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,7 +1,7 @@ -[ ] 未开始测试 - 7 (10.45%) -[-] 测试未完成 - 7 (10.45%) -[Y] 测试完成 - 31 (46.27%) -[x] 无需测试 - 22 (32.84%) +[ ] 未开始测试 - 5 (7.94%) +[-] 测试未完成 - 5 (7.94%) +[Y] 测试完成 - 31 (49.21%) +[x] 无需测试 - 22 (34.92%) xyz.zhouxy.plusone.commons ├───annotation @@ -69,12 +69,6 @@ xyz.zhouxy.plusone.commons │ PageResult.java [-] │ PagingAndSortingQueryParams.java [-] │ PagingParams.java [-] - │ UnifiedResponse.java [-] - │ - ├───sql - │ JdbcSql.java [ ] - │ MyBatisSql.java [-] - │ SQL.java [ ] │ ├───time │ Quarter.java [Y] diff --git a/src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java b/src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java deleted file mode 100644 index 6e031c4..0000000 --- a/src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java +++ /dev/null @@ -1,740 +0,0 @@ -/* - * Copyright 2009-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.ibatis.jdbc; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * @author Clinton Begin - * @author Jeff Butler - * @author Adam Gent - * @author Kazuki Shimizu - */ -public abstract class AbstractSQL { - - private static final String AND = ") \nAND ("; - private static final String OR = ") \nOR ("; - - private final SQLStatement sql = new SQLStatement(); - - public abstract T getSelf(); - - public T UPDATE(String table) { - sql().statementType = SQLStatement.StatementType.UPDATE; - sql().tables.add(table); - return getSelf(); - } - - public T SET(String sets) { - sql().sets.add(sets); - return getSelf(); - } - - /** - * Sets the. - * - * @param sets - * the sets - * - * @return the t - * - * @since 3.4.2 - */ - public T SET(String... sets) { - sql().sets.addAll(Arrays.asList(sets)); - return getSelf(); - } - - public T INSERT_INTO(String tableName) { - sql().statementType = SQLStatement.StatementType.INSERT; - sql().tables.add(tableName); - return getSelf(); - } - - public T VALUES(String columns, String values) { - INTO_COLUMNS(columns); - INTO_VALUES(values); - return getSelf(); - } - - /** - * Into columns. - * - * @param columns - * the columns - * - * @return the t - * - * @since 3.4.2 - */ - public T INTO_COLUMNS(String... columns) { - sql().columns.addAll(Arrays.asList(columns)); - return getSelf(); - } - - /** - * Into values. - * - * @param values - * the values - * - * @return the t - * - * @since 3.4.2 - */ - public T INTO_VALUES(String... values) { - List list = sql().valuesList.get(sql().valuesList.size() - 1); - Collections.addAll(list, values); - return getSelf(); - } - - public T SELECT(String columns) { - sql().statementType = SQLStatement.StatementType.SELECT; - sql().select.add(columns); - return getSelf(); - } - - /** - * Select. - * - * @param columns - * the columns - * - * @return the t - * - * @since 3.4.2 - */ - public T SELECT(String... columns) { - sql().statementType = SQLStatement.StatementType.SELECT; - sql().select.addAll(Arrays.asList(columns)); - return getSelf(); - } - - public T SELECT_DISTINCT(String columns) { - sql().distinct = true; - SELECT(columns); - return getSelf(); - } - - /** - * Select distinct. - * - * @param columns - * the columns - * - * @return the t - * - * @since 3.4.2 - */ - public T SELECT_DISTINCT(String... columns) { - sql().distinct = true; - SELECT(columns); - return getSelf(); - } - - public T DELETE_FROM(String table) { - sql().statementType = SQLStatement.StatementType.DELETE; - sql().tables.add(table); - return getSelf(); - } - - public T FROM(String table) { - sql().tables.add(table); - return getSelf(); - } - - /** - * From. - * - * @param tables - * the tables - * - * @return the t - * - * @since 3.4.2 - */ - public T FROM(String... tables) { - sql().tables.addAll(Arrays.asList(tables)); - return getSelf(); - } - - public T JOIN(String join) { - sql().join.add(join); - return getSelf(); - } - - /** - * Join. - * - * @param joins - * the joins - * - * @return the t - * - * @since 3.4.2 - */ - public T JOIN(String... joins) { - sql().join.addAll(Arrays.asList(joins)); - return getSelf(); - } - - public T INNER_JOIN(String join) { - sql().innerJoin.add(join); - return getSelf(); - } - - /** - * Inner join. - * - * @param joins - * the joins - * - * @return the t - * - * @since 3.4.2 - */ - public T INNER_JOIN(String... joins) { - sql().innerJoin.addAll(Arrays.asList(joins)); - return getSelf(); - } - - public T LEFT_OUTER_JOIN(String join) { - sql().leftOuterJoin.add(join); - return getSelf(); - } - - /** - * Left outer join. - * - * @param joins - * the joins - * - * @return the t - * - * @since 3.4.2 - */ - public T LEFT_OUTER_JOIN(String... joins) { - sql().leftOuterJoin.addAll(Arrays.asList(joins)); - return getSelf(); - } - - public T RIGHT_OUTER_JOIN(String join) { - sql().rightOuterJoin.add(join); - return getSelf(); - } - - /** - * Right outer join. - * - * @param joins - * the joins - * - * @return the t - * - * @since 3.4.2 - */ - public T RIGHT_OUTER_JOIN(String... joins) { - sql().rightOuterJoin.addAll(Arrays.asList(joins)); - return getSelf(); - } - - public T OUTER_JOIN(String join) { - sql().outerJoin.add(join); - return getSelf(); - } - - /** - * Outer join. - * - * @param joins - * the joins - * - * @return the t - * - * @since 3.4.2 - */ - public T OUTER_JOIN(String... joins) { - sql().outerJoin.addAll(Arrays.asList(joins)); - return getSelf(); - } - - public T WHERE(String conditions) { - sql().where.add(conditions); - sql().lastList = sql().where; - return getSelf(); - } - - /** - * Where. - * - * @param conditions - * the conditions - * - * @return the t - * - * @since 3.4.2 - */ - public T WHERE(String... conditions) { - sql().where.addAll(Arrays.asList(conditions)); - sql().lastList = sql().where; - return getSelf(); - } - - public T OR() { - sql().lastList.add(OR); - return getSelf(); - } - - public T AND() { - sql().lastList.add(AND); - return getSelf(); - } - - public T GROUP_BY(String columns) { - sql().groupBy.add(columns); - return getSelf(); - } - - /** - * Group by. - * - * @param columns - * the columns - * - * @return the t - * - * @since 3.4.2 - */ - public T GROUP_BY(String... columns) { - sql().groupBy.addAll(Arrays.asList(columns)); - return getSelf(); - } - - public T HAVING(String conditions) { - sql().having.add(conditions); - sql().lastList = sql().having; - return getSelf(); - } - - /** - * Having. - * - * @param conditions - * the conditions - * - * @return the t - * - * @since 3.4.2 - */ - public T HAVING(String... conditions) { - sql().having.addAll(Arrays.asList(conditions)); - sql().lastList = sql().having; - return getSelf(); - } - - public T ORDER_BY(String columns) { - sql().orderBy.add(columns); - return getSelf(); - } - - /** - * Order by. - * - * @param columns - * the columns - * - * @return the t - * - * @since 3.4.2 - */ - public T ORDER_BY(String... columns) { - sql().orderBy.addAll(Arrays.asList(columns)); - return getSelf(); - } - - /** - * Set the limit variable string(e.g. {@code "#{limit}"}). - * - * @param variable - * a limit variable string - * - * @return a self instance - * - * @see #OFFSET(String) - * - * @since 3.5.2 - */ - public T LIMIT(String variable) { - sql().limit = variable; - sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT; - return getSelf(); - } - - /** - * Set the limit value. - * - * @param value - * an offset value - * - * @return a self instance - * - * @see #OFFSET(long) - * - * @since 3.5.2 - */ - public T LIMIT(int value) { - return LIMIT(String.valueOf(value)); - } - - /** - * Set the offset variable string(e.g. {@code "#{offset}"}). - * - * @param variable - * a offset variable string - * - * @return a self instance - * - * @see #LIMIT(String) - * - * @since 3.5.2 - */ - public T OFFSET(String variable) { - sql().offset = variable; - sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT; - return getSelf(); - } - - /** - * Set the offset value. - * - * @param value - * an offset value - * - * @return a self instance - * - * @see #LIMIT(int) - * - * @since 3.5.2 - */ - public T OFFSET(long value) { - return OFFSET(String.valueOf(value)); - } - - /** - * Set the fetch first rows variable string(e.g. {@code "#{fetchFirstRows}"}). - * - * @param variable - * a fetch first rows variable string - * - * @return a self instance - * - * @see #OFFSET_ROWS(String) - * - * @since 3.5.2 - */ - public T FETCH_FIRST_ROWS_ONLY(String variable) { - sql().limit = variable; - sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO; - return getSelf(); - } - - /** - * Set the fetch first rows value. - * - * @param value - * a fetch first rows value - * - * @return a self instance - * - * @see #OFFSET_ROWS(long) - * - * @since 3.5.2 - */ - public T FETCH_FIRST_ROWS_ONLY(int value) { - return FETCH_FIRST_ROWS_ONLY(String.valueOf(value)); - } - - /** - * Set the offset rows variable string(e.g. {@code "#{offset}"}). - * - * @param variable - * a offset rows variable string - * - * @return a self instance - * - * @see #FETCH_FIRST_ROWS_ONLY(String) - * - * @since 3.5.2 - */ - public T OFFSET_ROWS(String variable) { - sql().offset = variable; - sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO; - return getSelf(); - } - - /** - * Set the offset rows value. - * - * @param value - * an offset rows value - * - * @return a self instance - * - * @see #FETCH_FIRST_ROWS_ONLY(int) - * - * @since 3.5.2 - */ - public T OFFSET_ROWS(long value) { - return OFFSET_ROWS(String.valueOf(value)); - } - - /** - * used to add a new inserted row while do multi-row insert. - * - * @return the t - * - * @since 3.5.2 - */ - public T ADD_ROW() { - sql().valuesList.add(new ArrayList<>()); - return getSelf(); - } - - private SQLStatement sql() { - return sql; - } - - public A usingAppender(A a) { - sql().sql(a); - return a; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sql().sql(sb); - return sb.toString(); - } - - private static class SafeAppendable { - private final Appendable appendable; - private boolean empty = true; - - public SafeAppendable(Appendable a) { - this.appendable = a; - } - - public SafeAppendable append(CharSequence s) { - try { - if (empty && s.length() > 0) { - empty = false; - } - appendable.append(s); - } catch (IOException e) { - throw new RuntimeException(e); - } - return this; - } - - public boolean isEmpty() { - return empty; - } - - } - - private static class SQLStatement { - - public enum StatementType { - - DELETE, - - INSERT, - - SELECT, - - UPDATE - - } - - private enum LimitingRowsStrategy { - NOP { - @Override - protected void appendClause(SafeAppendable builder, String offset, String limit) { - // NOP - } - }, - ISO { - @Override - protected void appendClause(SafeAppendable builder, String offset, String limit) { - if (offset != null) { - builder.append(" OFFSET ").append(offset).append(" ROWS"); - } - if (limit != null) { - builder.append(" FETCH FIRST ").append(limit).append(" ROWS ONLY"); - } - } - }, - OFFSET_LIMIT { - @Override - protected void appendClause(SafeAppendable builder, String offset, String limit) { - if (limit != null) { - builder.append(" LIMIT ").append(limit); - } - if (offset != null) { - builder.append(" OFFSET ").append(offset); - } - } - }; - - protected abstract void appendClause(SafeAppendable builder, String offset, String limit); - - } - - StatementType statementType; - List sets = new ArrayList<>(); - List select = new ArrayList<>(); - List tables = new ArrayList<>(); - List join = new ArrayList<>(); - List innerJoin = new ArrayList<>(); - List outerJoin = new ArrayList<>(); - List leftOuterJoin = new ArrayList<>(); - List rightOuterJoin = new ArrayList<>(); - List where = new ArrayList<>(); - List having = new ArrayList<>(); - List groupBy = new ArrayList<>(); - List orderBy = new ArrayList<>(); - List lastList = new ArrayList<>(); - List columns = new ArrayList<>(); - List> valuesList = new ArrayList<>(); - boolean distinct; - String offset; - String limit; - LimitingRowsStrategy limitingRowsStrategy = LimitingRowsStrategy.NOP; - - public SQLStatement() { - // Prevent Synthetic Access - valuesList.add(new ArrayList<>()); - } - - private void sqlClause(SafeAppendable builder, String keyword, List parts, String open, String close, - String conjunction) { - if (!parts.isEmpty()) { - if (!builder.isEmpty()) { - builder.append("\n"); - } - builder.append(keyword); - builder.append(" "); - builder.append(open); - String last = "________"; - for (int i = 0, n = parts.size(); i < n; i++) { - String part = parts.get(i); - if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) { - builder.append(conjunction); - } - builder.append(part); - last = part; - } - builder.append(close); - } - } - - private String selectSQL(SafeAppendable builder) { - if (distinct) { - sqlClause(builder, "SELECT DISTINCT", select, "", "", ", "); - } else { - sqlClause(builder, "SELECT", select, "", "", ", "); - } - - sqlClause(builder, "FROM", tables, "", "", ", "); - joins(builder); - sqlClause(builder, "WHERE", where, "(", ")", " AND "); - sqlClause(builder, "GROUP BY", groupBy, "", "", ", "); - sqlClause(builder, "HAVING", having, "(", ")", " AND "); - sqlClause(builder, "ORDER BY", orderBy, "", "", ", "); - limitingRowsStrategy.appendClause(builder, offset, limit); - return builder.toString(); - } - - private void joins(SafeAppendable builder) { - sqlClause(builder, "JOIN", join, "", "", "\nJOIN "); - sqlClause(builder, "INNER JOIN", innerJoin, "", "", "\nINNER JOIN "); - sqlClause(builder, "OUTER JOIN", outerJoin, "", "", "\nOUTER JOIN "); - sqlClause(builder, "LEFT OUTER JOIN", leftOuterJoin, "", "", "\nLEFT OUTER JOIN "); - sqlClause(builder, "RIGHT OUTER JOIN", rightOuterJoin, "", "", "\nRIGHT OUTER JOIN "); - } - - private String insertSQL(SafeAppendable builder) { - sqlClause(builder, "INSERT INTO", tables, "", "", ""); - sqlClause(builder, "", columns, "(", ")", ", "); - for (int i = 0; i < valuesList.size(); i++) { - sqlClause(builder, i > 0 ? "," : "VALUES", valuesList.get(i), "(", ")", ", "); - } - return builder.toString(); - } - - private String deleteSQL(SafeAppendable builder) { - sqlClause(builder, "DELETE FROM", tables, "", "", ""); - sqlClause(builder, "WHERE", where, "(", ")", " AND "); - limitingRowsStrategy.appendClause(builder, null, limit); - return builder.toString(); - } - - private String updateSQL(SafeAppendable builder) { - sqlClause(builder, "UPDATE", tables, "", "", ""); - joins(builder); - sqlClause(builder, "SET", sets, "", "", ", "); - sqlClause(builder, "WHERE", where, "(", ")", " AND "); - limitingRowsStrategy.appendClause(builder, null, limit); - return builder.toString(); - } - - public String sql(Appendable a) { - SafeAppendable builder = new SafeAppendable(a); - if (statementType == null) { - return null; - } - - String answer; - - switch (statementType) { - case DELETE: - answer = deleteSQL(builder); - break; - - case INSERT: - answer = insertSQL(builder); - break; - - case SELECT: - answer = selectSQL(builder); - break; - - case UPDATE: - answer = updateSQL(builder); - break; - - default: - answer = null; - } - - return answer; - } - } -} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/sql/JdbcSql.java b/src/main/java/xyz/zhouxy/plusone/commons/sql/JdbcSql.java deleted file mode 100644 index 5844d28..0000000 --- a/src/main/java/xyz/zhouxy/plusone/commons/sql/JdbcSql.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package xyz.zhouxy.plusone.commons.sql; - -import java.util.Collection; - -import xyz.zhouxy.plusone.commons.util.StringTools; - -public class JdbcSql extends SQL { - - JdbcSql() { - super(); - } - - public static JdbcSql newSql() { - return new JdbcSql(); - } - - @Override - public JdbcSql getSelf() { - return this; - } - - public static String IN(String col, Collection c) { // NOSONAR - return IN(col, c.size()); - } - - public static String IN(String col, T[] c) { // NOSONAR - return IN(col, c.length); - } - - private static String IN(String col, int length) { // NOSONAR - if (length == 0) { - return "false"; - } - return col + " IN (" + buildQuestionsList(length) + ')'; - } - - public static String NOT_IN(String col, Collection c) { // NOSONAR - return NOT_IN(col, c.size()); - } - - public static String NOT_IN(String col, T[] c) { // NOSONAR - return NOT_IN(col, c.length); - } - - private static String NOT_IN(String col, int length) { // NOSONAR - if (length == 0) { - return "true"; - } - return col + " NOT IN (" + buildQuestionsList(length) + ')'; - } - - private static String buildQuestionsList(int times) { - final int length = times <= 0 ? 0 : (times * 3 - 2); - return StringTools.repeat("?, ", times, length); - } -} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/sql/MyBatisSql.java b/src/main/java/xyz/zhouxy/plusone/commons/sql/MyBatisSql.java deleted file mode 100644 index 0382d43..0000000 --- a/src/main/java/xyz/zhouxy/plusone/commons/sql/MyBatisSql.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package xyz.zhouxy.plusone.commons.sql; - -import com.google.common.annotations.Beta; - -@Beta -public class MyBatisSql extends SQL { - - private final boolean withScript; - - MyBatisSql(boolean withScript) { - super(); - this.withScript = withScript; - } - - public static MyBatisSql newSql() { - return new MyBatisSql(false); - } - - public static MyBatisSql newScriptSql() { - return new MyBatisSql(true); - } - - @Override - public MyBatisSql getSelf() { - return this; - } - - public static String IN(String col, String paramName) { // NOSONAR - return " " + col + " IN" + buildForeach(col, paramName); - } - - public static String NOT_IN(String col, String paramName) { // NOSONAR - return col + " NOT IN" + buildForeach(col, paramName); - } - - private static String buildForeach(String col, String paramName) { - final String format = "" + - "#{%s}" + - ""; - return String.format(format, col, paramName, col); - } - - @Override - public String toString() { - if (withScript) { - return ""; - } - return super.toString(); - } -} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/sql/SQL.java b/src/main/java/xyz/zhouxy/plusone/commons/sql/SQL.java deleted file mode 100644 index a699639..0000000 --- a/src/main/java/xyz/zhouxy/plusone/commons/sql/SQL.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package xyz.zhouxy.plusone.commons.sql; - -import org.apache.ibatis.jdbc.AbstractSQL; - -import com.google.common.annotations.Beta; - -/** - * @author ZhouXY - */ -@Beta -public abstract class SQL extends AbstractSQL { - - public static JdbcSql newJdbcSql() { - return new JdbcSql(); - } - - public static MyBatisSql newMyBatisSql(boolean withScript) { - return new MyBatisSql(withScript); - } - - public T WHERE(boolean condition, String sqlCondition) { // NOSONAR - if (condition) { - return WHERE(sqlCondition); - } - return getSelf(); - } - - public T WHERE(boolean condition, String ifSqlCondition, String elseSqlCondition) { // NOSONAR - return WHERE(condition ? ifSqlCondition : elseSqlCondition); - } -} diff --git a/src/test/java/xyz/zhouxy/plusone/commons/sql/MyBatisSqlBuilderTests.java b/src/test/java/xyz/zhouxy/plusone/commons/sql/MyBatisSqlBuilderTests.java deleted file mode 100644 index 8576ad5..0000000 --- a/src/test/java/xyz/zhouxy/plusone/commons/sql/MyBatisSqlBuilderTests.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package xyz.zhouxy.plusone.commons.sql; - -import static xyz.zhouxy.plusone.commons.sql.MyBatisSql.IN; - -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class MyBatisSqlBuilderTests { - private static final Logger log = LoggerFactory.getLogger(MyBatisSqlBuilderTests.class); - - @Test - void test() { - // List ids = Arrays.asList("2333", "4501477"); - MyBatisSql sql = MyBatisSql.newScriptSql() - .SELECT("*") - .FROM("test_table") - .WHERE(IN("id", "ids")); - log.info("sql: {}", sql); - } -} From ab2fc541628d1ecfb771082a0f33fa44e4c77ea0 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 29 Dec 2024 00:20:12 +0800 Subject: [PATCH 12/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=20UnifiedResponse=20?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=9B=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=20PageResult#empty=EF=BC=8C=E4=BF=AE=E6=94=B9=20PageResult#toS?= =?UTF-8?q?tring=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 9 +- .../plusone/commons/model/dto/PageResult.java | 8 +- .../model/dto/UnifiedResponseTests.java | 612 +++++++++++++++--- 3 files changed, 525 insertions(+), 104 deletions(-) diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index 0db7d21..4e65a24 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,7 +1,7 @@ -[ ] 未开始测试 - 5 (7.94%) -[-] 测试未完成 - 5 (7.94%) -[Y] 测试完成 - 31 (49.21%) -[x] 无需测试 - 22 (34.92%) +[ ] 未开始测试 - 5 (7.81%) +[-] 测试未完成 - 5 (7.81%) +[Y] 测试完成 - 32 (50.00%) +[x] 无需测试 - 22 (34.38%) xyz.zhouxy.plusone.commons ├───annotation @@ -69,6 +69,7 @@ xyz.zhouxy.plusone.commons │ PageResult.java [-] │ PagingAndSortingQueryParams.java [-] │ PagingParams.java [-] + │ UnifiedResponse.java [Y] │ ├───time │ Quarter.java [Y] diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java b/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java index 7f913a8..b73d0a0 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java @@ -16,6 +16,7 @@ package xyz.zhouxy.plusone.commons.model.dto; +import java.util.Collections; import java.util.List; import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod; @@ -46,6 +47,11 @@ public class PageResult { return new PageResult<>(content, total); } + @StaticFactoryMethod(PageResult.class) + public static PageResult empty() { + return new PageResult<>(Collections.emptyList(), 0L); + } + public long getTotal() { return total; } @@ -56,6 +62,6 @@ public class PageResult { @Override public String toString() { - return "PageDTO [total=" + total + ", content=" + content + "]"; + return "PageResult [total=" + total + ", content=" + content + "]"; } } diff --git a/src/test/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponseTests.java b/src/test/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponseTests.java index 15ceb13..45a6ed2 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponseTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponseTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package xyz.zhouxy.plusone.commons.model.dto; import static org.junit.jupiter.api.Assertions.*; @@ -13,6 +29,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import xyz.zhouxy.plusone.commons.exception.business.BizException; import xyz.zhouxy.plusone.commons.model.dto.UnifiedResponse.SuccessResult; @Slf4j @@ -33,7 +50,7 @@ class UnifiedResponseTests { } @Test - void testSuccess_WithOutArgument() throws Exception { + void testSuccess_WithoutArgument() throws Exception { // 1. success without argument UnifiedResponse success = UnifiedResponse.success(); assertEquals(SuccessResult.SUCCESS_STATUS, success.getStatus()); @@ -56,68 +73,6 @@ class UnifiedResponseTests { assertEquals("{\"status\":\"2000000\",\"message\":\"成功\"}", jacksonSuccessWithMessage); } - @Test - void testSuccess_WithNullMessage() throws Exception { - // 3. success with null message - UnifiedResponse successWithNullMessage = UnifiedResponse.success(null); - assertEquals(SuccessResult.SUCCESS_STATUS, successWithNullMessage.getStatus()); - assertEquals("", successWithNullMessage.getMessage()); - assertNull(successWithNullMessage.getData()); - String jacksonSuccessWithNullMessage = jackson.writeValueAsString(successWithNullMessage); - log.info("jacksonSuccessWithNullMessage: {}", jacksonSuccessWithNullMessage); - assertEquals("{\"status\":\"2000000\",\"message\":\"\"}", jacksonSuccessWithNullMessage); - } - - @Test - void testSuccess_WithEmptyMessage() throws Exception { - // 4. success with empty message - UnifiedResponse successWithEmptyMessage = UnifiedResponse.success(""); - assertEquals(SuccessResult.SUCCESS_STATUS, successWithEmptyMessage.getStatus()); - assertEquals("", successWithEmptyMessage.getMessage()); - assertNull(successWithEmptyMessage.getData()); - String jacksonSuccessWithEmptyMessage = jackson.writeValueAsString(successWithEmptyMessage); - log.info("jacksonSuccessWithEmptyMessage: {}", jacksonSuccessWithEmptyMessage); - assertEquals("{\"status\":\"2000000\",\"message\":\"\"}", jacksonSuccessWithEmptyMessage); - } - - @Test - void testSuccess_WithData_String() throws Exception { - UnifiedResponse successWithStringData = UnifiedResponse.success("查询成功", "zhouxy"); - assertEquals(SuccessResult.SUCCESS_STATUS, successWithStringData.getStatus()); - assertEquals("查询成功", successWithStringData.getMessage()); - assertEquals("zhouxy", successWithStringData.getData()); - String jacksonSuccessWithStringData = jackson.writeValueAsString(successWithStringData); - log.info("jacksonSuccessWithStringData: {}", jacksonSuccessWithStringData); - assertEquals("{\"status\":\"2000000\",\"message\":\"查询成功\",\"data\":\"zhouxy\"}", jacksonSuccessWithStringData); - - assertEquals("{status: \"2000000\", message: \"查询成功\", data: \"zhouxy\"}", successWithStringData.toString()); - } - - @Test - void testSuccess_WithData_Integer() throws Exception { - final UnifiedResponse successWithIntegerData = UnifiedResponse.success("查询成功", 1); - assertEquals(SuccessResult.SUCCESS_STATUS, successWithIntegerData.getStatus()); - assertEquals("查询成功", successWithIntegerData.getMessage()); - assertEquals(1, successWithIntegerData.getData()); - final String jacksonSuccessWithIntegerData = jackson.writeValueAsString(successWithIntegerData); - log.info("jacksonSuccessWithIntegerData: {}", jacksonSuccessWithIntegerData); - assertEquals("{\"status\":\"2000000\",\"message\":\"查询成功\",\"data\":1}", jacksonSuccessWithIntegerData); - - assertEquals("{status: \"2000000\", message: \"查询成功\", data: 1}", successWithIntegerData.toString()); - } - - @Test - void testSuccess_WithData_PageResult() throws Exception { - UnifiedResponse successWithData = UnifiedResponse.success("查询成功", pageResult); - assertEquals(SuccessResult.SUCCESS_STATUS, successWithData.getStatus()); - assertEquals("查询成功", successWithData.getMessage()); - assertNotNull(successWithData.getData()); - assertEquals(pageResult, successWithData.getData()); - String jacksonSuccessWithData = jackson.writeValueAsString(successWithData); - log.info("jacksonSuccessWithData: {}", jacksonSuccessWithData); - assertEquals("{\"status\":\"2000000\",\"message\":\"查询成功\",\"data\":{\"total\":108,\"content\":[{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"},{\"username\":\"zhouxy2\",\"email\":\"zhouxy2@gmail.com\"}]}}", jacksonSuccessWithData); - } - @Test void testSuccess_WithMessageAndNullData() throws Exception { // success with message and null data @@ -133,17 +88,53 @@ class UnifiedResponseTests { } @Test - void testSuccess_WithNullMessageAndData() throws Exception { - // success with null message and data - final User user = new User("zhouxy", "zhouxy@code108.cn"); - final UnifiedResponse successWithNullMessageAndData = UnifiedResponse.success(null, user); - assertEquals(SuccessResult.SUCCESS_STATUS, successWithNullMessageAndData.getStatus()); - assertEquals("", successWithNullMessageAndData.getMessage()); - assertEquals(user, successWithNullMessageAndData.getData()); - final String jacksonSuccessWithNullMessageAndData = jackson.writeValueAsString(successWithNullMessageAndData); - log.info("jacksonSuccessWithNullMessageAndData: {}", jacksonSuccessWithNullMessageAndData); - assertEquals("{\"status\":\"2000000\",\"message\":\"\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@code108.cn\"}}", - jacksonSuccessWithNullMessageAndData); + void testSuccess_WithMessageAndStringData() throws Exception { + UnifiedResponse successWithStringData = UnifiedResponse.success("查询成功", "zhouxy"); + assertEquals(SuccessResult.SUCCESS_STATUS, successWithStringData.getStatus()); + assertEquals("查询成功", successWithStringData.getMessage()); + assertEquals("zhouxy", successWithStringData.getData()); + String jacksonSuccessWithStringData = jackson.writeValueAsString(successWithStringData); + log.info("jacksonSuccessWithStringData: {}", jacksonSuccessWithStringData); + assertEquals("{\"status\":\"2000000\",\"message\":\"查询成功\",\"data\":\"zhouxy\"}", jacksonSuccessWithStringData); + + assertEquals("{status: \"2000000\", message: \"查询成功\", data: \"zhouxy\"}", successWithStringData.toString()); + } + + @Test + void testSuccess_WithMessageAndIntegerData() throws Exception { + final UnifiedResponse successWithIntegerData = UnifiedResponse.success("查询成功", 1); + assertEquals(SuccessResult.SUCCESS_STATUS, successWithIntegerData.getStatus()); + assertEquals("查询成功", successWithIntegerData.getMessage()); + assertEquals(1, successWithIntegerData.getData()); + final String jacksonSuccessWithIntegerData = jackson.writeValueAsString(successWithIntegerData); + log.info("jacksonSuccessWithIntegerData: {}", jacksonSuccessWithIntegerData); + assertEquals("{\"status\":\"2000000\",\"message\":\"查询成功\",\"data\":1}", jacksonSuccessWithIntegerData); + + assertEquals("{status: \"2000000\", message: \"查询成功\", data: 1}", successWithIntegerData.toString()); + } + + @Test + void testSuccess_WithMessageAndData() throws Exception { + UnifiedResponse successWithData = UnifiedResponse.success("查询成功", pageResult); + assertEquals(SuccessResult.SUCCESS_STATUS, successWithData.getStatus()); + assertEquals("查询成功", successWithData.getMessage()); + assertNotNull(successWithData.getData()); + assertEquals(pageResult, successWithData.getData()); + String jacksonSuccessWithData = jackson.writeValueAsString(successWithData); + log.info("jacksonSuccessWithData: {}", jacksonSuccessWithData); + assertEquals("{\"status\":\"2000000\",\"message\":\"查询成功\",\"data\":{\"total\":108,\"content\":[{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"},{\"username\":\"zhouxy2\",\"email\":\"zhouxy2@gmail.com\"}]}}", jacksonSuccessWithData); + } + + @Test + void testSuccess_WithNullMessage() throws Exception { + // 3. success with null message + UnifiedResponse successWithNullMessage = UnifiedResponse.success(null); + assertEquals(SuccessResult.SUCCESS_STATUS, successWithNullMessage.getStatus()); + assertEquals("", successWithNullMessage.getMessage()); + assertNull(successWithNullMessage.getData()); + String jacksonSuccessWithNullMessage = jackson.writeValueAsString(successWithNullMessage); + log.info("jacksonSuccessWithNullMessage: {}", jacksonSuccessWithNullMessage); + assertEquals("{\"status\":\"2000000\",\"message\":\"\"}", jacksonSuccessWithNullMessage); } // success with null message and null data @@ -161,18 +152,30 @@ class UnifiedResponseTests { assertEquals("{status: \"2000000\", message: \"\", data: null}", successWithNullMessageAndNullData.toString()); } - // success with empty message and data @Test - void testSuccess_WithEmptyMessageAndData() throws Exception { - final User user = new User("zhouxy", "zhouxy@gmail.com"); - final UnifiedResponse successWithEmptyMessageAndData = UnifiedResponse.success("", user); - assertEquals(SuccessResult.SUCCESS_STATUS, successWithEmptyMessageAndData.getStatus()); - assertEquals("", successWithEmptyMessageAndData.getMessage()); - assertEquals(user, successWithEmptyMessageAndData.getData()); + void testSuccess_WithNullMessageAndData() throws Exception { + // success with null message and data + final User user = new User("zhouxy", "zhouxy@code108.cn"); + final UnifiedResponse successWithNullMessageAndData = UnifiedResponse.success(null, user); + assertEquals(SuccessResult.SUCCESS_STATUS, successWithNullMessageAndData.getStatus()); + assertEquals("", successWithNullMessageAndData.getMessage()); + assertEquals(user, successWithNullMessageAndData.getData()); + final String jacksonSuccessWithNullMessageAndData = jackson.writeValueAsString(successWithNullMessageAndData); + log.info("jacksonSuccessWithNullMessageAndData: {}", jacksonSuccessWithNullMessageAndData); + assertEquals("{\"status\":\"2000000\",\"message\":\"\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@code108.cn\"}}", + jacksonSuccessWithNullMessageAndData); + } - final String jacksonSuccessWithEmptyMessageAndData = jackson.writeValueAsString(successWithEmptyMessageAndData); - log.info("jacksonSuccessWithEmptyMessageAndData: {}", jacksonSuccessWithEmptyMessageAndData); - assertEquals("{\"status\":\"2000000\",\"message\":\"\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@gmail.com\"}}", jacksonSuccessWithEmptyMessageAndData); + @Test + void testSuccess_WithEmptyMessage() throws Exception { + // 4. success with empty message + UnifiedResponse successWithEmptyMessage = UnifiedResponse.success(""); + assertEquals(SuccessResult.SUCCESS_STATUS, successWithEmptyMessage.getStatus()); + assertEquals("", successWithEmptyMessage.getMessage()); + assertNull(successWithEmptyMessage.getData()); + String jacksonSuccessWithEmptyMessage = jackson.writeValueAsString(successWithEmptyMessage); + log.info("jacksonSuccessWithEmptyMessage: {}", jacksonSuccessWithEmptyMessage); + assertEquals("{\"status\":\"2000000\",\"message\":\"\"}", jacksonSuccessWithEmptyMessage); } // success with empty message and null data @@ -190,22 +193,297 @@ class UnifiedResponseTests { assertEquals("{status: \"2000000\", message: \"\", data: null}", successWithEmptyMessageAndNullData.toString()); } + // success with empty message and data @Test - void testError_Status_And_Message() throws Exception { + void testSuccess_WithEmptyMessageAndData() throws Exception { + final User user = new User("zhouxy", "zhouxy@gmail.com"); + final UnifiedResponse successWithEmptyMessageAndData = UnifiedResponse.success("", user); + assertEquals(SuccessResult.SUCCESS_STATUS, successWithEmptyMessageAndData.getStatus()); + assertEquals("", successWithEmptyMessageAndData.getMessage()); + assertEquals(user, successWithEmptyMessageAndData.getData()); + + final String jacksonSuccessWithEmptyMessageAndData = jackson.writeValueAsString(successWithEmptyMessageAndData); + log.info("jacksonSuccessWithEmptyMessageAndData: {}", jacksonSuccessWithEmptyMessageAndData); + assertEquals("{\"status\":\"2000000\",\"message\":\"\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@gmail.com\"}}", jacksonSuccessWithEmptyMessageAndData); + } + + @Test + void testError_WithMessage() throws Exception { + UnifiedResponse errorWithMessage = UnifiedResponse.error("查询失败"); + assertEquals("9999999", errorWithMessage.getStatus()); + assertEquals("查询失败", errorWithMessage.getMessage()); + assertNull(errorWithMessage.getData()); + + final String jacksonErrorWithMessage = jackson.writeValueAsString(errorWithMessage); + assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\"}", jacksonErrorWithMessage); + final String gsonErrorWithMessage = gson.toJson(errorWithMessage); + assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\"}", gsonErrorWithMessage); + } + + @Test + void testError_WithMessage_AndNullData() throws Exception { + UnifiedResponse errorWithMessageAndNullData = UnifiedResponse.error("查询失败", (Object) null); + assertEquals("9999999", errorWithMessageAndNullData.getStatus()); + assertEquals("查询失败", errorWithMessageAndNullData.getMessage()); + assertNull(errorWithMessageAndNullData.getData()); + + final String jacksonErrorWithMessageAndNullData = jackson.writeValueAsString(errorWithMessageAndNullData); + assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\"}", jacksonErrorWithMessageAndNullData); + final String gsonErrorWithMessageAndNullData = gson.toJson(errorWithMessageAndNullData); + assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\"}", gsonErrorWithMessageAndNullData); + } + + @Test + void testError_WithMessage_AndData() throws Exception { + final User user = new User("zhouxy", "zhouxy@gmail.com"); + UnifiedResponse errorWithMessageAndData = UnifiedResponse.error("查询失败", user); + assertEquals("9999999", errorWithMessageAndData.getStatus()); + assertEquals("查询失败", errorWithMessageAndData.getMessage()); + assertEquals(user, errorWithMessageAndData.getData()); + + final String jacksonErrorWithMessageAndData = jackson.writeValueAsString(errorWithMessageAndData); + assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@gmail.com\"}}", + jacksonErrorWithMessageAndData); + final String gsonErrorWithMessageAndData = gson.toJson(errorWithMessageAndData); + assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@gmail.com\"}}", + gsonErrorWithMessageAndData); + } + + @Test + void testError_WithNullMessage() throws Exception { + UnifiedResponse errorWithNullMessage = UnifiedResponse.error((String) null); + assertEquals("9999999", errorWithNullMessage.getStatus()); + assertEquals("", errorWithNullMessage.getMessage()); + assertNull(errorWithNullMessage.getData()); + + final String jacksonErrorWithNullMessage = jackson.writeValueAsString(errorWithNullMessage); + assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", jacksonErrorWithNullMessage); + final String gsonErrorWithNullMessage = gson.toJson(errorWithNullMessage); + assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", gsonErrorWithNullMessage); + } + + @Test + void testError_WithNullMessage_AndNullData() throws Exception { + UnifiedResponse errorWithNullMessageAndNullData = UnifiedResponse.error((String) null, (User) null); + assertEquals("9999999", errorWithNullMessageAndNullData.getStatus()); + assertEquals("", errorWithNullMessageAndNullData.getMessage()); + assertNull(errorWithNullMessageAndNullData.getData()); + + final String jacksonErrorWithNullMessageAndNullData = jackson.writeValueAsString(errorWithNullMessageAndNullData); + assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", jacksonErrorWithNullMessageAndNullData); + final String gsonErrorWithNullMessageAndNullData = gson.toJson(errorWithNullMessageAndNullData); + assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", gsonErrorWithNullMessageAndNullData); + } + + @Test + void testError_WithNullMessage_AndData() throws Exception { + final User user = new User("zhouxy1", "zhouxy1@gmail.com"); + UnifiedResponse errorWithNullMessageAndData = UnifiedResponse.error((String) null, user); + assertEquals("9999999", errorWithNullMessageAndData.getStatus()); + assertEquals("", errorWithNullMessageAndData.getMessage()); + assertEquals(user, errorWithNullMessageAndData.getData()); + + final String jacksonErrorWithNullMessageAndData = jackson.writeValueAsString(errorWithNullMessageAndData); + assertEquals("{\"status\":\"9999999\",\"message\":\"\",\"data\":{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"}}", + jacksonErrorWithNullMessageAndData); + final String gsonErrorWithNullMessageAndData = gson.toJson(errorWithNullMessageAndData); + assertEquals("{\"status\":\"9999999\",\"message\":\"\",\"data\":{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"}}", + gsonErrorWithNullMessageAndData); + } + + @Test + void testError_WithEmptyMessage() throws Exception { + UnifiedResponse errorWithEmptyMessage = UnifiedResponse.error(""); + assertEquals("9999999", errorWithEmptyMessage.getStatus()); + assertEquals("", errorWithEmptyMessage.getMessage()); + assertNull(errorWithEmptyMessage.getData()); + final String jacksonErrorWithEmptyMessage = jackson.writeValueAsString(errorWithEmptyMessage); + assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", jacksonErrorWithEmptyMessage); + final String gsonErrorWithEmptyMessage = gson.toJson(errorWithEmptyMessage); + assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", gsonErrorWithEmptyMessage); + } + + @Test + void testError_WithEmptyMessage_AndNullData() throws Exception { + UnifiedResponse errorWithEmptyMessageAndNullData = UnifiedResponse.error("", (User) null); + assertEquals("9999999", errorWithEmptyMessageAndNullData.getStatus()); + assertEquals("", errorWithEmptyMessageAndNullData.getMessage()); + assertNull(errorWithEmptyMessageAndNullData.getData()); + + final String jacksonErrorEmptyNullMessageAndNullData = jackson.writeValueAsString(errorWithEmptyMessageAndNullData); + assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", jacksonErrorEmptyNullMessageAndNullData); + final String gsonErrorWithEmptyMessageAndNullData = gson.toJson(errorWithEmptyMessageAndNullData); + assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", gsonErrorWithEmptyMessageAndNullData); + } + + @Test + void testError_WithEmptyMessage_AndData() throws Exception { + final User user = new User("zhouxy1", "zhouxy1@gmail.com"); + UnifiedResponse errorWithEmptyMessageAndData = UnifiedResponse.error("", user); + assertEquals("9999999", errorWithEmptyMessageAndData.getStatus()); + assertEquals("", errorWithEmptyMessageAndData.getMessage()); + assertEquals(user, errorWithEmptyMessageAndData.getData()); + + final String jacksonErrorWithEmptyMessageAndData = jackson.writeValueAsString(errorWithEmptyMessageAndData); + assertEquals("{\"status\":\"9999999\",\"message\":\"\",\"data\":{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"}}", + jacksonErrorWithEmptyMessageAndData); + final String gsonErrorWithEmptyMessageAndData = gson.toJson(errorWithEmptyMessageAndData); + assertEquals("{\"status\":\"9999999\",\"message\":\"\",\"data\":{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"}}", + gsonErrorWithEmptyMessageAndData); + } + + @Test + void testError_WithStatusAndMessage() throws Exception { final UnifiedResponse errorWithStatusAndMessage = UnifiedResponse.error(108, "查询失败"); assertEquals(108, errorWithStatusAndMessage.getStatus()); assertEquals("查询失败", errorWithStatusAndMessage.getMessage()); assertNull(errorWithStatusAndMessage.getData()); + assertEquals("{status: 108, message: \"查询失败\", data: null}", errorWithStatusAndMessage.toString()); final String jacksonErrorWithStatusAndMessage = jackson.writeValueAsString(errorWithStatusAndMessage); log.info("jacksonErrorWithStatusAndMessage: {}", jacksonErrorWithStatusAndMessage); assertEquals("{\"status\":108,\"message\":\"查询失败\"}", jacksonErrorWithStatusAndMessage); - assertEquals("{status: 108, message: \"查询失败\", data: null}", errorWithStatusAndMessage.toString()); + final String gsonErrorWithStatusAndMessage = gson.toJson(errorWithStatusAndMessage); + assertEquals("{\"status\":108,\"message\":\"查询失败\"}", gsonErrorWithStatusAndMessage); } @Test - void testError_NullStatus() { + void testError_WithStatusAndMessage_AndNullData() throws Exception { + final UnifiedResponse errorWithStatusAndMessageAndNullData = UnifiedResponse.error(108, "查询失败", null); + assertEquals(108, errorWithStatusAndMessageAndNullData.getStatus()); + assertEquals("查询失败", errorWithStatusAndMessageAndNullData.getMessage()); + assertNull(errorWithStatusAndMessageAndNullData.getData()); + assertEquals("{status: 108, message: \"查询失败\", data: null}", errorWithStatusAndMessageAndNullData.toString()); + + final String jacksonErrorWithStatusAndMessageAndNullData = jackson.writeValueAsString(errorWithStatusAndMessageAndNullData); + log.info("jacksonErrorWithStatusAndMessage: {}", jacksonErrorWithStatusAndMessageAndNullData); + assertEquals("{\"status\":108,\"message\":\"查询失败\"}", jacksonErrorWithStatusAndMessageAndNullData); + + final String gsonErrorWithStatusAndMessageAndNullData = gson.toJson(errorWithStatusAndMessageAndNullData); + assertEquals("{\"status\":108,\"message\":\"查询失败\"}", gsonErrorWithStatusAndMessageAndNullData); + } + + @Test + void testError_WithStatusAndMessage_AndData() throws Exception { + final PageResult emptyPageResult = PageResult.empty(); + final UnifiedResponse errorWithStatusAndMessageAndData = UnifiedResponse.error(108, "查询失败", emptyPageResult); + assertEquals(108, errorWithStatusAndMessageAndData.getStatus()); + assertEquals("查询失败", errorWithStatusAndMessageAndData.getMessage()); + assertEquals(emptyPageResult, errorWithStatusAndMessageAndData.getData()); + assertEquals("{status: 108, message: \"查询失败\", data: PageResult [total=0, content=[]]}", errorWithStatusAndMessageAndData.toString()); + + final String jacksonErrorWithStatusAndMessageAndData = jackson.writeValueAsString(errorWithStatusAndMessageAndData); + assertEquals("{\"status\":108,\"message\":\"查询失败\",\"data\":{\"total\":0,\"content\":[]}}", + jacksonErrorWithStatusAndMessageAndData); + + final String gsonErrorWithStatusAndMessageAndData = gson.toJson(errorWithStatusAndMessageAndData); + assertEquals("{\"status\":108,\"message\":\"查询失败\",\"data\":{\"total\":0,\"content\":[]}}", + gsonErrorWithStatusAndMessageAndData); + } + + @Test + void testError_WithStatusAndNullMessage() throws Exception { + UnifiedResponse errorWithStatusAndNullMessage = UnifiedResponse.error(500, (String) null); + assertEquals(500, errorWithStatusAndNullMessage.getStatus()); + assertEquals("", errorWithStatusAndNullMessage.getMessage()); + assertNull(errorWithStatusAndNullMessage.getData()); + + final String jacksonErrorWithStatusAndNullMessage = jackson.writeValueAsString(errorWithStatusAndNullMessage); + assertEquals("{\"status\":500,\"message\":\"\"}", jacksonErrorWithStatusAndNullMessage); + + final String gsonErrorWithStatusAndNullMessage = gson.toJson(errorWithStatusAndNullMessage); + assertEquals("{\"status\":500,\"message\":\"\"}", gsonErrorWithStatusAndNullMessage); + } + + @Test + void testError_WithStatusAndNullMessage_AndNullData() throws Exception { + UnifiedResponse errorWithStatusAndNullMessageAndNullData = UnifiedResponse.error(500, (String) null, null); + + assertEquals(500, errorWithStatusAndNullMessageAndNullData.getStatus()); + assertEquals("", errorWithStatusAndNullMessageAndNullData.getMessage()); + assertNull(errorWithStatusAndNullMessageAndNullData.getData()); + + final String jacksonErrorWithStatusAndNullMessageAndNullData = jackson.writeValueAsString(errorWithStatusAndNullMessageAndNullData); + assertEquals("{\"status\":500,\"message\":\"\"}", jacksonErrorWithStatusAndNullMessageAndNullData); + + final String gsonErrorWithStatusAndNullMessageAndNullData = gson.toJson(errorWithStatusAndNullMessageAndNullData); + assertEquals("{\"status\":500,\"message\":\"\"}", gsonErrorWithStatusAndNullMessageAndNullData); + } + + @Test + void testError_WithStatusAndNullMessage_AndData() throws Exception { + PageResult emptyPageResult = PageResult.empty(); + UnifiedResponse errorWithStatusAndNullMessageAndData = UnifiedResponse.error(500, (String) null, emptyPageResult); + assertEquals(500, errorWithStatusAndNullMessageAndData.getStatus()); + assertEquals("", errorWithStatusAndNullMessageAndData.getMessage()); + assertEquals(emptyPageResult, errorWithStatusAndNullMessageAndData.getData()); + final String jacksonErrorWithStatusAndNullMessageAndData = jackson.writeValueAsString(errorWithStatusAndNullMessageAndData); + assertEquals("{\"status\":500,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonErrorWithStatusAndNullMessageAndData); + final String gsonErrorWithStatusAndNullMessageAndData = gson.toJson(errorWithStatusAndNullMessageAndData); + assertEquals("{\"status\":500,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonErrorWithStatusAndNullMessageAndData); + } + + @Test + void testError_WithStatusAndEmptyMessage() throws Exception { + UnifiedResponse errorWithStatusAndEmptyMessage = UnifiedResponse.error(500, ""); + assertEquals(500, errorWithStatusAndEmptyMessage.getStatus()); + assertEquals("", errorWithStatusAndEmptyMessage.getMessage()); + assertNull(errorWithStatusAndEmptyMessage.getData()); + + final String jacksonErrorWithStatusAndEmptyMessage = jackson.writeValueAsString(errorWithStatusAndEmptyMessage); + assertEquals("{\"status\":500,\"message\":\"\"}", jacksonErrorWithStatusAndEmptyMessage); + + final String gsonErrorWithStatusAndEmptyMessage = gson.toJson(errorWithStatusAndEmptyMessage); + assertEquals("{\"status\":500,\"message\":\"\"}", gsonErrorWithStatusAndEmptyMessage); + } + + @Test + void testError_WithStatusAndEmptyMessage_AndNullData() throws Exception { + UnifiedResponse errorWithStatusAndEmptyMessageAndNullData = UnifiedResponse.error(500, "", null); + + assertEquals(500, errorWithStatusAndEmptyMessageAndNullData.getStatus()); + assertEquals("", errorWithStatusAndEmptyMessageAndNullData.getMessage()); + assertNull(errorWithStatusAndEmptyMessageAndNullData.getData()); + + final String jacksonErrorWithStatusAndEmptyMessageAndNullData = jackson.writeValueAsString(errorWithStatusAndEmptyMessageAndNullData); + assertEquals("{\"status\":500,\"message\":\"\"}", jacksonErrorWithStatusAndEmptyMessageAndNullData); + + final String gsonErrorWithStatusAndEmptyMessageAndNullData = gson.toJson(errorWithStatusAndEmptyMessageAndNullData); + assertEquals("{\"status\":500,\"message\":\"\"}", gsonErrorWithStatusAndEmptyMessageAndNullData); + } + + @Test + void testError_WithStatusAndEmptyMessage_AndData() throws Exception { + PageResult emptyPageResult = PageResult.empty(); + UnifiedResponse errorWithStatusAndEmptyMessageAndData = UnifiedResponse.error(500, "", emptyPageResult); + assertEquals(500, errorWithStatusAndEmptyMessageAndData.getStatus()); + assertEquals("", errorWithStatusAndEmptyMessageAndData.getMessage()); + assertEquals(emptyPageResult, errorWithStatusAndEmptyMessageAndData.getData()); + final String jacksonErrorWithStatusAndEmptyMessageAndData = jackson.writeValueAsString(errorWithStatusAndEmptyMessageAndData); + assertEquals("{\"status\":500,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonErrorWithStatusAndEmptyMessageAndData); + final String gsonErrorWithStatusAndEmptyMessageAndData = gson.toJson(errorWithStatusAndEmptyMessageAndData); + assertEquals("{\"status\":500,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonErrorWithStatusAndEmptyMessageAndData); + } + + @Test + void testError_WithStatusAndThrowable() throws Exception { + final IllegalArgumentException e = new IllegalArgumentException("ID cannot be null"); + final UnifiedResponse errorWithStatusThrowable = UnifiedResponse.error(500, e); + assertEquals(500, errorWithStatusThrowable.getStatus()); + assertEquals("ID cannot be null", errorWithStatusThrowable.getMessage()); + assertNull(errorWithStatusThrowable.getData()); + assertEquals("{\"status\":500,\"message\":\"ID cannot be null\"}", jackson.writeValueAsString(errorWithStatusThrowable)); + assertEquals("{\"status\":500,\"message\":\"ID cannot be null\"}", gson.toJson(errorWithStatusThrowable)); + } + + @Test + void testError_WithStatusAndNullThrowable() { + assertThrows(NullPointerException.class, () -> UnifiedResponse.error(500, (Throwable) null)); + } + + @Test + void testError_WithNullStatus() { final Object nullStatus = null; final String nullMessage = null; final User user = new User("zhouxy", "zhouxy@gmail.com"); @@ -222,36 +500,172 @@ class UnifiedResponseTests { // null message assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, nullMessage)); - assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "nullMessage", null)); - assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "nullMessage", user)); + assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "查询失败", null)); + assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "查询失败", user)); // Throwable - // TODO + BizException bizException = new BizException("业务异常"); + assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, bizException)); + assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, (Throwable) null)); + } + + + @Test + void testOf_WithStatusAndMessage() throws Exception { + final UnifiedResponse ofWithStatusAndMessage = UnifiedResponse.of(108, "This is a message."); + assertEquals(108, ofWithStatusAndMessage.getStatus()); + assertEquals("This is a message.", ofWithStatusAndMessage.getMessage()); + assertNull(ofWithStatusAndMessage.getData()); + + final String jacksonOfWithStatusAndMessage = jackson.writeValueAsString(ofWithStatusAndMessage); + log.info("jacksonOfWithStatusAndMessage: {}", jacksonOfWithStatusAndMessage); + assertEquals("{\"status\":108,\"message\":\"This is a message.\"}", jacksonOfWithStatusAndMessage); + + assertEquals("{status: 108, message: \"This is a message.\", data: null}", ofWithStatusAndMessage.toString()); + + final String gsonOfWithStatusAndMessage = gson.toJson(ofWithStatusAndMessage); + assertEquals("{\"status\":108,\"message\":\"This is a message.\"}", gsonOfWithStatusAndMessage); } @Test - void testError_Status_And_NullMessage() throws Exception { - // TODO + void testOf_WithStatusAndMessage_AndNullData() throws Exception { + final UnifiedResponse ofWithStatusAndMessageAndNullData = UnifiedResponse.of(108, "This is a message.", null); + assertEquals(108, ofWithStatusAndMessageAndNullData.getStatus()); + assertEquals("This is a message.", ofWithStatusAndMessageAndNullData.getMessage()); + assertNull(ofWithStatusAndMessageAndNullData.getData()); + + final String jacksonOfWithStatusAndMessageAndNullData = jackson.writeValueAsString(ofWithStatusAndMessageAndNullData); + log.info("jacksonOfWithStatusAndMessage: {}", jacksonOfWithStatusAndMessageAndNullData); + assertEquals("{\"status\":108,\"message\":\"This is a message.\"}", jacksonOfWithStatusAndMessageAndNullData); + + assertEquals("{status: 108, message: \"This is a message.\", data: null}", ofWithStatusAndMessageAndNullData.toString()); + + final String gsonOfWithStatusAndMessageAndNullData = gson.toJson(ofWithStatusAndMessageAndNullData); + assertEquals("{\"status\":108,\"message\":\"This is a message.\"}", gsonOfWithStatusAndMessageAndNullData); } @Test - void testError_Message() throws Exception { - // TODO + void testOf_WithStatusAndMessage_AndData() throws Exception { + final PageResult emptyPageResult = PageResult.empty(); + final UnifiedResponse ofWithStatusAndMessageAndData + = UnifiedResponse.of(108, "This is a message.", emptyPageResult); + assertEquals("{status: 108, message: \"This is a message.\", data: PageResult [total=0, content=[]]}", + ofWithStatusAndMessageAndData.toString()); + assertEquals(108, ofWithStatusAndMessageAndData.getStatus()); + assertEquals("This is a message.", ofWithStatusAndMessageAndData.getMessage()); + assertEquals(emptyPageResult, ofWithStatusAndMessageAndData.getData()); + final String jacksonOfWithStatusAndMessageAndData = jackson.writeValueAsString(ofWithStatusAndMessageAndData); + assertEquals("{\"status\":108,\"message\":\"This is a message.\",\"data\":{\"total\":0,\"content\":[]}}", + jacksonOfWithStatusAndMessageAndData); + final String gsonOfWithStatusAndMessageAndData = gson.toJson(ofWithStatusAndMessageAndData); + assertEquals("{\"status\":108,\"message\":\"This is a message.\",\"data\":{\"total\":0,\"content\":[]}}", + gsonOfWithStatusAndMessageAndData); } @Test - void testError_NullMessage() throws Exception { - // TODO + void testOf_WithStatusAndNullMessage() throws Exception { + UnifiedResponse ofWithStatusAndNullMessage = UnifiedResponse.of(108, (String) null); + assertEquals(108, ofWithStatusAndNullMessage.getStatus()); + assertEquals("", ofWithStatusAndNullMessage.getMessage()); + assertNull(ofWithStatusAndNullMessage.getData()); + + final String jacksonOfWithStatusAndNullMessage = jackson.writeValueAsString(ofWithStatusAndNullMessage); + assertEquals("{\"status\":108,\"message\":\"\"}", jacksonOfWithStatusAndNullMessage); + + final String gsonOfWithStatusAndNullMessage = gson.toJson(ofWithStatusAndNullMessage); + assertEquals("{\"status\":108,\"message\":\"\"}", gsonOfWithStatusAndNullMessage); } @Test - void testError_EmptyMessage() throws Exception { - // TODO + void testOf_WithStatusAndNullMessage_AndNullData() throws Exception { + UnifiedResponse ofWithStatusAndNullMessageAndNullData = UnifiedResponse.of(108, (String) null, null); + + assertEquals(108, ofWithStatusAndNullMessageAndNullData.getStatus()); + assertEquals("", ofWithStatusAndNullMessageAndNullData.getMessage()); + assertNull(ofWithStatusAndNullMessageAndNullData.getData()); + + final String jacksonOfWithStatusAndNullMessageAndNullData = jackson.writeValueAsString(ofWithStatusAndNullMessageAndNullData); + assertEquals("{\"status\":108,\"message\":\"\"}", jacksonOfWithStatusAndNullMessageAndNullData); + + final String gsonOfWithStatusAndNullMessageAndNullData = gson.toJson(ofWithStatusAndNullMessageAndNullData); + assertEquals("{\"status\":108,\"message\":\"\"}", gsonOfWithStatusAndNullMessageAndNullData); } @Test - void testOf() { + void testOf_WithStatusAndNullMessage_AndData() throws Exception { + PageResult emptyPageResult = PageResult.empty(); + UnifiedResponse ofWithStatusAndNullMessageAndData = UnifiedResponse.of(108, (String) null, emptyPageResult); + assertEquals(108, ofWithStatusAndNullMessageAndData.getStatus()); + assertEquals("", ofWithStatusAndNullMessageAndData.getMessage()); + assertEquals(emptyPageResult, ofWithStatusAndNullMessageAndData.getData()); + final String jacksonOfWithStatusAndNullMessageAndData = jackson.writeValueAsString(ofWithStatusAndNullMessageAndData); + assertEquals("{\"status\":108,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonOfWithStatusAndNullMessageAndData); + final String gsonOfWithStatusAndNullMessageAndData = gson.toJson(ofWithStatusAndNullMessageAndData); + assertEquals("{\"status\":108,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonOfWithStatusAndNullMessageAndData); + } + @Test + void testOf_WithStatusAndEmptyMessage() throws Exception { + UnifiedResponse ofWithStatusAndEmptyMessage = UnifiedResponse.of(108, ""); + assertEquals(108, ofWithStatusAndEmptyMessage.getStatus()); + assertEquals("", ofWithStatusAndEmptyMessage.getMessage()); + assertNull(ofWithStatusAndEmptyMessage.getData()); + + final String jacksonOfWithStatusAndEmptyMessage = jackson.writeValueAsString(ofWithStatusAndEmptyMessage); + assertEquals("{\"status\":108,\"message\":\"\"}", jacksonOfWithStatusAndEmptyMessage); + + final String gsonOfWithStatusAndEmptyMessage = gson.toJson(ofWithStatusAndEmptyMessage); + assertEquals("{\"status\":108,\"message\":\"\"}", gsonOfWithStatusAndEmptyMessage); + } + + @Test + void testOf_WithStatusAndEmptyMessage_AndNullData() throws Exception { + UnifiedResponse ofWithStatusAndEmptyMessageAndNullData = UnifiedResponse.of(108, "", null); + + assertEquals(108, ofWithStatusAndEmptyMessageAndNullData.getStatus()); + assertEquals("", ofWithStatusAndEmptyMessageAndNullData.getMessage()); + assertNull(ofWithStatusAndEmptyMessageAndNullData.getData()); + + final String jacksonOfWithStatusAndEmptyMessageAndNullData = jackson.writeValueAsString(ofWithStatusAndEmptyMessageAndNullData); + assertEquals("{\"status\":108,\"message\":\"\"}", jacksonOfWithStatusAndEmptyMessageAndNullData); + + final String gsonOfWithStatusAndEmptyMessageAndNullData = gson.toJson(ofWithStatusAndEmptyMessageAndNullData); + assertEquals("{\"status\":108,\"message\":\"\"}", gsonOfWithStatusAndEmptyMessageAndNullData); + } + + @Test + void testOf_WithStatusAndEmptyMessage_AndData() throws Exception { + PageResult emptyPageResult = PageResult.empty(); + UnifiedResponse ofWithStatusAndEmptyMessageAndData = UnifiedResponse.of(108, "", emptyPageResult); + assertEquals(108, ofWithStatusAndEmptyMessageAndData.getStatus()); + assertEquals("", ofWithStatusAndEmptyMessageAndData.getMessage()); + assertEquals(emptyPageResult, ofWithStatusAndEmptyMessageAndData.getData()); + final String jacksonOfWithStatusAndEmptyMessageAndData = jackson.writeValueAsString(ofWithStatusAndEmptyMessageAndData); + assertEquals("{\"status\":108,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonOfWithStatusAndEmptyMessageAndData); + final String gsonOfWithStatusAndEmptyMessageAndData = gson.toJson(ofWithStatusAndEmptyMessageAndData); + assertEquals("{\"status\":108,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonOfWithStatusAndEmptyMessageAndData); + } + + @Test + void testOf_WithNullStatus() { + final Object nullStatus = null; + final String nullMessage = null; + final User user = new User("zhouxy", "zhouxy@gmail.com"); + + // message + assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "查询失败")); + assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "查询失败", null)); + assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "查询失败", user)); + + // empty message + assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "")); + assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "", null)); + assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "", user)); + + // null message + assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, nullMessage)); + assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "查询失败", null)); + assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "查询失败", user)); } @Data From 37263c69332f5d684969e95100666c92bd00a949 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 29 Dec 2024 00:29:28 +0800 Subject: [PATCH 13/24] =?UTF-8?q?=E8=A1=A5=E5=85=85=20copyright?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commons/collection/CollectionToolsTests.java | 16 ++++++++++++++++ .../plusone/commons/util/EnumToolsTests.java | 16 ++++++++++++++++ .../plusone/commons/util/IdGeneratorTests.java | 16 ++++++++++++++++ .../plusone/commons/util/RandomToolsTests.java | 16 ++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java index 0b54e2d..bcb53a6 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package xyz.zhouxy.plusone.commons.collection; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/EnumToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/EnumToolsTests.java index a4c83fb..d4442cd 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/EnumToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/EnumToolsTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package xyz.zhouxy.plusone.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java index 55edba9..7447e48 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package xyz.zhouxy.plusone.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java index fcf5f44..2f63bc6 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package xyz.zhouxy.plusone.commons.util; import static org.junit.jupiter.api.Assertions.assertAll; From 1f8036e1703bcbc842ddc89a95fe1649c9ee78d7 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 29 Dec 2024 11:32:21 +0800 Subject: [PATCH 14/24] =?UTF-8?q?=E6=A3=80=E6=9F=A5=E5=B9=B6=E5=AE=8C?= =?UTF-8?q?=E5=96=84=20BigDecimals=E3=80=81Numbers=E3=80=81OptionalTools?= =?UTF-8?q?=20=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commons/util/BigDecimalsTests.java | 7 ++-- .../plusone/commons/util/NumbersTests.java | 2 - .../commons/util/OptionalToolsTests.java | 40 +++++++++++++++---- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/BigDecimalsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/BigDecimalsTests.java index 9a85b06..2fc6a0c 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/BigDecimalsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/BigDecimalsTests.java @@ -27,8 +27,6 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class BigDecimalsTests { - // TODO 【优化】 检查测试用例 - @Test void equalsValue_NullValues_ReturnsTrue() { assertTrue(BigDecimals.equalsValue(null, null)); @@ -176,7 +174,8 @@ public class BigDecimalsTests { BigDecimal bd1 = new BigDecimal("10"); BigDecimal bd2 = new BigDecimal("20"); BigDecimal bd3 = new BigDecimal("30"); - assertEquals(new BigDecimal("60"), BigDecimals.sum(bd1, bd2, bd3)); + BigDecimal bd4 = null; + assertEquals(new BigDecimal("60"), BigDecimals.sum(bd1, bd2, bd3, bd4)); } @Test @@ -192,6 +191,8 @@ public class BigDecimalsTests { @Test void of_BlankString_ReturnsZero() { + assertEquals(BigDecimal.ZERO, BigDecimals.of(null)); + assertEquals(BigDecimal.ZERO, BigDecimals.of("")); assertEquals(BigDecimal.ZERO, BigDecimals.of(" ")); } diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/NumbersTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/NumbersTests.java index d09300f..1e2435f 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/NumbersTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/NumbersTests.java @@ -24,8 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class NumbersTests { - // TODO 【优化】 检查测试用例 - @Test public void sum_ShortArray_ReturnsCorrectSum() { short[] numbers = {1, 2, 3, 4}; diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/OptionalToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/OptionalToolsTests.java index 47ba4db..79199e4 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/OptionalToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/OptionalToolsTests.java @@ -19,6 +19,7 @@ package xyz.zhouxy.plusone.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Optional; @@ -28,11 +29,12 @@ import java.util.OptionalLong; import org.junit.jupiter.api.Test; +/** + * {@link OptionalTools} 单元测试 + */ public class OptionalToolsTests { - // TODO 【优化】 检查测试用例 - @Test void optionalOf_NullInteger_ReturnsEmptyOptionalInt() { OptionalInt result = OptionalTools.optionalOf((Integer) null); @@ -112,8 +114,14 @@ class OptionalToolsTests { } @Test - void orElseNull_NullOptional_ReturnsNull() { - Object result = OptionalTools.orElseNull(Optional.ofNullable(null)); + void orElseNull_NullOptional_ThrowsNullPointerException() { + assertThrows(NullPointerException.class, + () -> OptionalTools.orElseNull(null)); + } + + @Test + void orElseNull_EmptyOptional_ReturnsNull() { + Object result = OptionalTools.orElseNull(Optional.empty()); assertNull(result); } @@ -124,7 +132,13 @@ class OptionalToolsTests { } @Test - void toInteger_NullOptionalInt_ReturnsNull() { + void toInteger_NullOptionalInt_ThrowsNullPointerException() { + assertThrows(NullPointerException.class, + () -> OptionalTools.toInteger(null)); + } + + @Test + void toInteger_EmptyOptionalInt_ReturnsNull() { Integer result = OptionalTools.toInteger(OptionalInt.empty()); assertNull(result); } @@ -136,7 +150,13 @@ class OptionalToolsTests { } @Test - void toLong_NullOptionalLong_ReturnsNull() { + void toLong_NullOptionalLong_ThrowsNullPointerException() { + assertThrows(NullPointerException.class, + () -> OptionalTools.toLong(null)); + } + + @Test + void toLong_EmptyOptionalLong_ReturnsNull() { Long result = OptionalTools.toLong(OptionalLong.empty()); assertNull(result); } @@ -148,7 +168,13 @@ class OptionalToolsTests { } @Test - void toDouble_NullOptionalDouble_ReturnsNull() { + void toDouble_NullOptionalDouble_ThrowsNullPointerException() { + assertThrows(NullPointerException.class, + () -> OptionalTools.toDouble(null)); + } + + @Test + void toDouble_EmptyOptionalDouble_ReturnsNull() { Double result = OptionalTools.toDouble(OptionalDouble.empty()); assertNull(result); } From 8411e75274f373c519410bf5f7e26eb02c85600e Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 29 Dec 2024 11:37:15 +0800 Subject: [PATCH 15/24] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=84=8F=E4=B9=89?= =?UTF-8?q?=E4=B8=8D=E5=A4=A7=E7=9A=84=20MapWrapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../collection/AbstractMapWrapper.java | 234 ------------------ .../commons/collection/MapWrapper.java | 95 ------- 2 files changed, 329 deletions(-) delete mode 100644 src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java delete mode 100644 src/main/java/xyz/zhouxy/plusone/commons/collection/MapWrapper.java diff --git a/src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java b/src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java deleted file mode 100644 index 04a974b..0000000 --- a/src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package xyz.zhouxy.plusone.commons.collection; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Function; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.common.annotations.Beta; - -import xyz.zhouxy.plusone.commons.util.AssertTools; - -/** - * AbstractMapWrapper - * - *

- * 包装了一个 {@link Map}。 - * 主要在于获取值时,如果 Key 不存在,是抛出异常;如果 Key 存在,则将 value 包装在 {@link Optional} 中 返回。 - * 此外可以定义 Key 和 Value 的检查规则。 - *

- * - * @author ZhouXY - * @since 1.0 - */ -@Beta -public abstract class AbstractMapWrapper> { - - private final Map map; - private final Consumer keyChecker; - private final Consumer valueChecker; - - protected AbstractMapWrapper(Map map, - @Nullable Consumer keyChecker, - @Nullable Consumer valueChecker) { - this.map = map; - this.keyChecker = keyChecker; - this.valueChecker = valueChecker; - } - - public final T put(K key, V value) { - if (this.keyChecker != null) { - this.keyChecker.accept(key); - } - if (this.valueChecker != null) { - this.valueChecker.accept(value); - } - this.map.put(key, value); - return getSelf(); - } - - public final T putAll(Map m) { - m.forEach(this::put); - return getSelf(); - } - - /** - * 获取 {@code map} 中的值。如果 {@code key} 不存在,则抛出异常。 - * 将 {@code value}(可为 {@code null})装进 {@link Optional} 中后返回。 - * 为了这碟醋包的这盘饺子。 - * - * @param key 键 - * @return 可缺失的值 - * @throws IllegalArgumentException key 不存在时抛出。 - */ - @Nonnull - public Optional get(K key) { - AssertTools.checkArgument(this.map.containsKey(key), "Key does not exist"); - return Optional.ofNullable(this.map.get(key)); - } - - @SuppressWarnings("unchecked") - public final Optional getAndConvert(K key) { - return get(key).map(v -> (R) v); - } - - public final Optional getAndConvert(K key, Function mapper) { - return get(key).map(mapper); - } - - public final boolean containsKey(Object key) { - return this.map.containsKey(key); - } - - public final int size() { - return this.map.size(); - } - - public final Set keySet() { - return this.map.keySet(); - } - - public final Collection values() { - return this.map.values(); - } - - public final Set> entrySet() { - return this.map.entrySet(); - } - - public final void clear() { - this.map.clear(); - } - - public final boolean containsValue(Object value) { - return this.map.containsValue(value); - } - - public final boolean isEmpty() { - return this.map.isEmpty(); - } - - public final V remove(Object key) { - return this.map.remove(key); - } - - public final V putIfAbsent(K key, V value) { - if (this.keyChecker != null) { - this.keyChecker.accept(key); - } - if (this.valueChecker != null) { - this.valueChecker.accept(value); - } - return this.map.putIfAbsent(key, value); - } - - public final V computeIfAbsent(K key, Function mappingFunction) { - if (this.keyChecker != null) { - this.keyChecker.accept(key); - } - Function func = (K k) -> { - V value = mappingFunction.apply(k); - if (this.valueChecker != null) { - this.valueChecker.accept(value); - } - return value; - }; - return this.map.computeIfAbsent(key, func); - } - - public final Map exportMap() { - return this.map; - } - - public final Map exportUnmodifiableMap() { - return Collections.unmodifiableMap(this.map); - } - - protected abstract T getSelf(); - - @Override - public String toString() { - return this.map.toString(); - } - - @Override - public int hashCode() { - return Objects.hash(map); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - AbstractMapWrapper other = (AbstractMapWrapper) obj; - return Objects.equals(map, other.map); - } - - public abstract static class Builder> { - protected final Map map; - protected Consumer keyChecker; - protected Consumer valueChecker; - - protected Builder(Map map) { - this.map = map; - } - - public Builder keyChecker(@Nullable Consumer keyChecker) { - this.keyChecker = keyChecker; - return this; - } - - public Builder valueChecker(@Nullable Consumer valueChecker) { - this.valueChecker = valueChecker; - return this; - } - - public Builder put(K key, V value) { - if (this.keyChecker != null) { - this.keyChecker.accept(key); - } - if (this.valueChecker != null) { - this.valueChecker.accept(value); - } - this.map.put(key, value); - return this; - } - - public Builder putAll(Map m) { - m.forEach(this::put); - return this; - } - - public abstract T build(); - - public abstract T buildUnmodifiableMap(); - } -} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/collection/MapWrapper.java b/src/main/java/xyz/zhouxy/plusone/commons/collection/MapWrapper.java deleted file mode 100644 index 026bd9c..0000000 --- a/src/main/java/xyz/zhouxy/plusone/commons/collection/MapWrapper.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package xyz.zhouxy.plusone.commons.collection; - -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.function.Consumer; - -import com.google.common.annotations.Beta; - -/** - * MapWrapper - * - *

- * Map 包装器的默认实现 - *

- * - * @author ZhouXY - * @since 0.1.0 - */ -@Beta -public final class MapWrapper extends AbstractMapWrapper> { - - private MapWrapper(Map map, Consumer keyChecker, Consumer valueChecker) { - super(map, keyChecker, valueChecker); - } - - public static Builder wrap(Map map) { - return new Builder<>(map); - } - - public static Builder wrapHashMap() { - return new Builder<>(new HashMap<>()); - } - - public static Builder wrapHashMap(int initialCapacity) { - return new Builder<>(new HashMap<>(initialCapacity)); - } - - public static Builder wrapHashMap(int initialCapacity, float loadFactor) { - return new Builder<>(new HashMap<>(initialCapacity, loadFactor)); - } - - public static , V> Builder wrapTreeMap() { - return new Builder<>(new TreeMap<>()); - } - - public static Builder wrapTreeMap(SortedMap m) { - return new Builder<>(new TreeMap<>(m)); - } - - public static Builder wrapTreeMap(Comparator comparator) { - return new Builder<>(new TreeMap<>(comparator)); - } - - public static final class Builder extends AbstractMapWrapper.Builder> { - - private Builder(Map map) { - super(map); - } - - @Override - public MapWrapper build() { - return new MapWrapper<>(map, keyChecker, valueChecker); - } - - @Override - public MapWrapper buildUnmodifiableMap() { - return new MapWrapper<>(Collections.unmodifiableMap(map), keyChecker, valueChecker); - } - } - - @Override - protected MapWrapper getSelf() { - return this; - } -} From 1727af5940de78f33b89bc3b5ced7b705af30a24 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 29 Dec 2024 11:41:28 +0800 Subject: [PATCH 16/24] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20ProgressOfTesting.tx?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index 4e65a24..cd77b87 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,7 +1,7 @@ -[ ] 未开始测试 - 5 (7.81%) -[-] 测试未完成 - 5 (7.81%) -[Y] 测试完成 - 32 (50.00%) -[x] 无需测试 - 22 (34.38%) +[ ] 未开始测试 - 3 (4.84%) +[-] 测试未完成 - 5 (8.06%) +[Y] 测试完成 - 32 (51.61%) +[x] 无需测试 - 22 (35.48%) xyz.zhouxy.plusone.commons ├───annotation @@ -24,9 +24,7 @@ xyz.zhouxy.plusone.commons │ Ref.java [Y] │ ├───collection - │ AbstractMapWrapper.java [ ] │ CollectionTools.java [Y] - │ MapWrapper.java [ ] │ ├───constant │ PatternConsts.java [ ] From 979eedabb17153e9de669a271dbcf48d48dce008 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 29 Dec 2024 15:33:33 +0800 Subject: [PATCH 17/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=AD=A3=E5=88=99?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E7=9B=B8=E5=85=B3=E6=B5=8B=E8=AF=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 68 ++--- .../commons/constant/PatternConsts.java | 57 ++++- .../plusone/commons/constant/RegexConsts.java | 10 +- .../plusone/commons/util/RegexTools.java | 17 -- .../commons/constant/PatternConstsTests.java | 235 ++++++++++++++++++ .../plusone/commons/util/RegexToolsTests.java | 165 ++++++++++++ 6 files changed, 486 insertions(+), 66 deletions(-) create mode 100644 src/test/java/xyz/zhouxy/plusone/commons/constant/PatternConstsTests.java create mode 100644 src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index cd77b87..ff0f1b7 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,16 +1,16 @@ -[ ] 未开始测试 - 3 (4.84%) -[-] 测试未完成 - 5 (8.06%) -[Y] 测试完成 - 32 (51.61%) -[x] 无需测试 - 22 (35.48%) +[ ] 未开始测试 - 0 (0.00%) +[*] 测试未完成 - 5 (8.06%) +[Y] 测试完成 - 35 (56.45%) +[-] 无需测试 - 22 (35.48%) xyz.zhouxy.plusone.commons ├───annotation - │ ReaderMethod.java [x] - │ StaticFactoryMethod.java [x] - │ UnsupportedOperation.java [x] - │ ValueObject.java [x] - │ Virtual.java [x] - │ WriterMethod.java [x] + │ ReaderMethod.java [-] + │ StaticFactoryMethod.java [-] + │ UnsupportedOperation.java [-] + │ ValueObject.java [-] + │ Virtual.java [-] + │ WriterMethod.java [-] │ ├───base │ BoolRef.java [Y] @@ -27,46 +27,46 @@ xyz.zhouxy.plusone.commons │ CollectionTools.java [Y] │ ├───constant - │ PatternConsts.java [ ] - │ RegexConsts.java [ ] + │ PatternConsts.java [Y] + │ RegexConsts.java [Y] │ ├───exception - │ │ DataNotExistsException.java [x] + │ │ DataNotExistsException.java [-] │ │ ParsingFailureException.java [Y] │ │ ExceptionType.java [Y] │ │ │ ├───business - │ │ BizException.java [x] + │ │ BizException.java [-] │ │ InvalidInputException.java [Y] - │ │ RequestParamsException.java [x] + │ │ RequestParamsException.java [-] │ │ │ └───system - │ DataOperationResultException.java [x] - │ NoAvailableMacFoundException.java [x] - │ SysException.java [x] + │ DataOperationResultException.java [-] + │ NoAvailableMacFoundException.java [-] + │ SysException.java [-] │ ├───function - │ BoolUnaryOperator.java [x] - │ CharUnaryOperator.java [x] - │ Executable.java [x] - │ OptionalSupplier.java [x] + │ BoolUnaryOperator.java [-] + │ CharUnaryOperator.java [-] + │ Executable.java [-] + │ OptionalSupplier.java [-] │ PredicateTools.java [Y] - │ ThrowingConsumer.java [x] - │ ThrowingPredicate.java [x] - │ ThrowingSupplier.java [x] - │ ToOptionalBiFunction.java [x] - │ ToOptionalFunction.java [x] + │ ThrowingConsumer.java [-] + │ ThrowingPredicate.java [-] + │ ThrowingSupplier.java [-] + │ ToOptionalBiFunction.java [-] + │ ToOptionalFunction.java [-] │ ├───model │ │ Chinese2ndGenIDCardNumber.java [Y] - │ │ Gender.java [x] + │ │ Gender.java [-] │ │ IDCardNumber.java [Y] │ │ ValidatableStringRecord.java [Y] │ │ │ └───dto - │ PageResult.java [-] - │ PagingAndSortingQueryParams.java [-] - │ PagingParams.java [-] + │ PageResult.java [*] + │ PagingAndSortingQueryParams.java [*] + │ PagingParams.java [*] │ UnifiedResponse.java [Y] │ ├───time @@ -74,10 +74,10 @@ xyz.zhouxy.plusone.commons │ YearQuarter.java [Y] │ └───util - ArrayTools.java [-] + ArrayTools.java [*] 61 AssertTools.java [Y] BigDecimals.java [Y] - DateTimeTools.java [-] + DateTimeTools.java [*] 83 Enumeration.java [Y] EnumTools.java [Y] IdGenerator.java [Y] @@ -85,7 +85,7 @@ xyz.zhouxy.plusone.commons Numbers.java [Y] OptionalTools.java [Y] RandomTools.java [Y] - RegexTools.java [ ] + RegexTools.java [Y] SnowflakeIdGenerator.java [Y] StringTools.java [Y] TreeBuilder.java [Y] diff --git a/src/main/java/xyz/zhouxy/plusone/commons/constant/PatternConsts.java b/src/main/java/xyz/zhouxy/plusone/commons/constant/PatternConsts.java index ad5b555..4d8e03b 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/constant/PatternConsts.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/constant/PatternConsts.java @@ -27,33 +27,70 @@ import java.util.regex.Pattern; */ public final class PatternConsts { - /** yyyyMMdd */ + /** + * yyyyMMdd + * + * @see RegexConsts#BASIC_ISO_DATE + *

+ */ public static final Pattern BASIC_ISO_DATE = Pattern.compile(RegexConsts.BASIC_ISO_DATE); - /** yyyy-MM-dd */ + /** + * yyyy-MM-dd + * + * @see RegexConsts#ISO_LOCAL_DATE + */ public static final Pattern ISO_LOCAL_DATE = Pattern.compile(RegexConsts.ISO_LOCAL_DATE); - /** 密码 */ + /** + * 密码 + * + * @see RegexConsts#PASSWORD + */ public static final Pattern PASSWORD = Pattern.compile(RegexConsts.PASSWORD); - /** 验证码 */ + /** + * 验证码 + * + * @see RegexConsts#CAPTCHA + */ public static final Pattern CAPTCHA = Pattern.compile(RegexConsts.CAPTCHA); - /** 邮箱地址 */ + /** + * 邮箱地址 + * + * @see RegexConsts#EMAIL + */ public static final Pattern EMAIL = Pattern.compile(RegexConsts.EMAIL); - /** 中国大陆手机号 */ + /** + * 中国大陆手机号 + * + * @see RegexConsts#MOBILE_PHONE + */ public static final Pattern MOBILE_PHONE = Pattern.compile(RegexConsts.MOBILE_PHONE); - /** 用户名 */ + /** + * 用户名 + * + * @see RegexConsts#USERNAME + */ public static final Pattern USERNAME = Pattern.compile(RegexConsts.USERNAME); - /** 昵称 */ + /** + * 昵称 + * + * @see RegexConsts#NICKNAME + */ public static final Pattern NICKNAME = Pattern.compile(RegexConsts.NICKNAME); - /** 中国第二代居民身份证 */ + /** + * 中国第二代居民身份证 + * + * @see RegexConsts#CHINESE_2ND_ID_CARD_NUMBER + */ public static final Pattern CHINESE_2ND_ID_CARD_NUMBER - = Pattern.compile(RegexConsts.CHINESE_2ND_ID_CARD_NUMBER); + = Pattern.compile(RegexConsts.CHINESE_2ND_ID_CARD_NUMBER, Pattern.CASE_INSENSITIVE); private PatternConsts() { throw new IllegalStateException("Utility class"); diff --git a/src/main/java/xyz/zhouxy/plusone/commons/constant/RegexConsts.java b/src/main/java/xyz/zhouxy/plusone/commons/constant/RegexConsts.java index f1bad7f..9730ce1 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/constant/RegexConsts.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/constant/RegexConsts.java @@ -24,9 +24,9 @@ package xyz.zhouxy.plusone.commons.constant; */ public final class RegexConsts { - public static final String BASIC_ISO_DATE = "^(?\\d{4})(?\\d{2})(?\\d{2})"; + public static final String BASIC_ISO_DATE = "^(?\\d{4,9})(?\\d{2})(?
\\d{2})"; - public static final String ISO_LOCAL_DATE = "^(?\\d{4})-(?\\d{2})-(?\\d{2})"; + public static final String ISO_LOCAL_DATE = "^(?\\d{4,9})-(?\\d{2})-(?
\\d{2})"; public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])[\\w\\\\!#$%&'*\\+\\-/=?^`{|}~@\\(\\)\\[\\]\",\\.;':><]{8,32}$"; @@ -38,12 +38,12 @@ public final class RegexConsts { public static final String MOBILE_PHONE = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$"; - public static final String USERNAME = "^[\\w_.@\\\\]{4,36}$"; + public static final String USERNAME = "^[\\w-_.@]{4,36}$"; - public static final String NICKNAME = "^[\\w_.@\\\\]{4,36}$"; + public static final String NICKNAME = "^[\\w-_.@]{4,36}$"; public static final String CHINESE_2ND_ID_CARD_NUMBER - = "^(?(?(?\\d{2})\\d{2})\\d{2})(?\\d{8})\\d{2}(?\\d)([\\dXx])$"; + = "^(?(?(?\\d{2})\\d{2})\\d{2})(?\\d{8})\\d{2}(?\\d)([\\dX])$"; private RegexConsts() { throw new IllegalStateException("Utility class"); diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/RegexTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/RegexTools.java index 34dfb5c..53551f8 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/RegexTools.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/RegexTools.java @@ -89,22 +89,6 @@ public final class RegexTools { return getPatternsInternal(patterns); } - /** - * 手动缓存 Pattern 实例。 - * - * @param pattern 要缓存的 {@link Pattern} 实例 - * @return 缓存的 Pattern 实例。如果缓存已满,则返回 {@code null}。 - */ - public static Pattern cachePattern(final Pattern pattern) { - AssertTools.checkNotNull(pattern, "The pattern can not be null."); - if (PATTERN_CACHE.size() >= MAX_CACHE_SIZE) { - return null; - } - final String patternStr = pattern.pattern(); - final Pattern pre = PATTERN_CACHE.putIfAbsent(patternStr, pattern); - return pre != null ? pre : pattern; - } - /** * 判断 {@code input} 是否匹配 {@code pattern}。 * @@ -285,7 +269,6 @@ public final class RegexTools { * 获取 {@link Pattern} 实例。 * * @param pattern 正则表达式 - * @param cachePattern 是否缓存 {@link Pattern} 实例 * @return {@link Pattern} 实例 */ @Nonnull diff --git a/src/test/java/xyz/zhouxy/plusone/commons/constant/PatternConstsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/constant/PatternConstsTests.java new file mode 100644 index 0000000..88304d6 --- /dev/null +++ b/src/test/java/xyz/zhouxy/plusone/commons/constant/PatternConstsTests.java @@ -0,0 +1,235 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.zhouxy.plusone.commons.constant; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.regex.Matcher; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public // +class PatternConstsTests { + + // ================================ + // #region - BASIC_ISO_DATE + // ================================ + + @Test + void testBasicIsoDate_ValidDate() { + Matcher matcher = PatternConsts.BASIC_ISO_DATE.matcher("20241229"); + assertTrue(matcher.matches()); + + assertEquals("2024", matcher.group(1)); + assertEquals("12", matcher.group(2)); + assertEquals("29", matcher.group(3)); + assertEquals("2024", matcher.group("yyyy")); + assertEquals("12", matcher.group("MM")); + assertEquals("29", matcher.group("dd")); + + // LeapYearFeb29() + assertTrue(PatternConsts.BASIC_ISO_DATE.matcher("20200229").matches()); + + // BoundaryMin() + assertTrue(PatternConsts.BASIC_ISO_DATE.matcher("00000101").matches()); + + // BoundaryMax() + assertTrue(PatternConsts.BASIC_ISO_DATE.matcher("9999999991231").matches()); + } + + @ParameterizedTest + @ValueSource(strings = { + "20231301", // InvalidMonth + "20230230", // InvalidDay + "20210229", // NonLeapYearFeb29 + }) + void testBasicIsoDate_InvalidDate_butMatches(String date) { + // 虽然日期有误,但这个正则无法判断。实际工作中,应使用日期时间 API。 + Matcher matcher = PatternConsts.BASIC_ISO_DATE.matcher(date); + assertTrue(matcher.matches()); + } + + @ParameterizedTest + @ValueSource(strings = { + "2023041", // TooShort + "99999999990415", // TooLong + "2023-04-15", // NonNumeric + }) + void testBasicIsoDate_InvalidDate_Mismatches(String date) { + Matcher matcher = PatternConsts.BASIC_ISO_DATE.matcher(date); + assertFalse(matcher.matches()); + } + + // ================================ + // #endregion - BASIC_ISO_DATE + // ================================ + + // ================================ + // #region - ISO_LOCAL_DATE + // ================================ + + @Test + void testIsoLocalDate_ValidDate() { + Matcher matcher = PatternConsts.ISO_LOCAL_DATE.matcher("2024-12-29"); + assertTrue(matcher.matches()); + assertEquals("2024", matcher.group("yyyy")); + assertEquals("12", matcher.group("MM")); + assertEquals("29", matcher.group("dd")); + + // LeapYearFeb29() + assertTrue(PatternConsts.ISO_LOCAL_DATE.matcher("2020-02-29").matches()); + + // BoundaryMin() + assertTrue(PatternConsts.ISO_LOCAL_DATE.matcher("0000-01-01").matches()); + + // BoundaryMax() + assertTrue(PatternConsts.ISO_LOCAL_DATE.matcher("999999999-12-31").matches()); + } + + @ParameterizedTest + @ValueSource(strings = { + "2023-13-01", // InvalidMonth + "2023-02-30", // InvalidDay + "2021-02-29", // NonLeapYearFeb29 + }) + void testIsoLocalDate_InvalidDate_butMatches(String date) { + // 虽然日期有误,但这个正则无法判断。实际工作中,应使用日期时间 API。 + Matcher matcher = PatternConsts.ISO_LOCAL_DATE.matcher(date); + assertTrue(matcher.matches()); + } + + @ParameterizedTest + @ValueSource(strings = { + "2023-04-1", // TooShort + "9999999999-04-15", // TooLong + "20230415", + }) + void testIsoLocalDate_InvalidDate_Mismatches(String date) { + Matcher matcher = PatternConsts.ISO_LOCAL_DATE.matcher(date); + assertFalse(matcher.matches()); + } + + // ================================ + // #endregion - ISO_LOCAL_DATE + // ================================ + + // ================================ + // #region - PASSWORD + // ================================ + + @Test + void testPassword_ValidPassword_Matches() { + assertTrue(PatternConsts.PASSWORD.matcher("Abc123!@#").matches()); + } + + @Test + void testPassword_InvalidPassword_Mismatches() { + assertFalse(PatternConsts.PASSWORD.matcher("Abc123 !@#").matches()); // 带空格 + assertFalse(PatternConsts.PASSWORD.matcher("Abc123!@# ").matches()); // 带空格 + assertFalse(PatternConsts.PASSWORD.matcher(" Abc123!@#").matches()); // 带空格 + assertFalse(PatternConsts.PASSWORD.matcher(" Abc123!@# ").matches()); // 带空格 + assertFalse(PatternConsts.PASSWORD.matcher("77553366998844113322").matches()); // 纯数字 + assertFalse(PatternConsts.PASSWORD.matcher("poiujhgbfdsazxcfvghj").matches()); // 纯小写字母 + assertFalse(PatternConsts.PASSWORD.matcher("POIUJHGBFDSAZXCFVGHJ").matches()); // 纯大写字母 + assertFalse(PatternConsts.PASSWORD.matcher("!#$%&'*\\+-/=?^`{|}~@()[]\",.;':").matches()); // 纯特殊字符 + assertFalse(PatternConsts.PASSWORD.matcher("sdfrghbv525842582752").matches()); // 没有小写字母 + assertFalse(PatternConsts.PASSWORD.matcher("SDFRGHBV525842582752").matches()); // 没有小写字母 + assertFalse(PatternConsts.PASSWORD.matcher("sdfrghbvSDFRGHBV").matches()); // 没有数字 + assertFalse(PatternConsts.PASSWORD.matcher("Abc1!").matches()); // 太短 + assertFalse(PatternConsts.PASSWORD.matcher("Abc1!Abc1!Abc1!Abc1!Abc1!Abc1!Abc1!").matches()); // 太长 + assertFalse(PatternConsts.PASSWORD.matcher("").matches()); + assertFalse(PatternConsts.PASSWORD.matcher(" ").matches()); + } + + // ================================ + // #endregion - PASSWORD + // ================================ + + // ================================ + // #region - EMAIL + // ================================ + + @Test + public void testValidEmails() { + assertTrue(PatternConsts.EMAIL.matcher("test@example.com").matches()); + assertTrue(PatternConsts.EMAIL.matcher("user.name+tag+sorting@example.com").matches()); + assertTrue(PatternConsts.EMAIL.matcher("user@sub.example.com").matches()); + assertTrue(PatternConsts.EMAIL.matcher("user@123.123.123.123").matches()); + } + + @Test + public void testInvalidEmails() { + assertFalse(PatternConsts.EMAIL.matcher(".username@example.com").matches()); + assertFalse(PatternConsts.EMAIL.matcher("@missingusername.com").matches()); + assertFalse(PatternConsts.EMAIL.matcher("plainaddress").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username..username@example.com").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username.@example.com").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@-example.com").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@-example.com").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@.com.com").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@.com.my").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@.com").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@com.").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@com").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@example..com").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@example.com-").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@example.com.").matches()); + assertFalse(PatternConsts.EMAIL.matcher("username@example").matches()); + } + + // ================================ + // #endregion - EMAIL + // ================================ + + // ================================ + // #region - Chinese2ndIdCardNumber + // ================================ + + @ParameterizedTest + @ValueSource(strings = { + "44520019900101456X", + "44520019900101456x", + "445200199001014566", + }) + void testChinese2ndIdCardNumber_ValidChinese2ndIdCardNumber(String value) { + Matcher matcher = PatternConsts.CHINESE_2ND_ID_CARD_NUMBER.matcher(value); + assertTrue(matcher.matches()); + assertEquals("44", matcher.group("province")); + assertEquals("4452", matcher.group("city")); + assertEquals("445200", matcher.group("county")); + } + + @ParameterizedTest + @ValueSource(strings = { + "4452200199001014566", + "44520199001014566", + " ", + "", + }) + void testChinese2ndIdCardNumber_InvalidChinese2ndIdCardNumber(String value) { + assertFalse(PatternConsts.CHINESE_2ND_ID_CARD_NUMBER.matcher(value).matches()); + } + + // ================================ + // #endregion - Chinese2ndIdCardNumber + // ================================ +} diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java new file mode 100644 index 0000000..3968a24 --- /dev/null +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java @@ -0,0 +1,165 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.zhouxy.plusone.commons.util; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.junit.jupiter.api.Test; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public +class RegexToolsTests { + + @Test + void getPattern_CachePatternTrue_ReturnsCachedPattern() { + String pattern = "abc"; + Pattern cachedPattern = RegexTools.getPattern(pattern, true); + Pattern patternFromCache = RegexTools.getPattern(pattern, true); + assertSame(cachedPattern, patternFromCache, "Pattern should be cached"); + } + + @Test + void getPattern_CachePatternFalse_ReturnsNewPattern() { + String pattern = "abc"; + Pattern pattern1 = RegexTools.getPattern(pattern, false); + Pattern pattern2 = RegexTools.getPattern(pattern, false); + assertNotSame(pattern1, pattern2, "Pattern should not be cached"); + } + + @Test + void getPattern_NullPattern_ThrowsException() { + assertThrows(NullPointerException.class, () -> { + RegexTools.getPattern(null, true); + }); + } + + @Test + void getPatterns_CachePatternTrue_ReturnsCachedPatterns() { + String[] patterns = {"abc", "def"}; + Pattern[] cachedPatterns = RegexTools.getPatterns(patterns, true); + Pattern[] patternsFromCache = RegexTools.getPatterns(patterns, true); + assertSame(cachedPatterns[0], patternsFromCache[0]); + assertSame(cachedPatterns[1], patternsFromCache[1]); + } + + @Test + void getPatterns_CachePatternFalse_ReturnsNewPatterns() { + String[] patterns = {"abc", "def"}; + Pattern[] patterns1 = RegexTools.getPatterns(patterns, false); + Pattern[] patterns2 = RegexTools.getPatterns(patterns, false); + assertNotSame(patterns1[0], patterns2[0]); + assertNotSame(patterns1[1], patterns2[1]); + } + + @Test + void getPatterns_NullPatterns_ThrowsException() { + assertThrows(NullPointerException.class, () -> { + RegexTools.getPatterns(null, true); + }); + } + + @Test + void matches_InputMatchesPattern_ReturnsTrue() { + String pattern = "abc"; + Pattern compiledPattern = Pattern.compile(pattern); + assertTrue(RegexTools.matches("abc", compiledPattern), "Input should match pattern"); + } + + @Test + void matches_InputDoesNotMatchPattern_ReturnsFalse() { + String pattern = "abc"; + Pattern compiledPattern = Pattern.compile(pattern); + assertFalse(RegexTools.matches("abcd", compiledPattern), "Input should not match pattern"); + } + + @Test + void matches_NullInput_ReturnsFalse() { + String pattern = "abc"; + Pattern compiledPattern = Pattern.compile(pattern); + assertFalse(RegexTools.matches(null, compiledPattern), "Null input should return false"); + } + + @Test + void matchesOne_InputMatchesOnePattern_ReturnsTrue() { + String[] patterns = {"abc", "def"}; + Pattern[] compiledPatterns = new Pattern[patterns.length]; + for (int i = 0; i < patterns.length; i++) { + compiledPatterns[i] = Pattern.compile(patterns[i]); + } + assertTrue(RegexTools.matchesOne("abc", compiledPatterns), "Input should match one pattern"); + } + + @Test + void matchesOne_InputDoesNotMatchAnyPattern_ReturnsFalse() { + String[] patterns = {"abc", "def"}; + Pattern[] compiledPatterns = new Pattern[patterns.length]; + for (int i = 0; i < patterns.length; i++) { + compiledPatterns[i] = Pattern.compile(patterns[i]); + } + assertFalse(RegexTools.matchesOne("xyz", compiledPatterns), "Input should not match any pattern"); + } + + @Test + void matchesAll_InputMatchesAllPatterns_ReturnsTrue() { + String[] patterns = {"abc", "abc"}; + Pattern[] compiledPatterns = new Pattern[patterns.length]; + for (int i = 0; i < patterns.length; i++) { + compiledPatterns[i] = Pattern.compile(patterns[i]); + } + assertTrue(RegexTools.matchesAll("abc", compiledPatterns), "Input should match all patterns"); + } + + @Test + void matchesAll_InputDoesNotMatchAllPatterns_ReturnsFalse() { + String[] patterns = {"abc", "def"}; + Pattern[] compiledPatterns = new Pattern[patterns.length]; + for (int i = 0; i < patterns.length; i++) { + compiledPatterns[i] = Pattern.compile(patterns[i]); + } + assertFalse(RegexTools.matchesAll("abc", compiledPatterns), "Input should not match all patterns"); + } + + @Test + void getMatcher_ValidInputAndPattern_ReturnsMatcher() { + String pattern = "abc"; + Pattern compiledPattern = Pattern.compile(pattern); + Matcher matcher = RegexTools.getMatcher("abc", compiledPattern); + assertNotNull(matcher, "Matcher should not be null"); + } + + @Test + void getMatcher_NullInput_ThrowsException() { + String pattern = "abc"; + Pattern compiledPattern = Pattern.compile(pattern); + assertThrows(NullPointerException.class, () -> { + RegexTools.getMatcher(null, compiledPattern); + }); + } + + @Test + void getMatcher_NullPattern_ThrowsException() { + final Pattern pattern = null; + assertThrows(NullPointerException.class, () -> { + RegexTools.getMatcher("abc", pattern); + }); + } +} From a2482419e0960fe7aa05a4d234e5d27cc3ab84f3 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 29 Dec 2024 21:29:23 +0800 Subject: [PATCH 18/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=20DateTimeTools=20?= =?UTF-8?q?=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=E5=92=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=EF=BC=8C=E5=B9=B6=E4=BF=AE=E6=94=B9=E5=85=B6=E5=AE=83?= =?UTF-8?q?=E4=B8=8D=E5=90=88=E9=80=82=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plusone/commons/util/DateTimeTools.java | 25 +- .../commons/util/DateTimeToolsTests.java | 502 ++++++++++++++++-- .../commons/util/IdGeneratorTests.java | 13 +- .../plusone/commons/util/RegexToolsTests.java | 4 +- 4 files changed, 466 insertions(+), 78 deletions(-) diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java index 28e1989..e580706 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java @@ -16,6 +16,8 @@ package xyz.zhouxy.plusone.commons.util; +import static java.time.temporal.ChronoField.*; + import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; @@ -30,7 +32,6 @@ import java.util.Calendar; import java.util.Date; import java.util.TimeZone; -import com.google.common.base.Strings; import com.google.common.collect.Range; import xyz.zhouxy.plusone.commons.time.Quarter; @@ -46,29 +47,27 @@ public class DateTimeTools { // #region - toString public static String toYearString(int year) { - return Integer.toString(year); + return Integer.toString(YEAR.checkValidIntValue(year)); } public static String toYearString(Year year) { return year.toString(); } - public static String toMonthString(int monthValue) { - return Strings.padStart(Integer.toString(monthValue), 2, '0'); + public static String toMonthStringM(int monthValue) { + return Integer.toString(MONTH_OF_YEAR.checkValidIntValue(monthValue)); } - public static String toMonthString(int monthValue, boolean padStart) { - return padStart ? toMonthString(monthValue) : Integer.toString(monthValue); + public static String toMonthStringMM(int monthValue) { + return String.format("%02d", MONTH_OF_YEAR.checkValidIntValue(monthValue)); } - public static String toMonthString(Month month) { - final int monthValue = month.getValue(); - return Strings.padStart(Integer.toString(monthValue), 2, '0'); + public static String toMonthStringM(Month month) { + return Integer.toString(month.getValue()); } - public static String toMonthString(Month month, boolean padStart) { - final int monthValue = month.getValue(); - return padStart ? toMonthString(month) : Integer.toString(monthValue); + public static String toMonthStringMM(Month month) { + return String.format("%02d", month.getValue()); } // #endregion @@ -282,7 +281,7 @@ public class DateTimeTools { * @param zone 时区 * @return 带时区的地区时间 */ - public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime, ZoneId zone) { // NOSONAR + public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime, ZoneId zone) { return ZonedDateTime.of(localDateTime, zone); } diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java index 370c0eb..a1796bf 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java @@ -17,96 +17,480 @@ package xyz.zhouxy.plusone.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.DateTimeException; import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.Month; +import java.time.Year; +import java.time.YearMonth; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.Range; + +import xyz.zhouxy.plusone.commons.time.Quarter; +import xyz.zhouxy.plusone.commons.time.YearQuarter; + class DateTimeToolsTests { private static final Logger log = LoggerFactory.getLogger(DateTimeToolsTests.class); - @Test - void testToJoda() { - LocalDateTime dt = LocalDateTime.of(2008, 8, 8, 20, 18, 59, 108000000); - log.info("src: {}", dt); - org.joda.time.LocalDateTime dt2 = DateTimeTools.toJodaLocalDateTime(dt); - log.info("result: {}", dt2); - org.joda.time.format.DateTimeFormatter f = org.joda.time.format.DateTimeFormat - .forPattern("yyyy-MM-dd HH:mm:ss.SSS"); + // Java + static final LocalDateTime LOCAL_DATE_TIME = LocalDateTime.of(2024, 12, 29, 12, 58, 30, 333000000); + static final LocalDate LOCAL_DATE = LOCAL_DATE_TIME.toLocalDate(); + static final LocalTime LOCAL_TIME = LOCAL_DATE_TIME.toLocalTime(); - assertEquals("2008-08-08 20:18:59.108", f.print(dt2)); + // Java - 2024-12-29 12:58:30.333333333 SystemDefaultZone + static final ZoneId SYS_ZONE_ID = ZoneId.systemDefault(); + static final ZonedDateTime ZONED_DATE_TIME_WITH_SYS_ZONE = LOCAL_DATE_TIME.atZone(SYS_ZONE_ID); + static final Instant INSTANT_WITH_SYS_ZONE = ZONED_DATE_TIME_WITH_SYS_ZONE.toInstant(); + static final long INSTANT_MILLIS = INSTANT_WITH_SYS_ZONE.toEpochMilli(); + + static final TimeZone SYS_TIME_ZONE = TimeZone.getDefault(); + static final Date SYS_DATE = Date.from(INSTANT_WITH_SYS_ZONE); + static final Calendar SYS_CALENDAR = Calendar.getInstance(SYS_TIME_ZONE); + static { + SYS_CALENDAR.setTime(SYS_DATE); + } + + // Java - 2024-12-29 12:58:30.333333333 GMT+04:00 + static final ZoneId ZONE_ID = ZoneId.of("GMT+04:00"); + static final ZonedDateTime ZONED_DATE_TIME = LOCAL_DATE_TIME.atZone(ZONE_ID); + static final Instant INSTANT = ZONED_DATE_TIME.toInstant(); + static final long MILLIS = INSTANT.toEpochMilli(); + + static final TimeZone TIME_ZONE = TimeZone.getTimeZone(ZONE_ID); + static final Date DATE = Date.from(INSTANT); + static final Calendar CALENDAR = Calendar.getInstance(TIME_ZONE); + static { + CALENDAR.setTime(DATE); + } + + // Joda + static final org.joda.time.LocalDateTime JODA_LOCAL_DATE_TIME + = new org.joda.time.LocalDateTime(2024, 12, 29, 12, 58, 30, 333); + static final org.joda.time.LocalDate JODA_LOCAL_DATE = JODA_LOCAL_DATE_TIME.toLocalDate(); + static final org.joda.time.LocalTime JODA_LOCAL_TIME = JODA_LOCAL_DATE_TIME.toLocalTime(); + + // Joda - 2024-12-29 12:58:30.333 SystemDefaultZone + static final org.joda.time.DateTimeZone JODA_SYS_ZONE = org.joda.time.DateTimeZone.getDefault(); + static final org.joda.time.DateTime JODA_DATE_TIME_WITH_SYS_ZONE = JODA_LOCAL_DATE_TIME.toDateTime(JODA_SYS_ZONE); + static final org.joda.time.Instant JODA_INSTANT_WITH_SYS_ZONE = JODA_DATE_TIME_WITH_SYS_ZONE.toInstant(); + static final long JODA_INSTANT_MILLIS = JODA_INSTANT_WITH_SYS_ZONE.getMillis(); + + // Joda - 2024-12-29 12:58:30.333 GMT+04:00 + static final org.joda.time.DateTimeZone JODA_ZONE = org.joda.time.DateTimeZone.forID("GMT+04:00"); + static final org.joda.time.DateTime JODA_DATE_TIME = JODA_LOCAL_DATE_TIME.toDateTime(JODA_ZONE); + static final org.joda.time.Instant JODA_INSTANT = JODA_DATE_TIME.toInstant(); + static final long JODA_MILLIS = JODA_INSTANT.getMillis(); + + // ================================ + // #region - toDate + // ================================ + + @Test + void toDate_timeMillis() { + assertNotEquals(SYS_DATE, DATE); + log.info("SYS_DATE: {}, DATE: {}", SYS_DATE, DATE); + assertEquals(SYS_DATE, JODA_DATE_TIME_WITH_SYS_ZONE.toDate()); + assertEquals(SYS_DATE, DateTimeTools.toDate(INSTANT_MILLIS)); + assertEquals(DATE, JODA_DATE_TIME.toDate()); + assertEquals(DATE, DateTimeTools.toDate(MILLIS)); } @Test - void testToInstant() { - ZonedDateTime dt = ZonedDateTime.of(2008, 1, 8, 10, 23, 50, 108000000, ZoneId.systemDefault()); - Instant instant = DateTimeTools.toInstant(dt.toInstant().toEpochMilli()); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yy-M-d HH:mm:ss.SSS"); - String str = formatter.format(instant.atZone(ZoneId.systemDefault())); - log.info(str); - assertEquals("08-1-8 10:23:50.108", str); + void toDate_calendar() { + assertEquals(SYS_DATE, DateTimeTools.toDate(SYS_CALENDAR)); + assertEquals(DATE, DateTimeTools.toDate(CALENDAR)); } @Test - void testToJodaDateTime() { - ZonedDateTime dt = ZonedDateTime.of(2008, 1, 8, 10, 23, 50, 108000000, ZoneId.systemDefault()); - Instant instant = DateTimeTools.toInstant(dt.toInstant().toEpochMilli()); - - org.joda.time.format.DateTimeFormatter f = org.joda.time.format.DateTimeFormat - .forPattern("yyyy-MM-dd HH:mm:ss.SSS"); - - org.joda.time.DateTime jodaDateTime = DateTimeTools.toJodaDateTime(instant, ZoneId.of("+08:00")); - log.info("jodaDateTime: {}", jodaDateTime); - assertEquals("2008-01-08 10:23:50.108", f.print(jodaDateTime)); - - jodaDateTime = DateTimeTools.toJodaDateTime(instant, ZoneId.of("+02:00")); - log.info("jodaDateTime: {}", jodaDateTime); - assertEquals("2008-01-08 04:23:50.108", f.print(jodaDateTime)); + void toDate_instant() { + assertEquals(SYS_DATE, DateTimeTools.toDate(INSTANT_WITH_SYS_ZONE)); + assertEquals(DATE, DateTimeTools.toDate(INSTANT)); } @Test - void test() { - java.time.Instant now = java.time.Instant.now(); - org.joda.time.DateTime jodaDateTime = DateTimeTools.toJodaDateTime(now, ZoneId.of("America/New_York")); - org.joda.time.format.DateTimeFormatter formatter = org.joda.time.format.DateTimeFormat - .forPattern("yyyy-MM-dd HH:mm:ss.SSS"); - log.info(formatter.print(jodaDateTime)); - log.info(jodaDateTime.getZone().toString()); - log.info(jodaDateTime.toString()); - log.info("=========================================="); - org.joda.time.Instant instant = new org.joda.time.Instant(System.currentTimeMillis() - 500000); - log.info(instant.toString()); - log.info(DateTimeTools.toJavaInstant(instant).toString()); - log.info(DateTimeTools.toZonedDateTime(instant, org.joda.time.DateTimeZone.forID("America/New_York")) - .toString()); + void toDate_ZoneDateTime() { + assertEquals(SYS_DATE, DateTimeTools.toDate(ZONED_DATE_TIME_WITH_SYS_ZONE)); + assertEquals(DATE, DateTimeTools.toDate(ZONED_DATE_TIME)); } @Test - void testToJodaInstant() { - java.time.Instant javaInstant = java.time.Instant.now(); - log.info("javaInstant: {}", javaInstant); - - org.joda.time.Instant jodaInstant = DateTimeTools.toJodaInstant(javaInstant); - log.info("jodaInstant: {}", jodaInstant); - - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); - DateTimeFormatter formatter2 = formatter.withZone(ZoneId.systemDefault()); - log.info("{}", formatter); - log.info("{}", formatter2); + void toDate_LocalDateTimeAndZoneId() { + assertEquals(SYS_DATE, DateTimeTools.toDate(LOCAL_DATE_TIME, SYS_ZONE_ID)); + assertEquals(DATE, DateTimeTools.toDate(LOCAL_DATE_TIME, ZONE_ID)); + assertEquals(SYS_DATE, DateTimeTools.toDate(LOCAL_DATE, LOCAL_TIME, SYS_ZONE_ID)); + assertEquals(DATE, DateTimeTools.toDate(LOCAL_DATE, LOCAL_TIME, ZONE_ID)); } + // ================================ + // #endregion - toDate + // ================================ + + // ================================ + // #region - toInstant + // ================================ + + @Test + void toInstant_timeMillis() { + assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toInstant(INSTANT_MILLIS)); + assertEquals(INSTANT, DateTimeTools.toInstant(MILLIS)); + } + + @Test + void toInstant_Date() { + assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toInstant(SYS_DATE)); + assertEquals(INSTANT, DateTimeTools.toInstant(DATE)); + } + + @Test + void toInstant_Calendar() { + assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toInstant(SYS_CALENDAR)); + assertEquals(INSTANT, DateTimeTools.toInstant(CALENDAR)); + } + + @Test + void toInstant_ZonedDateTime() { + assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toInstant(ZONED_DATE_TIME_WITH_SYS_ZONE)); + assertEquals(INSTANT, DateTimeTools.toInstant(ZONED_DATE_TIME)); + } + + @Test + void toInstant_LocalDateTimeAndZone() { + assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toInstant(LOCAL_DATE_TIME, SYS_ZONE_ID)); + assertEquals(INSTANT, DateTimeTools.toInstant(LOCAL_DATE_TIME, ZONE_ID)); + } + + // ================================ + // #endregion - toInstant + // ================================ + + // ================================ + // #region - toZonedDateTime + // ================================ + + @Test + void toZonedDateTime_TimeMillisAndZone() { + assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(INSTANT_MILLIS, SYS_ZONE_ID)); + assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(MILLIS, ZONE_ID)); + } + + @Test + void toZonedDateTime_DateAndZoneId() { + assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(SYS_DATE, SYS_ZONE_ID)); + assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(DATE, ZONE_ID)); + } + + @Test + void toZonedDateTime_DateAndTimeZone() { + assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(SYS_DATE, SYS_TIME_ZONE)); + assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(DATE, TIME_ZONE)); + } + + @Test + void toZonedDateTime_Calendar() { + assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(SYS_CALENDAR)); + assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(CALENDAR)); + } + + @Test + void toZonedDateTime_CalendarAndZoneId() { + assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(SYS_CALENDAR, SYS_ZONE_ID)); + assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(CALENDAR, ZONE_ID)); + } + + @Test + void toZonedDateTime_CalendarAndTimeZone() { + assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(SYS_CALENDAR, SYS_TIME_ZONE)); + assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(CALENDAR, TIME_ZONE)); + } + + @Test + void toZonedDateTime_LocalDateTimeAndZoneId() { + assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(LOCAL_DATE_TIME, SYS_ZONE_ID)); + assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(LOCAL_DATE_TIME, ZONE_ID)); + } + + // ================================ + // #endregion - toZonedDateTime + // ================================ + + // ================================ + // #region - toLocalDateTime + // ================================ + + @Test + void toLocalDateTime_TimeMillisAndZoneId() { + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(INSTANT_MILLIS, SYS_ZONE_ID)); + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(MILLIS, ZONE_ID)); + } + + @Test + void toLocalDateTime_DateAndZoneId() { + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(SYS_DATE, SYS_ZONE_ID)); + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(DATE, ZONE_ID)); + } + + @Test + void toLocalDateTime_DateAndTimeZone() { + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(SYS_DATE, SYS_TIME_ZONE)); + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(DATE, TIME_ZONE)); + } + + @Test + void toLocalDateTime_CalendarAndZoneId() { + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(SYS_CALENDAR, SYS_ZONE_ID)); + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(CALENDAR, ZONE_ID)); + } + + @Test + void toLocalDateTime_CalendarAndTimeZone() { + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(SYS_CALENDAR, SYS_TIME_ZONE)); + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(CALENDAR, TIME_ZONE)); + } + + @Test + void toLocalDateTime_ZonedDateTimeAndZoneId() { + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(ZONED_DATE_TIME_WITH_SYS_ZONE, SYS_ZONE_ID)); + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(ZONED_DATE_TIME, ZONE_ID)); + } + + // ================================ + // #endregion - toLocalDateTime + // ================================ + + // ================================ + // #region - toJodaInstant + // ================================ + + @Test + void toJodaInstant_JavaInstant() { + assertEquals(JODA_INSTANT_WITH_SYS_ZONE, DateTimeTools.toJodaInstant(INSTANT_WITH_SYS_ZONE)); + assertEquals(JODA_INSTANT, DateTimeTools.toJodaInstant(INSTANT)); + } + + @Test + void toJodaInstant_ZonedDateTime() { + assertEquals(JODA_INSTANT_WITH_SYS_ZONE, DateTimeTools.toJodaInstant(ZONED_DATE_TIME_WITH_SYS_ZONE)); + assertEquals(JODA_INSTANT, DateTimeTools.toJodaInstant(ZONED_DATE_TIME)); + } + + @Test + void toJodaInstant_LocalDateTimeAndZoneId() { + assertEquals(JODA_INSTANT_WITH_SYS_ZONE, DateTimeTools.toJodaInstant(LOCAL_DATE_TIME, SYS_ZONE_ID)); + assertEquals(JODA_INSTANT, DateTimeTools.toJodaInstant(LOCAL_DATE_TIME, ZONE_ID)); + } + + // ================================ + // #endregion - toJodaInstant + // ================================ + + // ================================ + // #region - toJavaInstant + // ================================ + + @Test + void toJavaInstant_JodaInstant() { + assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toJavaInstant(JODA_INSTANT_WITH_SYS_ZONE)); + assertEquals(INSTANT, DateTimeTools.toJavaInstant(JODA_INSTANT)); + } + + @Test + void toJavaInstant_JodaDateTime() { + assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toJavaInstant(JODA_DATE_TIME_WITH_SYS_ZONE)); + assertEquals(INSTANT, DateTimeTools.toJavaInstant(JODA_DATE_TIME)); + } + + @Test + void toJavaInstant_JodaLocalDateTimeAndJodaDateTimeZone() { + assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toJavaInstant(JODA_LOCAL_DATE_TIME, JODA_SYS_ZONE)); + assertEquals(INSTANT, DateTimeTools.toJavaInstant(JODA_LOCAL_DATE_TIME, JODA_ZONE)); + } + + // ================================ + // #endregion - toJavaInstant + // ================================ + + // ================================ + // #region - toJodaDateTime + // ================================ + + @Test + void toJodaDateTime_ZonedDateTime() { + assertEquals(JODA_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toJodaDateTime(ZONED_DATE_TIME_WITH_SYS_ZONE)); + assertEquals(JODA_DATE_TIME, DateTimeTools.toJodaDateTime(ZONED_DATE_TIME)); + } + + @Test + void toJodaDateTime_LocalDateTimeAndZoneId() { + assertEquals(JODA_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toJodaDateTime(LOCAL_DATE_TIME, SYS_ZONE_ID)); + assertEquals(JODA_DATE_TIME, DateTimeTools.toJodaDateTime(LOCAL_DATE_TIME, ZONE_ID)); + } + + @Test + void toJodaDateTime_InstantAndZoneId() { + assertEquals(JODA_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toJodaDateTime(INSTANT_WITH_SYS_ZONE, SYS_ZONE_ID)); + assertEquals(JODA_DATE_TIME, DateTimeTools.toJodaDateTime(INSTANT, ZONE_ID)); + } + + // ================================ + // #endregion - toJodaDateTime + // ================================ + + // ================================ + // #region - toZonedDateTime + // ================================ + + @Test + void toZonedDateTime_JodaDateTime() { + assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(JODA_DATE_TIME_WITH_SYS_ZONE)); + assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(JODA_DATE_TIME)); + } + + @Test + void toZonedDateTime_JodaLocalDateTimeAndJodaDateTimeZone() { + assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(JODA_LOCAL_DATE_TIME, JODA_SYS_ZONE)); + assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(JODA_LOCAL_DATE_TIME, JODA_ZONE)); + } + + @Test + void toZonedDateTime_JodaInstantAndJodaDateTimeZone() { + assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(JODA_INSTANT_WITH_SYS_ZONE, JODA_SYS_ZONE)); + assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(JODA_INSTANT, JODA_ZONE)); + } + + // ================================ + // #endregion - toZonedDateTime + // ================================ + + // ================================ + // #region - toJodaLocalDateTime + // ================================ + + @Test + void toJodaLocalDateTime_JavaLocalDateTime() { + assertEquals(JODA_LOCAL_DATE_TIME, DateTimeTools.toJodaLocalDateTime(LOCAL_DATE_TIME)); + } + + @Test + void toJavaLocalDateTime_JodaLocalDateTime() { + assertEquals(LOCAL_DATE_TIME, DateTimeTools.toJavaLocalDateTime(JODA_LOCAL_DATE_TIME)); + } + + // ================================ + // #endregion - toJodaLocalDateTime + // ================================ + + // ================================ + // #region - ZondId <--> DateTimeZone + // ================================ + + @Test + void convertJavaZoneIdAndJodaDateTimeZone() { + assertEquals(SYS_ZONE_ID, DateTimeTools.toJavaZone(JODA_SYS_ZONE)); + assertEquals(ZONE_ID, DateTimeTools.toJavaZone(JODA_ZONE)); + assertEquals(JODA_SYS_ZONE, DateTimeTools.toJodaZone(SYS_ZONE_ID)); + assertEquals(JODA_ZONE, DateTimeTools.toJodaZone(ZONE_ID)); + } + + // ================================ + // #endregion - ZondId <--> DateTimeZone + // ================================ + + // ================================ + // #region - YearQuarter & Quarter + // ================================ + + @Test + void getQuarter() { + YearQuarter expectedYearQuarter = YearQuarter.of(2024, 4); + assertEquals(expectedYearQuarter, DateTimeTools.getQuarter(SYS_DATE)); + assertEquals(expectedYearQuarter, DateTimeTools.getQuarter(SYS_CALENDAR)); + assertEquals(Quarter.Q4, DateTimeTools.getQuarter(Month.DECEMBER)); + assertEquals(expectedYearQuarter, DateTimeTools.getQuarter(2024, Month.DECEMBER)); + assertEquals(expectedYearQuarter, DateTimeTools.getQuarter(YearMonth.of(2024, Month.DECEMBER))); + assertEquals(expectedYearQuarter, DateTimeTools.getQuarter(LOCAL_DATE)); + } + + // ================================ + // #endregion - YearQuarter & Quarter + // ================================ + + // ================================ + // #region - others + // ================================ + + @Test + void startDateOfYear() { + assertEquals(LocalDate.of(2008, 1, 1), DateTimeTools.startDateOfYear(2008)); + assertEquals(LocalDate.of(2008, 12, 31), DateTimeTools.endDateOfYear(2008)); + assertEquals(LocalDateTime.of(2024, 12, 30, 0, 0, 0), + DateTimeTools.startOfNextDate(LOCAL_DATE)); + assertEquals(LocalDateTime.of(2024, 12, 30, 0, 0, 0).atZone(SYS_ZONE_ID), + DateTimeTools.startOfNextDate(LOCAL_DATE, SYS_ZONE_ID)); + assertEquals(LocalDateTime.of(2024, 12, 30, 0, 0, 0).atZone(ZONE_ID), + DateTimeTools.startOfNextDate(LOCAL_DATE, ZONE_ID)); + + Range localDateTimeRange = DateTimeTools.toDateTimeRange(LOCAL_DATE); + assertEquals(LOCAL_DATE.atStartOfDay(), localDateTimeRange.lowerEndpoint()); + assertTrue(localDateTimeRange.contains(LOCAL_DATE.atStartOfDay())); + assertEquals(LocalDate.of(2024, 12, 30).atStartOfDay(), localDateTimeRange.upperEndpoint()); + assertEquals(LocalDate.of(2024, 12, 30).atStartOfDay(), localDateTimeRange.upperEndpoint()); + assertFalse(localDateTimeRange.contains(LocalDate.of(2024, 12, 30).atStartOfDay())); + + Range zonedDateTimeRange = DateTimeTools.toDateTimeRange(LOCAL_DATE, SYS_ZONE_ID); + assertEquals(LOCAL_DATE.atStartOfDay().atZone(SYS_ZONE_ID), zonedDateTimeRange.lowerEndpoint()); + assertTrue(zonedDateTimeRange.contains(LOCAL_DATE.atStartOfDay().atZone(SYS_ZONE_ID))); + assertEquals(ZonedDateTime.of(LocalDate.of(2024, 12, 30).atStartOfDay(), SYS_ZONE_ID), zonedDateTimeRange.upperEndpoint()); + assertEquals(ZonedDateTime.of(LocalDate.of(2024, 12, 30).atStartOfDay(), SYS_ZONE_ID), zonedDateTimeRange.upperEndpoint()); + assertFalse(zonedDateTimeRange.contains(LocalDate.of(2024, 12, 30).atStartOfDay().atZone(SYS_ZONE_ID))); + } + + // ================================ + // #endregion - others + // ================================ + + // ================================ + // #region - toString + // ================================ + @Test void testToString() { - assertEquals("04", DateTimeTools.toMonthString(Month.APRIL)); - assertEquals("04", DateTimeTools.toMonthString(Month.APRIL, true)); - assertEquals("4", DateTimeTools.toMonthString(Month.APRIL, false)); + assertEquals("2024", DateTimeTools.toYearString(2024)); + assertEquals("999999999", DateTimeTools.toYearString(Year.MAX_VALUE)); + assertEquals("-999999999", DateTimeTools.toYearString(Year.MIN_VALUE)); + assertThrows(DateTimeException.class, () -> DateTimeTools.toYearString(Year.MIN_VALUE - 1)); + assertThrows(DateTimeException.class, () -> DateTimeTools.toYearString(Year.MAX_VALUE + 1)); + + assertEquals("01", DateTimeTools.toMonthStringMM(1)); + assertEquals("02", DateTimeTools.toMonthStringMM(2)); + assertEquals("3", DateTimeTools.toMonthStringM(3)); + assertEquals("04", DateTimeTools.toMonthStringMM(Month.APRIL)); + assertEquals("05", DateTimeTools.toMonthStringMM(Month.MAY)); + assertEquals("6", DateTimeTools.toMonthStringM(Month.JUNE)); + + assertThrows(DateTimeException.class, () -> DateTimeTools.toMonthStringM(0)); + assertThrows(DateTimeException.class, () -> DateTimeTools.toMonthStringMM(0)); + assertThrows(DateTimeException.class, () -> DateTimeTools.toMonthStringM(13)); + assertThrows(DateTimeException.class, () -> DateTimeTools.toMonthStringMM(13)); } + + // ================================ + // #endregion - toString + // ================================ } diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java index 7447e48..ccc3f59 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java @@ -25,6 +25,8 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import cn.hutool.core.collection.ConcurrentHashSet; @@ -49,9 +51,12 @@ public class IdGeneratorTests { } } - @Test - void testIdWorker() { // NOSONAR - final IdWorker idWorker = new IdWorker(0L); + @ParameterizedTest + @ValueSource(longs = { 0L, 1L, 108L, 300L }) + void testIdWorker(long workerId) { // NOSONAR + // 如果使用 new IdWorker(0L) 创建,会和下面的 IdGenerator#nextSnowflakeId 使用相同 workerId 的不同 IdWorker 实例,造成 ID 重复 + final IdWorker idWorker = IdGenerator.getSnowflakeIdGenerator(workerId); + final Set ids = new ConcurrentHashSet<>(); for (int i = 0; i < 10000; i++) { executor.execute(() -> { @@ -63,7 +68,7 @@ public class IdGeneratorTests { }); executor.execute(() -> { for (int j = 0; j < 50000; j++) { - if (false == ids.add(IdGenerator.nextSnowflakeId(0))) { + if (false == ids.add(IdGenerator.nextSnowflakeId(workerId))) { throw new RuntimeException("重复ID!"); } } diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java index 3968a24..0d3fb8d 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java @@ -39,7 +39,7 @@ class RegexToolsTests { @Test void getPattern_CachePatternFalse_ReturnsNewPattern() { - String pattern = "abc"; + String pattern = "getPattern_CachePatternFalse_ReturnsNewPattern"; Pattern pattern1 = RegexTools.getPattern(pattern, false); Pattern pattern2 = RegexTools.getPattern(pattern, false); assertNotSame(pattern1, pattern2, "Pattern should not be cached"); @@ -63,7 +63,7 @@ class RegexToolsTests { @Test void getPatterns_CachePatternFalse_ReturnsNewPatterns() { - String[] patterns = {"abc", "def"}; + String[] patterns = {"getPatterns_CachePatternFalse_ReturnsNewPatterns1", "getPatterns_CachePatternFalse_ReturnsNewPatterns2"}; Pattern[] patterns1 = RegexTools.getPatterns(patterns, false); Pattern[] patterns2 = RegexTools.getPatterns(patterns, false); assertNotSame(patterns1[0], patterns2[0]); From f1412d6eead85d14ff798ff2c4b05fb9b0fbe022 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 29 Dec 2024 21:32:19 +0800 Subject: [PATCH 19/24] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=BF=9B=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index ff0f1b7..07efbfc 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,6 +1,6 @@ [ ] 未开始测试 - 0 (0.00%) -[*] 测试未完成 - 5 (8.06%) -[Y] 测试完成 - 35 (56.45%) +[*] 测试未完成 - 4 (06.45%) +[Y] 测试完成 - 36 (58.06%) [-] 无需测试 - 22 (35.48%) xyz.zhouxy.plusone.commons @@ -77,7 +77,7 @@ xyz.zhouxy.plusone.commons ArrayTools.java [*] 61 AssertTools.java [Y] BigDecimals.java [Y] - DateTimeTools.java [*] 83 + DateTimeTools.java [Y] Enumeration.java [Y] EnumTools.java [Y] IdGenerator.java [Y] From 8a60f4db66bad9b13e5be243a1a1acfe979240ca Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 29 Dec 2024 22:35:53 +0800 Subject: [PATCH 20/24] =?UTF-8?q?=E4=BF=9D=E5=AD=98=20CollectionTools#null?= =?UTF-8?q?ToEmptyXXX=20=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../collection/CollectionToolsTests.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java index bcb53a6..97fdef5 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java @@ -16,16 +16,24 @@ package xyz.zhouxy.plusone.commons.collection; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + public class CollectionToolsTests { @Test void testIsEmpty() { @@ -46,4 +54,19 @@ public class CollectionToolsTests { assertFalse(CollectionTools.isEmpty(map)); assertTrue(CollectionTools.isNotEmpty(map)); } + + @Test + void testNullToEmpty() { + List list = Lists.newArrayList("Java", "C", "C++", "C#"); + assertSame(list, CollectionTools.nullToEmptyList(list)); + assertEquals(Collections.emptyList(), CollectionTools.nullToEmptyList(null)); + + Set set = Sets.newHashSet("Java", "C", "C++", "C#"); + assertSame(set, CollectionTools.nullToEmptySet(set)); + assertEquals(Collections.emptySet(), CollectionTools.nullToEmptySet(null)); + + Map map = ImmutableMap.of("K1", 1, "K2", 2, "K3", 3); + assertSame(map, CollectionTools.nullToEmptyMap(map)); + assertEquals(Collections.emptyMap(), CollectionTools.nullToEmptyMap(null)); + } } From 8f588c25b5b8db8ad6abff9f0fc1292018867284 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sun, 29 Dec 2024 22:40:35 +0800 Subject: [PATCH 21/24] =?UTF-8?q?=E6=9E=84=E9=80=A0=20PageResult=20?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E5=A6=82=E6=9E=9C=20list=20=E6=98=AF=20null?= =?UTF-8?q?=EF=BC=8C=E5=88=99=E8=87=AA=E5=8A=A8=E8=BD=AC=E4=B8=BA=E7=A9=BA?= =?UTF-8?q?=E5=88=97=E8=A1=A8=EF=BC=8C=E4=B8=8D=E6=8A=9B=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xyz/zhouxy/plusone/commons/model/dto/PageResult.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java b/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java index b73d0a0..353d86f 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod; -import xyz.zhouxy.plusone.commons.util.AssertTools; +import xyz.zhouxy.plusone.commons.collection.CollectionTools; /** * 返回分页查询的结果 @@ -37,8 +37,7 @@ public class PageResult { private final List content; private PageResult(List content, long total) { - AssertTools.checkNotNull(content, "Content must not be null."); - this.content = content; + this.content = CollectionTools.nullToEmptyList(content); this.total = total; } From 9f96db1a0bcaa667ee0aaacd07654223ba28d52f Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Wed, 1 Jan 2025 17:17:50 +0800 Subject: [PATCH 22/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=20ArrayTools=20?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plusone/commons/util/ArrayTools.java | 60 +- .../plusone/commons/util/ArrayToolsTests.java | 1102 +++++++++++++---- 2 files changed, 915 insertions(+), 247 deletions(-) diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/ArrayTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/ArrayTools.java index d547ade..8ad67ee 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/ArrayTools.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/ArrayTools.java @@ -18,6 +18,7 @@ package xyz.zhouxy.plusone.commons.util; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -260,12 +261,7 @@ public class ArrayTools { */ public static boolean isAllElementsNotNull(@Nonnull final T[] arr) { AssertTools.checkArgument(arr != null, "The array cannot be null."); - for (T element : arr) { - if (element == null) { - return false; - } - } - return true; + return Arrays.stream(arr).allMatch(Objects::nonNull); } // #endregion @@ -626,7 +622,7 @@ public class ArrayTools { // fill - char - public static void fill(char[] a, char... values) { + public static void fill(char[] a, char[] values) { fill(a, 0, a.length, values); } @@ -634,9 +630,9 @@ public class ArrayTools { fill(a, 0, a.length, values != null ? values.toCharArray() : EMPTY_CHAR_ARRAY); } - public static void fill(char[] a, int fromIndex, int toIndex, char... values) { + public static void fill(char[] a, int fromIndex, int toIndex, char[] values) { AssertTools.checkArgument(Objects.nonNull(a)); - if (values.length == 0) { + if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); @@ -659,13 +655,13 @@ public class ArrayTools { // fill - byte - public static void fill(byte[] a, byte... values) { + public static void fill(byte[] a, byte[] values) { fill(a, 0, a.length, values); } - public static void fill(byte[] a, int fromIndex, int toIndex, byte... values) { + public static void fill(byte[] a, int fromIndex, int toIndex, byte[] values) { AssertTools.checkArgument(Objects.nonNull(a)); - if (values.length == 0) { + if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); @@ -688,13 +684,13 @@ public class ArrayTools { // fill - short - public static void fill(short[] a, short... values) { + public static void fill(short[] a, short[] values) { fill(a, 0, a.length, values); } - public static void fill(short[] a, int fromIndex, int toIndex, short... values) { + public static void fill(short[] a, int fromIndex, int toIndex, short[] values) { AssertTools.checkArgument(Objects.nonNull(a)); - if (values.length == 0) { + if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); @@ -717,13 +713,13 @@ public class ArrayTools { // fill - int - public static void fill(int[] a, int... values) { + public static void fill(int[] a, int[] values) { fill(a, 0, a.length, values); } - public static void fill(int[] a, int fromIndex, int toIndex, int... values) { + public static void fill(int[] a, int fromIndex, int toIndex, int[] values) { AssertTools.checkArgument(Objects.nonNull(a)); - if (values.length == 0) { + if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); @@ -746,13 +742,13 @@ public class ArrayTools { // fill - long - public static void fill(long[] a, long... values) { + public static void fill(long[] a, long[] values) { fill(a, 0, a.length, values); } - public static void fill(long[] a, int fromIndex, int toIndex, long... values) { + public static void fill(long[] a, int fromIndex, int toIndex, long[] values) { AssertTools.checkArgument(Objects.nonNull(a)); - if (values.length == 0) { + if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); @@ -775,13 +771,13 @@ public class ArrayTools { // fill - float - public static void fill(float[] a, float... values) { + public static void fill(float[] a, float[] values) { fill(a, 0, a.length, values); } - public static void fill(float[] a, int fromIndex, int toIndex, float... values) { + public static void fill(float[] a, int fromIndex, int toIndex, float[] values) { AssertTools.checkArgument(Objects.nonNull(a)); - if (values.length == 0) { + if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); @@ -804,13 +800,13 @@ public class ArrayTools { // fill - double - public static void fill(double[] a, double... values) { + public static void fill(double[] a, double[] values) { fill(a, 0, a.length, values); } - public static void fill(double[] a, int fromIndex, int toIndex, double... values) { + public static void fill(double[] a, int fromIndex, int toIndex, double[] values) { AssertTools.checkArgument(Objects.nonNull(a)); - if (values.length == 0) { + if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); @@ -868,7 +864,7 @@ public class ArrayTools { // #region - indexOf - public static int indexOf(T[] arr, Predicate predicate) { + public static int indexOfWithPredicate(T[] arr, Predicate predicate) { AssertTools.checkNotNull(predicate); if (isNullOrEmpty(arr)) { return NOT_FOUND_INDEX; @@ -882,7 +878,7 @@ public class ArrayTools { } public static int indexOf(T[] arr, T obj) { - return indexOf(arr, item -> Objects.equals(item, obj)); + return indexOfWithPredicate(arr, item -> Objects.equals(item, obj)); } public static int indexOf(char[] arr, char value) { @@ -973,7 +969,7 @@ public class ArrayTools { // #region - lastIndexOf - public static int lastIndexOf(T[] arr, @Nonnull Predicate predicate) { + public static int lastIndexOfWithPredicate(T[] arr, @Nonnull Predicate predicate) { AssertTools.checkNotNull(predicate); if (isNullOrEmpty(arr)) { return NOT_FOUND_INDEX; @@ -987,7 +983,7 @@ public class ArrayTools { } public static int lastIndexOf(T[] arr, T obj) { - return lastIndexOf(arr, item -> Objects.equals(item, obj)); + return lastIndexOfWithPredicate(arr, item -> Objects.equals(item, obj)); } public static int lastIndexOf(char[] arr, char value) { @@ -1111,7 +1107,7 @@ public class ArrayTools { } public static boolean containsValue(BigDecimal[] arr, BigDecimal obj) { - return indexOf(arr, item -> BigDecimals.equalsValue(item, obj)) > NOT_FOUND_INDEX; + return indexOfWithPredicate(arr, item -> BigDecimals.equalsValue(item, obj)) > NOT_FOUND_INDEX; } // #endregion diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/ArrayToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/ArrayToolsTests.java index ceafaf1..32423a1 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/ArrayToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/ArrayToolsTests.java @@ -27,44 +27,63 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.function.Predicate; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; @SuppressWarnings("null") public class ArrayToolsTests { - // TODO 【优化】 检查、完善测试用例 + static final String[] NULL_STRING_ARRAY = null; + static final Integer[] NULL_INTEGER_ARRAY = null; + static final char[] NULL_CHAR_ARRAY = null; + static final byte[] NULL_BYTE_ARRAY = null; + static final short[] NULL_SHORT_ARRAY = null; + static final int[] NULL_INT_ARRAY = null; + static final long[] NULL_LONG_ARRAY = null; + static final float[] NULL_FLOAT_ARRAY = null; + static final double[] NULL_DOUBLE_ARRAY = null; - //#region null or empty + static final String[] EMPTY_STRING_ARRAY = {}; + static final Integer[] EMPTY_INTEGER_ARRAY = {}; + static final char[] EMPTY_CHAR_ARRAY = {}; + static final byte[] EMPTY_BYTE_ARRAY = {}; + static final short[] EMPTY_SHORT_ARRAY = {}; + static final int[] EMPTY_INT_ARRAY = {}; + static final long[] EMPTY_LONG_ARRAY = {}; + static final float[] EMPTY_FLOAT_ARRAY = {}; + static final double[] EMPTY_DOUBLE_ARRAY = {}; + + // ================================ + // #region - isNullOrEmpty + // ================================ @Test void isNullOrEmpty_NullArray_ReturnsTrue() { assertAll( - () -> assertTrue(ArrayTools.isNullOrEmpty((String[]) null)), - () -> assertTrue(ArrayTools.isNullOrEmpty((Integer[]) null)), - () -> assertTrue(ArrayTools.isNullOrEmpty((char[]) null)), - () -> assertTrue(ArrayTools.isNullOrEmpty((byte[]) null)), - () -> assertTrue(ArrayTools.isNullOrEmpty((short[]) null)), - () -> assertTrue(ArrayTools.isNullOrEmpty((int[]) null)), - () -> assertTrue(ArrayTools.isNullOrEmpty((long[]) null)), - () -> assertTrue(ArrayTools.isNullOrEmpty((float[]) null)), - () -> assertTrue(ArrayTools.isNullOrEmpty((double[]) null))); + () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_STRING_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_INTEGER_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_CHAR_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_BYTE_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_SHORT_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_INT_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_LONG_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_FLOAT_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_DOUBLE_ARRAY))); } @Test void isNullOrEmpty_EmptyArray_ReturnsTrue() { assertAll( - () -> assertTrue(ArrayTools.isNullOrEmpty(new String[] {})), - () -> assertTrue(ArrayTools.isNullOrEmpty(new Integer[] {})), - () -> assertTrue(ArrayTools.isNullOrEmpty(new char[] {})), - () -> assertTrue(ArrayTools.isNullOrEmpty(new byte[] {})), - () -> assertTrue(ArrayTools.isNullOrEmpty(new short[] {})), - () -> assertTrue(ArrayTools.isNullOrEmpty(new int[] {})), - () -> assertTrue(ArrayTools.isNullOrEmpty(new long[] {})), - () -> assertTrue(ArrayTools.isNullOrEmpty(new float[] {})), - () -> assertTrue(ArrayTools.isNullOrEmpty(new double[] {}))); + () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_STRING_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_INTEGER_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_CHAR_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_BYTE_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_SHORT_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_INT_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_LONG_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_FLOAT_ARRAY)), + () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_DOUBLE_ARRAY))); } @Test @@ -81,32 +100,40 @@ public class ArrayToolsTests { () -> assertFalse(ArrayTools.isNullOrEmpty(new double[] { 1 }))); } + // ================================ + // #endregion - isNullOrEmpty + // ================================ + + // ================================ + // #region - isNotEmpty + // ================================ + @Test void isNotEmpty_NullArray_ReturnsFalse() { assertAll( - () -> assertFalse(ArrayTools.isNotEmpty((String[]) null)), - () -> assertFalse(ArrayTools.isNotEmpty((Integer[]) null)), - () -> assertFalse(ArrayTools.isNotEmpty((char[]) null)), - () -> assertFalse(ArrayTools.isNotEmpty((byte[]) null)), - () -> assertFalse(ArrayTools.isNotEmpty((short[]) null)), - () -> assertFalse(ArrayTools.isNotEmpty((int[]) null)), - () -> assertFalse(ArrayTools.isNotEmpty((long[]) null)), - () -> assertFalse(ArrayTools.isNotEmpty((float[]) null)), - () -> assertFalse(ArrayTools.isNotEmpty((double[]) null))); + () -> assertFalse(ArrayTools.isNotEmpty(NULL_STRING_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(NULL_INTEGER_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(NULL_CHAR_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(NULL_BYTE_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(NULL_SHORT_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(NULL_INT_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(NULL_LONG_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(NULL_FLOAT_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(NULL_DOUBLE_ARRAY))); } @Test void isNotEmpty_EmptyArray_ReturnsFalse() { assertAll( - () -> assertFalse(ArrayTools.isNotEmpty(new String[] {})), - () -> assertFalse(ArrayTools.isNotEmpty(new Integer[] {})), - () -> assertFalse(ArrayTools.isNotEmpty(new char[] {})), - () -> assertFalse(ArrayTools.isNotEmpty(new byte[] {})), - () -> assertFalse(ArrayTools.isNotEmpty(new short[] {})), - () -> assertFalse(ArrayTools.isNotEmpty(new int[] {})), - () -> assertFalse(ArrayTools.isNotEmpty(new long[] {})), - () -> assertFalse(ArrayTools.isNotEmpty(new float[] {})), - () -> assertFalse(ArrayTools.isNotEmpty(new double[] {}))); + () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_STRING_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_INTEGER_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_CHAR_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_BYTE_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_SHORT_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_INT_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_LONG_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_FLOAT_ARRAY)), + () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_DOUBLE_ARRAY))); } @Test @@ -123,16 +150,24 @@ public class ArrayToolsTests { () -> assertTrue(ArrayTools.isNotEmpty(new double[] { 1 }))); } + // ================================ + // #endregion - isNotEmpty + // ================================ + + // ================================ + // #region - isAllElementsNotNull + // ================================ + @Test void isAllElementsNotNull_NullArray_ThrowsException() { - assertThrows(IllegalArgumentException.class, () -> ArrayTools.isAllElementsNotNull((String[]) null)); - assertThrows(IllegalArgumentException.class, () -> ArrayTools.isAllElementsNotNull((Integer[]) null)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.isAllElementsNotNull(NULL_STRING_ARRAY)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.isAllElementsNotNull(NULL_INTEGER_ARRAY)); } @Test void isAllElementsNotNull_EmptyArray_ReturnsTrue() { - assertTrue(ArrayTools.isAllElementsNotNull(new String[] {})); - assertTrue(ArrayTools.isAllElementsNotNull(new Integer[] {})); + assertTrue(ArrayTools.isAllElementsNotNull(EMPTY_STRING_ARRAY)); + assertTrue(ArrayTools.isAllElementsNotNull(EMPTY_INTEGER_ARRAY)); } @Test @@ -147,155 +182,122 @@ public class ArrayToolsTests { assertTrue(ArrayTools.isAllElementsNotNull(new Integer[] { 1, 2 })); } - // #endregion + // ================================ + // #endregion - isAllElementsNotNull + // ================================ + // ================================ // #region - concat + // ================================ - private static List charArrays; - private static List byteArrays; - private static List shortArrays; - private static List intArrays; - private static List longArrays; - private static List floatArrays; - private static List doubleArrays; + static final List charArrays; + static final List byteArrays; + static final List shortArrays; + static final List intArrays; + static final List longArrays; + static final List floatArrays; + static final List doubleArrays; - @BeforeAll - public static void setUp() { + static { charArrays = new ArrayList<>(); + charArrays.add(null); + charArrays.add(new char[0]); charArrays.add(new char[] { 'a', 'b' }); charArrays.add(new char[] { 'c', 'd', 'e' }); - charArrays.add(null); byteArrays = new ArrayList<>(); + byteArrays.add(null); + byteArrays.add(new byte[0]); byteArrays.add(new byte[] { 1, 2 }); byteArrays.add(new byte[] { 3, 4, 5 }); - byteArrays.add(null); shortArrays = new ArrayList<>(); + shortArrays.add(null); + shortArrays.add(new short[0]); shortArrays.add(new short[] { 10, 20 }); shortArrays.add(new short[] { 30, 40, 50 }); - shortArrays.add(null); intArrays = new ArrayList<>(); + intArrays.add(null); + intArrays.add(new int[0]); intArrays.add(new int[] { 100, 200 }); intArrays.add(new int[] { 300, 400, 500 }); - intArrays.add(null); longArrays = new ArrayList<>(); + longArrays.add(null); + longArrays.add(new long[0]); longArrays.add(new long[] { 1000L, 2000L }); longArrays.add(new long[] { 3000L, 4000L, 5000L }); - longArrays.add(null); floatArrays = new ArrayList<>(); + floatArrays.add(null); + floatArrays.add(new float[0]); floatArrays.add(new float[] { 1000.1f, 2000.2f }); floatArrays.add(new float[] { 3000.3f, 4000.4f, 5000.5f }); - floatArrays.add(null); doubleArrays = new ArrayList<>(); + doubleArrays.add(null); + doubleArrays.add(new double[0]); doubleArrays.add(new double[] { 1000.1d, 2000.2d }); doubleArrays.add(new double[] { 3000.3d, 4000.4d, 5000.5d }); - doubleArrays.add(null); } @Test - public void testConcatCharArray_NullOrEmptyCollection_ReturnsEmptyArray() { - assertArrayEquals(new char[] {}, ArrayTools.concatCharArray(null)); + void concat_NullOrEmptyCollection_ReturnsEmptyArray() { + assertEquals(0, ArrayTools.concatCharArray(null).length); assertEquals(0, ArrayTools.concatCharArray(Collections.emptyList()).length); - } - @Test - public void testConcatCharArray_ValidCollection_ReturnsConcatenatedArray() { - char[] expected = { 'a', 'b', 'c', 'd', 'e' }; - char[] result = ArrayTools.concatCharArray(charArrays); - assertArrayEquals(expected, result); - } - - @Test - public void testConcatByteArray_NullOrEmptyCollection_ReturnsEmptyArray() { - assertArrayEquals(new byte[0], ArrayTools.concatByteArray(null)); + assertEquals(0, ArrayTools.concatByteArray(null).length); assertEquals(0, ArrayTools.concatByteArray(Collections.emptyList()).length); - } - @Test - public void testConcatByteArray_ValidCollection_ReturnsConcatenatedArray() { - byte[] expected = { 1, 2, 3, 4, 5 }; - byte[] result = ArrayTools.concatByteArray(byteArrays); - assertArrayEquals(expected, result); - } - - @Test - public void testConcatShortArray_NullOrEmptyCollection_ReturnsEmptyArray() { - assertArrayEquals(new short[0], ArrayTools.concatShortArray(null)); + assertEquals(0, ArrayTools.concatShortArray(null).length); assertEquals(0, ArrayTools.concatShortArray(Collections.emptyList()).length); - } - @Test - public void testConcatShortArray_ValidCollection_ReturnsConcatenatedArray() { - short[] expected = { 10, 20, 30, 40, 50 }; - short[] result = ArrayTools.concatShortArray(shortArrays); - assertArrayEquals(expected, result); - } - - @Test - public void testConcatIntArray_NullOrEmptyCollection_ReturnsEmptyArray() { - assertArrayEquals(new int[0], ArrayTools.concatIntArray(null)); + assertEquals(0, ArrayTools.concatIntArray(null).length); assertEquals(0, ArrayTools.concatIntArray(Collections.emptyList()).length); - } - @Test - public void testConcatIntArray_ValidCollection_ReturnsConcatenatedArray() { - int[] expected = { 100, 200, 300, 400, 500 }; - int[] result = ArrayTools.concatIntArray(intArrays); - assertArrayEquals(expected, result); - } - - @Test - public void testConcatLongArray_NullOrEmptyCollection_ReturnsEmptyArray() { - assertArrayEquals(new long[0], ArrayTools.concatLongArray(null)); + assertEquals(0, ArrayTools.concatLongArray(null).length); assertEquals(0, ArrayTools.concatLongArray(Collections.emptyList()).length); - } - @Test - public void testConcatLongArray_ValidCollection_ReturnsConcatenatedArray() { - long[] expected = { 1000L, 2000L, 3000L, 4000L, 5000L }; - long[] result = ArrayTools.concatLongArray(longArrays); - assertArrayEquals(expected, result); - } - - @Test - public void testConcatFloatArray_NullOrEmptyCollection_ReturnsEmptyArray() { - assertArrayEquals(new float[0], ArrayTools.concatFloatArray(null)); + assertEquals(0, ArrayTools.concatFloatArray(null).length); assertEquals(0, ArrayTools.concatFloatArray(Collections.emptyList()).length); - } - @Test - public void testConcatFloatArray_ValidCollection_ReturnsConcatenatedArray() { - float[] expected = { 1000.1f, 2000.2f, 3000.3f, 4000.4f, 5000.5f }; - float[] result = ArrayTools.concatFloatArray(floatArrays); - assertArrayEquals(expected, result); - } - - @Test - public void testConcatDoubleArray_NullOrEmptyCollection_ReturnsEmptyArray() { - assertArrayEquals(new double[] {}, ArrayTools.concatDoubleArray(null)); + assertEquals(0, ArrayTools.concatDoubleArray(null).length); assertEquals(0, ArrayTools.concatDoubleArray(Collections.emptyList()).length); } @Test - public void testConcatDoubleArray_ValidCollection_ReturnsConcatenatedArray() { - double[] expected = { 1000.1d, 2000.2d, 3000.3d, 4000.4d, 5000.5d }; - double[] result = ArrayTools.concatDoubleArray(doubleArrays); - assertArrayEquals(expected, result); + void concat_ValidCollection_ReturnsConcatenatedArray() { + assertArrayEquals(new char[] { 'a', 'b', 'c', 'd', 'e' }, + ArrayTools.concatCharArray(charArrays)); + + assertArrayEquals(new byte[] { 1, 2, 3, 4, 5 }, + ArrayTools.concatByteArray(byteArrays)); + + assertArrayEquals(new short[] { 10, 20, 30, 40, 50 }, + ArrayTools.concatShortArray(shortArrays)); + + assertArrayEquals(new int[] { 100, 200, 300, 400, 500 }, + ArrayTools.concatIntArray(intArrays)); + + assertArrayEquals(new long[] { 1000L, 2000L, 3000L, 4000L, 5000L }, + ArrayTools.concatLongArray(longArrays)); + + assertArrayEquals(new float[] { 1000.1f, 2000.2f, 3000.3f, 4000.4f, 5000.5f }, + ArrayTools.concatFloatArray(floatArrays)); + + assertArrayEquals(new double[] { 1000.1d, 2000.2d, 3000.3d, 4000.4d, 5000.5d }, + ArrayTools.concatDoubleArray(doubleArrays)); } @Test - public void testConcatToList_NullOrEmptyCollection_ReturnsEmptyList() { + void concatToList_NullOrEmptyCollection_ReturnsEmptyList() { assertTrue(ArrayTools.concatToList(null).isEmpty()); assertTrue(ArrayTools.concatToList(Collections.emptyList()).isEmpty()); } @Test - public void testConcatToList_ValidCollection_ReturnsConcatenatedList() { + void concatToList_ValidCollection_ReturnsConcatenatedList() { Character[] charArray1 = { 'a', 'b' }; Character[] charArray2 = { 'c', 'd', 'e' }; List expected = Arrays.asList('a', 'b', 'c', 'd', 'e'); @@ -303,116 +305,786 @@ public class ArrayToolsTests { assertEquals(expected, result); } + // ================================ // #endregion + // ================================ + // ================================ // #region - repeat + // ================================ @Test - void repeat_CharArray_TimesZero_ReturnsEmptyArray() { - char[] input = { 'a', 'b', 'c' }; - char[] result = ArrayTools.repeat(input, 0); - assertArrayEquals(ArrayTools.EMPTY_CHAR_ARRAY, result); + void repeat_NullArray_ThrowsException() { + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_CHAR_ARRAY, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_BYTE_ARRAY, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_SHORT_ARRAY, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_INT_ARRAY, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_LONG_ARRAY, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_FLOAT_ARRAY, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_DOUBLE_ARRAY, 2)); + + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_CHAR_ARRAY, 2, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_BYTE_ARRAY, 2, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_SHORT_ARRAY, 2, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_INT_ARRAY, 2, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_LONG_ARRAY, 2, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_FLOAT_ARRAY, 2, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_DOUBLE_ARRAY, 2, 2)); } @Test - void repeat_CharArray_TimesOne_ReturnsSameArray() { - char[] input = { 'a', 'b', 'c' }; - char[] result = ArrayTools.repeat(input, 1); - assertArrayEquals(input, result); + void repeat_NegativeTimes_ThrowsException() { + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new char[]{ 'a' }, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new byte[]{ 1 }, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new short[]{ 1 }, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new int[]{ 1 }, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new long[]{ 1 }, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new float[]{ 1 }, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new double[]{ 1 }, -1)); + + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new char[]{ 'a' }, -1, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new byte[]{ 1 }, -1, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new short[]{ 1 }, -1, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new int[]{ 1 }, -1, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new long[]{ 1 }, -1, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new float[]{ 1 }, -1, 2)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new double[]{ 1 }, -1, 2)); } @Test - void repeat_CharArray_TimesTwo_ReturnsRepeatedArray() { - char[] input = { 'a', 'b', 'c' }; - char[] expected = { 'a', 'b', 'c', 'a', 'b', 'c' }; - char[] result = ArrayTools.repeat(input, 2); - assertArrayEquals(expected, result); + void repeat_NegativeMaxLength_ThrowsException() { + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new char[]{ 'a' }, 2, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new byte[]{ 1 }, 2, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new short[]{ 1 }, 2, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new int[]{ 1 }, 2, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new long[]{ 1 }, 2, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new float[]{ 1 }, 2, -1)); + assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new double[]{ 1 }, 2, -1)); } @Test - void repeat_CharArray_WithMaxLength_ReturnsTruncatedArray() { - char[] input = { 'a', 'b', 'c' }; - char[] expected = { 'a', 'b', 'c', 'a', 'b' }; - char[] result = ArrayTools.repeat(input, 2, 5); - assertArrayEquals(expected, result); + void repeat_ZeroTimes_ReturnsEmptyArray() { + assertEquals(0, ArrayTools.repeat(new char[]{ 'a' }, 0).length); + assertEquals(0, ArrayTools.repeat(new byte[]{ 1 }, 0).length); + assertEquals(0, ArrayTools.repeat(new short[]{ 1 }, 0).length); + assertEquals(0, ArrayTools.repeat(new int[]{ 1 }, 0).length); + assertEquals(0, ArrayTools.repeat(new long[]{ 1 }, 0).length); + assertEquals(0, ArrayTools.repeat(new float[]{ 1 }, 0).length); + assertEquals(0, ArrayTools.repeat(new double[]{ 1 }, 0).length); } @Test - void repeat_ByteArray_TimesZero_ReturnsEmptyArray() { - byte[] input = { 1, 2, 3 }; - byte[] result = ArrayTools.repeat(input, 0); - assertArrayEquals(ArrayTools.EMPTY_BYTE_ARRAY, result); + void repeat_NormalCase_RepeatsArray() { + assertArrayEquals(new char[] { 'a', 'b', 'a', 'b', 'a', 'b' }, ArrayTools.repeat(new char[] { 'a', 'b' }, 3)); + assertArrayEquals(new byte[] { 1, 2, 1, 2, 1, 2 }, ArrayTools.repeat(new byte[] { 1, 2 }, 3)); + assertArrayEquals(new short[] { 1, 2, 1, 2, 1, 2 }, ArrayTools.repeat(new short[] { 1, 2 }, 3)); + assertArrayEquals(new int[] { 1, 2, 1, 2, 1, 2 }, ArrayTools.repeat(new int[] { 1, 2 }, 3)); + assertArrayEquals(new long[] { 1L, 2L, 1L, 2L, 1L, 2L }, ArrayTools.repeat(new long[] { 1L, 2L }, 3)); + assertArrayEquals(new float[] { 1.1F, 2.2F, 1.1F, 2.2F, 1.1F, 2.2F }, ArrayTools.repeat(new float[] { 1.1F, 2.2F }, 3)); + assertArrayEquals(new double[] { 1.12, 2.23, 1.12, 2.23, 1.12, 2.23 }, ArrayTools.repeat(new double[] { 1.12, 2.23 }, 3)); } @Test - void repeat_ShortArray_TimesZero_ReturnsEmptyArray() { - short[] input = { 1, 2, 3 }; - short[] result = ArrayTools.repeat(input, 0); - assertArrayEquals(ArrayTools.EMPTY_SHORT_ARRAY, result); - } - - @Test - void repeat_IntArray_TimesZero_ReturnsEmptyArray() { - int[] input = { 1, 2, 3 }; - int[] result = ArrayTools.repeat(input, 0); - assertArrayEquals(ArrayTools.EMPTY_INT_ARRAY, result); - } - - @Test - void repeat_LongArray_TimesZero_ReturnsEmptyArray() { - long[] input = { 1, 2, 3 }; - long[] result = ArrayTools.repeat(input, 0); - assertArrayEquals(ArrayTools.EMPTY_LONG_ARRAY, result); - } - - @Test - void repeat_FloatArray_TimesZero_ReturnsEmptyArray() { - float[] input = { 1, 2, 3 }; - float[] result = ArrayTools.repeat(input, 0); - assertArrayEquals(ArrayTools.EMPTY_FLOAT_ARRAY, result); - } - - @Test - void repeat_DoubleArray_TimesZero_ReturnsEmptyArray() { - double[] input = { 1, 2, 3 }; - double[] result = ArrayTools.repeat(input, 0); - assertArrayEquals(ArrayTools.EMPTY_DOUBLE_ARRAY, result); - } - - @Test - void repeat_CharArray_ThrowsExceptionForNullArray() { - Executable executable = () -> ArrayTools.repeat((char[]) null, 2); - assertThrows(IllegalArgumentException.class, executable); - } - - @Test - void repeat_CharArray_ThrowsExceptionForNegativeTimes() { - char[] input = { 'a', 'b', 'c' }; - Executable executable = () -> ArrayTools.repeat(input, -1); - assertThrows(IllegalArgumentException.class, executable); - } - - @Test - void repeat_CharArray_ThrowsExceptionForNegativeMaxLength() { - char[] input = { 'a', 'b', 'c' }; - Executable executable = () -> ArrayTools.repeat(input, 2, -1); - assertThrows(IllegalArgumentException.class, executable); + void repeat_WithMaxLength_TruncatesResult() { + assertArrayEquals(new char[] { 'a', 'b', 'a', 'b', 'a' }, ArrayTools.repeat(new char[] { 'a', 'b' }, 3, 5)); + assertArrayEquals(new byte[] { 1, 2, 1, 2, 1 }, ArrayTools.repeat(new byte[] { 1, 2 }, 3, 5)); + assertArrayEquals(new short[] { 1, 2, 1, 2, 1 }, ArrayTools.repeat(new short[] { 1, 2 }, 3, 5)); + assertArrayEquals(new int[] { 1, 2, 1, 2, 1 }, ArrayTools.repeat(new int[] { 1, 2 }, 3, 5)); + assertArrayEquals(new long[] { 1L, 2L, 1L, 2L, 1L }, ArrayTools.repeat(new long[] { 1L, 2L }, 3, 5)); + assertArrayEquals(new float[] { 1.1F, 2.2F, 1.1F, 2.2F, 1.1F }, ArrayTools.repeat(new float[] { 1.1F, 2.2F }, 3, 5)); + assertArrayEquals(new double[] { 1.12, 2.23, 1.12, 2.23, 1.12 }, ArrayTools.repeat(new double[] { 1.12, 2.23 }, 3, 5)); } + // ================================ // #endregion + // ================================ - // TODO 【添加】 补充测试用例 - + // ================================ // #region - fill - // #endregion + // ================================ + @Test + void fill_WithValues() { + // fill - char + char[] charArray = new char[10]; + ArrayTools.fill(charArray, new char[] {'a', 'b', 'c'}); + assertArrayEquals(new char[] { 'a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c', 'a' }, charArray); + ArrayTools.fill(charArray, "abcd"); + assertArrayEquals(new char[] { 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'a', 'b' }, charArray); + + // fill - byte + byte[] byteArray = new byte[10]; + ArrayTools.fill(byteArray, new byte[] { 1, 2, 3 }); + assertArrayEquals(new byte[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }, byteArray); + + // fill - short + short[] shortArray = new short[10]; + ArrayTools.fill(shortArray, new short[] { 1, 2, 3 }); + assertArrayEquals(new short[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }, shortArray); + + // fill - int + int[] intArray = new int[10]; + ArrayTools.fill(intArray, new int[] { 1, 2, 3 }); + assertArrayEquals(new int[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }, intArray); + + // fill - long + long[] longArray = new long[10]; + ArrayTools.fill(longArray, new long[] { 1, 2, 3 }); + assertArrayEquals(new long[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }, longArray); + + // fill - float + float[] floatArray = new float[10]; + ArrayTools.fill(floatArray, new float[] { 1.1F, 2.2F, 3.3F }); + assertArrayEquals(new float[] { 1.1F, 2.2F, 3.3F, 1.1F, 2.2F, 3.3F, 1.1F, 2.2F, 3.3F, 1.1F }, floatArray); + + // fill - double + double[] doubleArray = new double[10]; + ArrayTools.fill(doubleArray, new double[] { 1.1, 2.2, 3.3 }); + assertArrayEquals(new double[] { 1.1, 2.2, 3.3, 1.1, 2.2, 3.3, 1.1, 2.2, 3.3, 1.1 }, doubleArray); + + // fill - T + String[] stringArray = new String[10]; + ArrayTools.fill(stringArray, new String[] { "aa", "bb", "cc" }); + assertArrayEquals(new String[] { "aa", "bb", "cc", "aa", "bb", "cc", "aa", "bb", "cc", "aa" }, stringArray); + } + + @Test + void fill_WithEmptyValues() { + // fill - char + char[] charArray = new char[10]; + ArrayTools.fill(charArray, EMPTY_CHAR_ARRAY); + assertArrayEquals(new char[10], charArray); + ArrayTools.fill(charArray, ""); + assertArrayEquals(new char[10], charArray); + + // fill - byte + byte[] byteArray = new byte[10]; + ArrayTools.fill(byteArray, EMPTY_BYTE_ARRAY); + assertArrayEquals(new byte[10], byteArray); + + // fill - short + short[] shortArray = new short[10]; + ArrayTools.fill(shortArray, EMPTY_SHORT_ARRAY); + assertArrayEquals(new short[10], shortArray); + + // fill - int + int[] intArray = new int[10]; + ArrayTools.fill(intArray, EMPTY_INT_ARRAY); + assertArrayEquals(new int[10], intArray); + + // fill - long + long[] longArray = new long[10]; + ArrayTools.fill(longArray, EMPTY_LONG_ARRAY); + assertArrayEquals(new long[10], longArray); + + // fill - float + float[] floatArray = new float[10]; + ArrayTools.fill(floatArray, EMPTY_FLOAT_ARRAY); + assertArrayEquals(new float[10], floatArray); + + // fill - double + double[] doubleArray = new double[10]; + ArrayTools.fill(doubleArray, EMPTY_DOUBLE_ARRAY); + assertArrayEquals(new double[10], doubleArray); + + // fill - T + String[] stringArray = new String[10]; + ArrayTools.fill(stringArray, EMPTY_STRING_ARRAY); + assertArrayEquals(new String[10], stringArray); + } + + @Test + void fill_WithNullValues() { + // fill - char + char[] charArray = new char[10]; + ArrayTools.fill(charArray, NULL_CHAR_ARRAY); + assertArrayEquals(new char[10], charArray); + ArrayTools.fill(charArray, (String) null); + assertArrayEquals(new char[10], charArray); + + // fill - byte + byte[] byteArray = new byte[10]; + ArrayTools.fill(byteArray, NULL_BYTE_ARRAY); + assertArrayEquals(new byte[10], byteArray); + + // fill - short + short[] shortArray = new short[10]; + ArrayTools.fill(shortArray, NULL_SHORT_ARRAY); + assertArrayEquals(new short[10], shortArray); + + // fill - int + int[] intArray = new int[10]; + ArrayTools.fill(intArray, NULL_INT_ARRAY); + assertArrayEquals(new int[10], intArray); + + // fill - long + long[] longArray = new long[10]; + ArrayTools.fill(longArray, NULL_LONG_ARRAY); + assertArrayEquals(new long[10], longArray); + + // fill - float + float[] floatArray = new float[10]; + ArrayTools.fill(floatArray, NULL_FLOAT_ARRAY); + assertArrayEquals(new float[10], floatArray); + + // fill - double + double[] doubleArray = new double[10]; + ArrayTools.fill(doubleArray, NULL_DOUBLE_ARRAY); + assertArrayEquals(new double[10], doubleArray); + + // fill - T + String[] stringArray = new String[10]; + ArrayTools.fill(stringArray, NULL_STRING_ARRAY); + assertArrayEquals(new String[10], stringArray); + } + + @Test + void fill_WithValuesInRange() { + // fill - char + char[] charArray = new char[10]; + ArrayTools.fill(charArray, 2, 8, new char[] { 'x', 'y' }); + assertArrayEquals(new char[] { '\0', '\0', 'x', 'y', 'x', 'y', 'x', 'y', '\0', '\0' }, charArray); + // fill - byte + byte[] byteArray = new byte[10]; + ArrayTools.fill(byteArray, 2, 8, new byte[] { 1, 2 }); + assertArrayEquals(new byte[] { 0, 0, 1, 2, 1, 2, 1, 2, 0, 0 }, byteArray); + // fill - short + short[] shortArray = new short[10]; + ArrayTools.fill(shortArray, 2, 8, new short[] { 1, 2 }); + assertArrayEquals(new short[] { 0, 0, 1, 2, 1, 2, 1, 2, 0, 0 }, shortArray); + // fill - int + int[] intArray = new int[10]; + ArrayTools.fill(intArray, 2, 8, new int[] { 1, 2 }); + assertArrayEquals(new int[] { 0, 0, 1, 2, 1, 2, 1, 2, 0, 0 }, intArray); + // fill - long + long[] longArray = new long[10]; + ArrayTools.fill(longArray, 2, 8, new long[] { 1, 2 }); + assertArrayEquals(new long[] { 0, 0, 1, 2, 1, 2, 1, 2, 0, 0 }, longArray); + // fill - float + float[] floatArray = new float[10]; + ArrayTools.fill(floatArray, 2, 8, new float[] { 1.1F, 2.2F }); + assertArrayEquals(new float[] { 0F, 0F, 1.1F, 2.2F, 1.1F, 2.2F, 1.1F, 2.2F, 0F, 0F }, floatArray); + // fill - double + double[] doubleArray = new double[10]; + ArrayTools.fill(doubleArray, 2, 8, new double[] { 0.1, 0.2 }); + assertArrayEquals(new double[] { 0.0, 0.0, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.0, 0.0 }, doubleArray); + // fill - T + String[] stringArray = new String[10]; + ArrayTools.fill(stringArray, 2, 8, new String[] { "Java", "C++" }); + assertArrayEquals(new String[] { null, null, "Java", "C++", "Java", "C++", "Java", "C++", null, null }, + stringArray); + } + + @Test + void fill_WithValues_OutOfRange() { + // fill - char + char[] charArray = new char[10]; + ArrayTools.fill(charArray, 10, 20, new char[] { 'x', 'y' }); + assertArrayEquals(new char[10], charArray); + // fill - byte + byte[] byteArray = new byte[10]; + ArrayTools.fill(byteArray, 10, 20, new byte[] { 1, 2 }); + assertArrayEquals(new byte[10], byteArray); + // fill - short + short[] shortArray = new short[10]; + ArrayTools.fill(shortArray, 10, 20, new short[] { 1, 2 }); + assertArrayEquals(new short[10], shortArray); + // fill - int + int[] intArray = new int[10]; + ArrayTools.fill(intArray, 10, 20, new int[] { 1, 2 }); + assertArrayEquals(new int[10], intArray); + // fill - long + long[] longArray = new long[10]; + ArrayTools.fill(longArray, 10, 20, new long[] { 1, 2 }); + assertArrayEquals(new long[10], longArray); + // fill - float + float[] floatArray = new float[10]; + ArrayTools.fill(floatArray, 10, 20, new float[] { 1.1F, 2.2F }); + assertArrayEquals(new float[10], floatArray); + // fill - double + double[] doubleArray = new double[10]; + ArrayTools.fill(doubleArray, 10, 20, new double[] { 0.1, 0.2 }); + assertArrayEquals(new double[10], doubleArray); + // fill - T + String[] stringArray = new String[10]; + ArrayTools.fill(stringArray, 10, 20, new String[] { "Java", "C++" }); + assertArrayEquals(new String[10], stringArray); + } + + @Test + void fill_WithValues_NegativeRange() { + // fill - char + char[] charArray = new char[10]; + ArrayTools.fill(charArray, -5, 5, new char[] {'w'}); + assertArrayEquals(new char[]{'w', 'w', 'w', 'w', 'w', '\0', '\0', '\0', '\0', '\0'}, charArray); + // fill - byte + byte[] byteArray = new byte[10]; + ArrayTools.fill(byteArray, -5, 5, new byte[] {108}); + assertArrayEquals(new byte[]{108, 108, 108, 108, 108, 0, 0, 0, 0, 0}, byteArray); + // fill - short + short[] shortArray = new short[10]; + ArrayTools.fill(shortArray, -5, 5, new short[] {108}); + assertArrayEquals(new short[]{108, 108, 108, 108, 108, 0, 0, 0, 0, 0}, shortArray); + // fill - int + int[] intArray = new int[10]; + ArrayTools.fill(intArray, -5, 5, new int[] {108}); + assertArrayEquals(new int[]{108, 108, 108, 108, 108, 0, 0, 0, 0, 0}, intArray); + // fill - long + long[] longArray = new long[10]; + ArrayTools.fill(longArray, -5, 5, new long[] {108L}); + assertArrayEquals(new long[]{108L, 108L, 108L, 108L, 108L, 0L, 0L, 0L, 0L, 0L}, longArray); + // fill - float + float[] floatArray = new float[10]; + ArrayTools.fill(floatArray, -5, 5, new float[] {108.01F}); + assertArrayEquals(new float[]{108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}, floatArray); + // fill - double + double[] doubleArray = new double[10]; + ArrayTools.fill(doubleArray, -5, 5, new double[] {108.01}); + assertArrayEquals(new double[]{108.01, 108.01, 108.01, 108.01, 108.01, 0.0, 0.0, 0.0, 0.0, 0.0}, doubleArray); + // fill - T + String[] stringArray = new String[10]; + ArrayTools.fill(stringArray, -5, 5, new String[] {"test"}); + assertArrayEquals(new String[]{"test", "test", "test", "test", "test", null, null, null, null, null}, stringArray); + } + + @Test + void fill_WithValues_ExactRange() { + // fill - char + char[] charArray = new char[10]; + ArrayTools.fill(charArray, 0, 10, new char[] {'w'}); + assertArrayEquals(new char[]{'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w'}, charArray); + // fill - byte + byte[] byteArray = new byte[10]; + ArrayTools.fill(byteArray, 0, 10, new byte[] {108}); + assertArrayEquals(new byte[]{108, 108, 108, 108, 108, 108, 108, 108, 108, 108}, byteArray); + // fill - short + short[] shortArray = new short[10]; + ArrayTools.fill(shortArray, 0, 10, new short[] {108}); + assertArrayEquals(new short[]{108, 108, 108, 108, 108, 108, 108, 108, 108, 108}, shortArray); + // fill - int + int[] intArray = new int[10]; + ArrayTools.fill(intArray, 0, 10, new int[] {108}); + assertArrayEquals(new int[]{108, 108, 108, 108, 108, 108, 108, 108, 108, 108}, intArray); + // fill - long + long[] longArray = new long[10]; + ArrayTools.fill(longArray, 0, 10, new long[] {108L}); + assertArrayEquals(new long[]{108L, 108L, 108L, 108L, 108L, 108L, 108L, 108L, 108L, 108L}, longArray); + // fill - float + float[] floatArray = new float[10]; + ArrayTools.fill(floatArray, 0, 10, new float[] {108.01F}); + assertArrayEquals(new float[]{108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 108.01F}, floatArray); + // fill - double + double[] doubleArray = new double[10]; + ArrayTools.fill(doubleArray, 0, 10, new double[] {108.01}); + assertArrayEquals(new double[]{108.01, 108.01, 108.01, 108.01, 108.01, 108.01, 108.01, 108.01, 108.01, 108.01}, doubleArray); + // fill - T + String[] stringArray = new String[10]; + ArrayTools.fill(stringArray, 0, 10, new String[] {"test"}); + assertArrayEquals(new String[]{"test", "test", "test", "test", "test", "test", "test", "test", "test", "test"}, stringArray); + } + + @Test + void fill_WithValues_Overlap() { + // fill - char + char[] charArray = new char[10]; + ArrayTools.fill(charArray, 0, 5, new char[] {'a', 'b', 'c', 'd', 'e', 'f'}); + assertArrayEquals(new char[]{'a', 'b', 'c', 'd', 'e', '\0', '\0', '\0', '\0', '\0'}, charArray); + // fill - byte + byte[] byteArray = new byte[10]; + ArrayTools.fill(byteArray, 0, 5, new byte[] {1, 2, 3, 4, 5, 6}); + assertArrayEquals(new byte[]{1, 2, 3, 4, 5, 0, 0, 0, 0, 0}, byteArray); + // fill - short + short[] shortArray = new short[10]; + ArrayTools.fill(shortArray, 0, 5, new short[] {1, 2, 3, 4, 5, 6}); + assertArrayEquals(new short[]{1, 2, 3, 4, 5, 0, 0, 0, 0, 0}, shortArray); + // fill - int + int[] intArray = new int[10]; + ArrayTools.fill(intArray, 0, 5, new int[] {1, 2, 3, 4, 5, 6}); + assertArrayEquals(new int[]{1, 2, 3, 4, 5, 0, 0, 0, 0, 0}, intArray); + // fill - long + long[] longArray = new long[10]; + ArrayTools.fill(longArray, 0, 5, new long[] {1L, 2L, 3L, 4L, 5L, 6L}); + assertArrayEquals(new long[]{1L, 2L, 3L, 4L, 5L, 0L, 0L, 0L, 0L, 0L}, longArray); + // fill - float + float[] floatArray = new float[10]; + ArrayTools.fill(floatArray, 0, 5, new float[] {1.1F, 2.2F, 3.3F, 4.4F, 5.5F, 6.6F}); + assertArrayEquals(new float[]{1.1F, 2.2F, 3.3F, 4.4F, 5.5F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}, floatArray); + // fill - double + double[] doubleArray = new double[10]; + ArrayTools.fill(doubleArray, 0, 5, new double[] {1.1, 2.2, 3.3, 4.4, 5.5, 6.6}); + assertArrayEquals(new double[]{1.1, 2.2, 3.3, 4.4, 5.5, 0.0, 0.0, 0.0, 0.0, 0.0}, doubleArray); + // fill - T + String[] stringArray = new String[10]; + ArrayTools.fill(stringArray, 0, 5, new String[] {"aaa", "bbb", "ccc", "ddd", "eee", "fff"}); + assertArrayEquals(new String[]{"aaa", "bbb", "ccc", "ddd", "eee", null, null, null, null, null}, stringArray); + } + + // ================================ + // #endregion + // ================================ + + // ================================ // #region - indexOf - // #endregion + // ================================ + @Test + void indexOfWithPredicate_NullPredicate_ThrowsNullPointerException() { + assertThrows(NullPointerException.class, () -> ArrayTools.indexOfWithPredicate(new String[] {}, null)); + } + + @Test + void indexOfWithPredicate_NullArray_ReturnsNotFoundIndex() { + Predicate predicate = s -> s.equals("test"); + int result = ArrayTools.indexOfWithPredicate(null, predicate); + assertEquals(ArrayTools.NOT_FOUND_INDEX, result); + } + + @Test + void indexOfWithPredicate_EmptyArray_ReturnsNotFoundIndex() { + Predicate predicate = s -> s.equals("test"); + int result = ArrayTools.indexOfWithPredicate(new String[] {}, predicate); + assertEquals(ArrayTools.NOT_FOUND_INDEX, result); + } + + @Test + void indexOfWithPredicate_ArrayContainsMatchingElement_ReturnsIndex() { + String[] array = { "apple", "banana", "cherry" }; + Predicate predicate = s -> s.equals("banana"); + int result = ArrayTools.indexOfWithPredicate(array, predicate); + assertEquals(1, result); + } + + @Test + void indexOfWithPredicate_ArrayDoesNotContainMatchingElement_ReturnsNotFoundIndex() { + String[] array = { "apple", "banana", "cherry" }; + Predicate predicate = s -> s.equals("orange"); + int result = ArrayTools.indexOfWithPredicate(array, predicate); + assertEquals(ArrayTools.NOT_FOUND_INDEX, result); + } + + @Test + void indexOf_NullArray_ReturnsNotFound() { + // T + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((String[]) null, "test")); + // char + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((char[]) null, 'a')); + // byte + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((byte[]) null, (byte) 1)); + // short + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((short[]) null, (short) 1)); + // int + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((int[]) null, 1)); + // long + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((long[]) null, 1L)); + // float + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((float[]) null, 1.23F)); + // double + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((double[]) null, 1.23)); + } + + @Test + void indexOf_EmptyArray_ReturnsNotFound() { + // T + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new String[] {}, "test")); + // char + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new char[] {}, 'a')); + // byte + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new byte[] {}, (byte) 1)); + // short + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new short[] {}, (short) 1)); + // int + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new int[] {}, 1)); + // long + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new long[] {}, 1L)); + // float + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new float[] {}, 1.23F)); + // double + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new double[] {}, 1.23)); + } + + @Test + void indexOf_ObjectFound_ReturnsIndex() { + // T + assertEquals(1, ArrayTools.indexOf(new String[] { "a", "b", "c" }, "b")); + // char + assertEquals(1, ArrayTools.indexOf(new char[] { 'a', 'b', 'c' }, 'b')); + // byte + assertEquals(1, ArrayTools.indexOf(new byte[] { 1, 2, 3 }, (byte) 2)); + // short + assertEquals(1, ArrayTools.indexOf(new short[] { 1, 2, 3 }, (short) 2)); + // int + assertEquals(1, ArrayTools.indexOf(new int[] { 1, 2, 3 }, 2)); + // long + assertEquals(1, ArrayTools.indexOf(new long[] { 1000000L, 2000000L, 3000000L }, 2000000L)); + // float + assertEquals(1, ArrayTools.indexOf(new float[] { 1.11F, 2.22F, 3.33F }, 2.22F)); + // double + assertEquals(1, ArrayTools.indexOf(new double[] { 1.11, 2.22, 3.33 }, 2.22)); + } + + @Test + void indexOf_ObjectNotFound_ReturnsNotFound() { + // T + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new String[] { "a", "b", "c" }, "d")); + // char + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new char[] { 'a', 'b', 'c' }, 'd')); + // byte + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new byte[] { 1, 2, 3 }, (byte) 4)); + // short + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new short[] { 1, 2, 3 }, (short) 4)); + // int + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new int[] { 1, 2, 3 }, 4)); + // long + assertEquals(ArrayTools.NOT_FOUND_INDEX, + ArrayTools.indexOf(new long[] { 1000000L, 2000000L, 3000000L }, 4000000L)); + // float + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new float[] { 1.11F, 2.22F, 3.33F }, 4.44F)); + // double + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new double[] { 1.11, 2.22, 3.33 }, 4.44)); + } + + @Test + void indexOf_NullObject_ReturnsNotFound() { + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new String[] { "a", "b", "c" }, null)); + } + + @Test + void indexOf_ArrayContainsNull_ReturnsIndex() { + assertEquals(1, ArrayTools.indexOf(new String[] { "a", null, "c" }, null)); + } + + // ================================ + // #endregion + // ================================ + + // ================================ // #region - lastIndexOf - // #endregion + // ================================ - // #region - contains + @Test + void lastIndexOfWithPredicate_NullPredicate_ThrowsNullPointerException() { + assertThrows(NullPointerException.class, () -> ArrayTools.lastIndexOfWithPredicate(new String[] {}, null)); + } + + @Test + void lastIndexOfWithPredicate_NullArray_ReturnsNotFoundIndex() { + Predicate predicate = s -> s.equals("test"); + int result = ArrayTools.lastIndexOfWithPredicate(null, predicate); + assertEquals(ArrayTools.NOT_FOUND_INDEX, result); + } + + @Test + void lastIndexOfWithPredicate_EmptyArray_ReturnsNotFoundIndex() { + Predicate predicate = s -> s.equals("test"); + int result = ArrayTools.lastIndexOfWithPredicate(new String[] {}, predicate); + assertEquals(ArrayTools.NOT_FOUND_INDEX, result); + } + + @Test + void lastIndexOfWithPredicate_ArrayContainsMatchingElement_ReturnsIndex() { + String[] array = { "apple", "banana", "banana", "cherry" }; + Predicate predicate = s -> s.equals("banana"); + int result = ArrayTools.lastIndexOfWithPredicate(array, predicate); + assertEquals(2, result); + } + + @Test + void lastIndexOfWithPredicate_ArrayDoesNotContainMatchingElement_ReturnsNotFoundIndex() { + String[] array = { "apple", "banana", "cherry" }; + Predicate predicate = s -> s.equals("orange"); + int result = ArrayTools.lastIndexOfWithPredicate(array, predicate); + assertEquals(ArrayTools.NOT_FOUND_INDEX, result); + } + + @Test + void lastIndexOf_NullArray_ReturnsNotFound() { + // T + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((String[]) null, "test")); + // char + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((char[]) null, 'a')); + // byte + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((byte[]) null, (byte) 1)); + // short + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((short[]) null, (short) 1)); + // int + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((int[]) null, 1)); + // long + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((long[]) null, 1L)); + // float + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((float[]) null, 1.23F)); + // double + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((double[]) null, 1.23)); + } + + @Test + void lastIndexOf_EmptyArray_ReturnsNotFound() { + // T + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new String[] {}, "test")); + // char + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new char[] {}, 'a')); + // byte + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new byte[] {}, (byte) 1)); + // short + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new short[] {}, (short) 1)); + // int + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new int[] {}, 1)); + // long + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new long[] {}, 1L)); + // float + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new float[] {}, 1.23F)); + // double + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new double[] {}, 1.23)); + } + + @Test + void lastIndexOf_ObjectFound_ReturnsIndex() { + // T + assertEquals(3, ArrayTools.lastIndexOf(new String[] { "a", "b", "c", "b", "c" }, "b")); + // char + assertEquals(3, ArrayTools.lastIndexOf(new char[] { 'a', 'b', 'c', 'b', 'c' }, 'b')); + // byte + assertEquals(3, ArrayTools.lastIndexOf(new byte[] { 1, 2, 3, 2, 3 }, (byte) 2)); + // short + assertEquals(3, ArrayTools.lastIndexOf(new short[] { 1, 2, 3, 2, 3 }, (short) 2)); + // int + assertEquals(3, ArrayTools.lastIndexOf(new int[] { 1, 2, 3, 2, 3 }, 2)); + // long + assertEquals(3, + ArrayTools.lastIndexOf(new long[] { 1000000L, 2000000L, 3000000L, 2000000L, 3000000L }, 2000000L)); + // float + assertEquals(3, ArrayTools.lastIndexOf(new float[] { 1.11F, 2.22F, 3.33F, 2.22F, 3.33F }, 2.22F)); + // double + assertEquals(3, ArrayTools.lastIndexOf(new double[] { 1.11, 2.22, 3.33, 2.22, 3.33 }, 2.22)); + } + + @Test + void lastIndexOf_ObjectNotFound_ReturnsNotFound() { + // T + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new String[] { "a", "b", "c" }, "d")); + // char + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new char[] { 'a', 'b', 'c' }, 'd')); + // byte + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new byte[] { 1, 2, 3 }, (byte) 4)); + // short + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new short[] { 1, 2, 3 }, (short) 4)); + // int + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new int[] { 1, 2, 3 }, 4)); + // long + assertEquals(ArrayTools.NOT_FOUND_INDEX, + ArrayTools.lastIndexOf(new long[] { 1000000L, 2000000L, 3000000L }, 4000000L)); + // float + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new float[] { 1.11F, 2.22F, 3.33F }, 4.44F)); + // double + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new double[] { 1.11, 2.22, 3.33 }, 4.44)); + } + + @Test + void lastIndexOf_NullObject_ReturnsNotFound() { + assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new String[] { "a", "b", "c" }, null)); + } + + @Test + void lastIndexOf_ArrayContainsNull_ReturnsIndex() { + assertEquals(3, ArrayTools.lastIndexOf(new String[] { "a", null, "c", null, "e" }, null)); + } + + // ================================ // #endregion + // ================================ + + // ================================ + // #region - contains + // ================================ + + @Test + void contains_NullArray_ReturnsFalse() { + assertFalse(ArrayTools.contains((String[]) null, "test")); + // char + assertFalse(ArrayTools.contains((char[]) null, 'a')); + // byte + assertFalse(ArrayTools.contains((byte[]) null, (byte) 1)); + // short + assertFalse(ArrayTools.contains((short[]) null, (short) 1)); + // int + assertFalse(ArrayTools.contains((int[]) null, 1)); + // long + assertFalse(ArrayTools.contains((long[]) null, 1L)); + // float + assertFalse(ArrayTools.contains((float[]) null, 1.2F)); + // double + assertFalse(ArrayTools.contains((double[]) null, 1.2)); + } + + @Test + void contains_EmptyArray_ReturnsFalse() { + assertFalse(ArrayTools.contains(new String[] {}, "test")); + // char + assertFalse(ArrayTools.contains(new char[] {}, 'a')); + // byte + assertFalse(ArrayTools.contains(new byte[] {}, (byte) 1)); + // short + assertFalse(ArrayTools.contains(new short[] {}, (short) 1)); + // int + assertFalse(ArrayTools.contains(new int[] {}, 1)); + // long + assertFalse(ArrayTools.contains(new long[] {}, 1L)); + // float + assertFalse(ArrayTools.contains(new float[] {}, 1.2F)); + // double + assertFalse(ArrayTools.contains(new double[] {}, 1.2)); + } + + @Test + void contains_ArrayContainsObject_ReturnsTrue() { + assertTrue(ArrayTools.contains(new String[] { "test", "example" }, "test")); + // char + assertTrue(ArrayTools.contains(new char[] { 'a', 'b', 'c' }, 'a')); + // byte + assertTrue(ArrayTools.contains(new byte[] { 1, 2, 3 }, (byte) 1)); + // short + assertTrue(ArrayTools.contains(new short[] { 1, 2, 3 }, (short) 1)); + // int + assertTrue(ArrayTools.contains(new int[] { 1, 2, 3 }, 1)); + // long + assertTrue(ArrayTools.contains(new long[] { 1000000L, 2000000L, 3000000L }, 1000000L)); + // float + assertTrue(ArrayTools.contains(new float[] { 1.11F, 2.22F, 3.33F }, 2.22F)); + // double + assertTrue(ArrayTools.contains(new double[] { 1.11, 2.22, 3.33 }, 2.22)); + } + + @Test + void contains_ArrayDoesNotContainObject_ReturnsFalse() { + // T + assertFalse(ArrayTools.contains(new String[] { "a", "b", "c" }, "d")); + // char + assertFalse(ArrayTools.contains(new char[] { 'a', 'b', 'c' }, 'd')); + // byte + assertFalse(ArrayTools.contains(new byte[] { 1, 2, 3 }, (byte) 4)); + // short + assertFalse(ArrayTools.contains(new short[] { 1, 2, 3 }, (short) 4)); + // int + assertFalse(ArrayTools.contains(new int[] { 1, 2, 3 }, 4)); + // long + assertFalse(ArrayTools.contains(new long[] { 1000000L, 2000000L, 3000000L }, 4000000L)); + // float + assertFalse(ArrayTools.contains(new float[] { 1.11F, 2.22F, 3.33F }, 4.44F)); + // double + assertFalse(ArrayTools.contains(new double[] { 1.11, 2.22, 3.33 }, 4.44)); + } + + @Test + void contains_ObjectIsNull_ReturnsFalse() { + assertFalse(ArrayTools.contains(new String[] { "test", "example" }, null)); + } + + @Test + void contains_ArrayContainsNull_ReturnsTrue() { + assertTrue(ArrayTools.contains(new String[] { "test", null }, null)); + } + + // ================================ + // #endregion + // ================================ } From 6c225513cad3b9d5b31aa39ce4891cbcaccd95f7 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Wed, 1 Jan 2025 17:22:42 +0800 Subject: [PATCH 23/24] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=BF=9B=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index 07efbfc..f0a6c78 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,6 +1,6 @@ [ ] 未开始测试 - 0 (0.00%) -[*] 测试未完成 - 4 (06.45%) -[Y] 测试完成 - 36 (58.06%) +[*] 测试未完成 - 3 (04.84%) +[Y] 测试完成 - 37 (59.68%) [-] 无需测试 - 22 (35.48%) xyz.zhouxy.plusone.commons @@ -74,7 +74,7 @@ xyz.zhouxy.plusone.commons │ YearQuarter.java [Y] │ └───util - ArrayTools.java [*] 61 + ArrayTools.java [Y] AssertTools.java [Y] BigDecimals.java [Y] DateTimeTools.java [Y] From 25161044e970bf88f7974808018557ad0c3c2e09 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Wed, 1 Jan 2025 20:37:21 +0800 Subject: [PATCH 24/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=8F=82=E6=95=B0=E7=9A=84=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 10 +- pom.xml | 14 ++ .../PagingAndSortingQueryParamsTests.java | 235 ++++++++++++++---- src/test/resources/mybatis-config.xml | 20 ++ .../test/AccountQueries/AccountQueries.xml | 77 ++++++ 5 files changed, 298 insertions(+), 58 deletions(-) create mode 100644 src/test/resources/mybatis-config.xml create mode 100644 src/test/resources/xyz/zhouxy/plusone/commons/model/dto/test/AccountQueries/AccountQueries.xml diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index f0a6c78..ec459a2 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,6 +1,6 @@ [ ] 未开始测试 - 0 (0.00%) -[*] 测试未完成 - 3 (04.84%) -[Y] 测试完成 - 37 (59.68%) +[*] 测试未完成 - 0 (0.00%) +[Y] 测试完成 - 40 (64.52%) [-] 无需测试 - 22 (35.48%) xyz.zhouxy.plusone.commons @@ -64,9 +64,9 @@ xyz.zhouxy.plusone.commons │ │ ValidatableStringRecord.java [Y] │ │ │ └───dto - │ PageResult.java [*] - │ PagingAndSortingQueryParams.java [*] - │ PagingParams.java [*] + │ PageResult.java [Y] + │ PagingAndSortingQueryParams.java [Y] + │ PagingParams.java [Y] │ UnifiedResponse.java [Y] │ ├───time diff --git a/pom.xml b/pom.xml index d71a63a..e21f4e1 100644 --- a/pom.xml +++ b/pom.xml @@ -82,6 +82,20 @@ test + + org.mybatis + mybatis + 3.5.17 + test + + + + com.h2database + h2 + 2.2.224 + test + + com.fasterxml.jackson.core diff --git a/src/test/java/xyz/zhouxy/plusone/commons/model/dto/test/PagingAndSortingQueryParamsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/model/dto/test/PagingAndSortingQueryParamsTests.java index e94c7d5..2810acc 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/model/dto/test/PagingAndSortingQueryParamsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/model/dto/test/PagingAndSortingQueryParamsTests.java @@ -16,88 +16,217 @@ package xyz.zhouxy.plusone.commons.model.dto.test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + import java.io.IOException; +import java.io.InputStream; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.Statement; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.Map; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.h2.jdbcx.JdbcDataSource; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; +import xyz.zhouxy.plusone.commons.model.dto.PageResult; import xyz.zhouxy.plusone.commons.model.dto.PagingAndSortingQueryParams; import xyz.zhouxy.plusone.commons.model.dto.PagingParams; @Slf4j -public // -class PagingAndSortingQueryParamsTests { +public class PagingAndSortingQueryParamsTests { + + static SqlSessionFactory sqlSessionFactory; + + @BeforeAll + static void setUp() throws Exception { + initDatabase(); + + String resource = "mybatis-config.xml"; + InputStream inputStream = Resources.getResourceAsStream(resource); + sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); + } + + static void initDatabase() throws Exception { + JdbcDataSource dataSource = new JdbcDataSource(); + dataSource.setURL("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=FALSE;MODE=MySQL"); + dataSource.setUser("sa"); + dataSource.setPassword(""); + + List data = Lists.newArrayList( + new AccountVO(1L, "zhouxy01", "zhouxy01@qq.com", 0, 108L, LocalDateTime.of(2020, 1, 1, 13, 15), 0L), + new AccountVO(2L, "zhouxy02", "zhouxy02@qq.com", 0, 108L, LocalDateTime.of(2020, 1, 2, 13, 15), 0L), + new AccountVO(3L, "zhouxy03", "zhouxy03@qq.com", 0, 108L, LocalDateTime.of(2020, 1, 3, 13, 15), 0L), + new AccountVO(4L, "zhouxy04", "zhouxy04@qq.com", 0, 108L, LocalDateTime.of(2020, 1, 4, 13, 15), 0L), + new AccountVO(5L, "zhouxy05", "zhouxy05@qq.com", 0, 108L, LocalDateTime.of(2020, 1, 5, 13, 15), 0L), + new AccountVO(6L, "zhouxy06", "zhouxy06@qq.com", 0, 108L, LocalDateTime.of(2024, 1, 6, 13, 15), 0L), + new AccountVO(7L, "zhouxy07", "zhouxy07@qq.com", 0, 108L, LocalDateTime.of(2024, 1, 7, 13, 15), 0L), + new AccountVO(8L, "zhouxy08", "zhouxy08@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 8, 13, 15), 0L), + new AccountVO(9L, "zhouxy09", "zhouxy09@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 9, 13, 15), 0L), + new AccountVO(10L, "zhouxy10", "zhouxy10@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 10, 13, 15), 0L), + new AccountVO(11L, "zhouxy11", "zhouxy11@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 11, 13, 15), 0L), + new AccountVO(12L, "zhouxy12", "zhouxy12@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 12, 13, 15), 0L), + new AccountVO(13L, "zhouxy13", "zhouxy13@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 13, 13, 15), 0L), + new AccountVO(14L, "zhouxy14", "zhouxy14@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 14, 13, 15), 0L), + new AccountVO(15L, "zhouxy15", "zhouxy15@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 15, 13, 15), 0L), + new AccountVO(16L, "zhouxy16", "zhouxy16@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 16, 13, 15), 0L), + new AccountVO(17L, "zhouxy17", "zhouxy17@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 17, 13, 15), 0L), + new AccountVO(18L, "zhouxy18", "zhouxy18@qq.com", 1, 108L, LocalDateTime.of(2024, 10, 18, 13, 15), 0L), + new AccountVO(19L, "zhouxy19", "zhouxy19@qq.com", 1, 108L, LocalDateTime.of(2024, 11, 19, 13, 15), 0L), + new AccountVO(20L, "zhouxy20", "zhouxy20@qq.com", 1, 108L, LocalDateTime.of(2024, 12, 20, 13, 15), 0L) + ); + + try (Connection conn = dataSource.getConnection()) { + try (Statement statement = conn.createStatement()) { + String ddl = "CREATE TABLE sys_account (" + + "\n" + " id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY" + + "\n" + " ,username VARCHAR(255) NOT NULL" + + "\n" + " ,email VARCHAR(255) NOT NULL" + + "\n" + " ,status VARCHAR(2) NOT NULL" + + "\n" + " ,create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP" + + "\n" + " ,created_by BIGINT NOT NULL" + + "\n" + " ,version BIGINT NOT NULL DEFAULT 0" + + "\n" + ")"; + statement.execute(ddl); + } + + String sql = "INSERT INTO sys_account(id, username, email, status, create_time, created_by, version) VALUES" + + "\n" + "(?, ?, ?, ?, ?, ?, ?)"; + try (PreparedStatement statement = conn.prepareStatement(sql)) { + for (AccountVO a : data) { + statement.setObject(1, a.getId()); + statement.setObject(2, a.getUsername()); + statement.setObject(3, a.getEmail()); + statement.setObject(4, a.getStatus()); + statement.setObject(5, a.getCreateTime()); + statement.setObject(6, a.getCreatedBy()); + statement.setObject(7, a.getVersion()); + statement.addBatch(); + } + statement.executeBatch(); + statement.clearBatch(); + } + } + } + static final String JSON_STR = "" + "{\n" + - " \"size\": 15,\n" + + " \"pageNum\": 3,\n" + + " \"size\": 3,\n" + " \"orderBy\": [\"username-asc\"],\n" + " \"createTimeStart\": \"2024-05-06\",\n" + - " \"createTimeEnd\": \"2024-07-06\",\n" + - " \"updateTimeStart\": \"2024-08-06\",\n" + - " \"updateTimeEnd\": \"2024-10-06\",\n" + - " \"mobilePhone\": \"13169053215\"\n" + + " \"createTimeEnd\": \"2030-07-06\"" + + "}"; + + static final String WRONG_JSON_STR = "" + + "{\n" + + " \"pageNum\": 3,\n" + + " \"size\": 3,\n" + + " \"orderBy\": [\"status-asc\"],\n" + + " \"createTimeStart\": \"2024-05-06\",\n" + + " \"createTimeEnd\": \"2030-07-06\"" + "}"; @Test void testJackson() throws Exception { - try { - ObjectMapper om = new ObjectMapper(); - om.registerModule(new JavaTimeModule()); - AccountQueryParams params = om.readValue(JSON_STR, AccountQueryParams.class); - log.info(params.toString()); + ObjectMapper jackson = new ObjectMapper(); + jackson.registerModule(new JavaTimeModule()); + try (SqlSession session = sqlSessionFactory.openSession()) { + AccountQueryParams params = jackson.readValue(JSON_STR, AccountQueryParams.class); PagingParams pagingParams = params.buildPagingParams(); - log.info("pagingParams: {}", pagingParams); + + AccountQueries accountQueries = session.getMapper(AccountQueries.class); + List list = accountQueries.queryAccountList(params, pagingParams); + long count = accountQueries.countAccount(params); + PageResult accountPageResult = PageResult.of(list, count); + log.info(jackson.writeValueAsString(accountPageResult)); + + assertEquals(Lists.newArrayList( + new AccountVO(14L, "zhouxy14", "zhouxy14@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 14, 13, 15), 0L), + new AccountVO(15L, "zhouxy15", "zhouxy15@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 15, 13, 15), 0L), + new AccountVO(16L, "zhouxy16", "zhouxy16@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 16, 13, 15), 0L) + ), accountPageResult.getContent()); + assertEquals(13, accountPageResult.getTotal()); } catch (Exception e) { log.error("测试不通过", e); throw e; } + + AccountQueryParams queryParams = jackson.readValue(WRONG_JSON_STR, AccountQueryParams.class); + assertThrows(IllegalArgumentException.class, () -> queryParams.buildPagingParams()); // NOSONAR } static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - static final TypeAdapter dateAdapter = new TypeAdapter() { - - @Override - public void write(JsonWriter out, LocalDate value) throws IOException { - out.value(dateFormatter.format(value)); - } - - @Override - public LocalDate read(JsonReader in) throws IOException { - return LocalDate.parse(in.nextString(), dateFormatter); - } - - }; - @Test void testGson() throws Exception { - try { - Gson gson = new GsonBuilder() - .registerTypeAdapter(LocalDate.class, dateAdapter) - .create(); + Gson gson = new GsonBuilder() + .registerTypeAdapter(LocalDate.class, new TypeAdapter() { + + @Override + public void write(JsonWriter out, LocalDate value) throws IOException { + out.value(dateFormatter.format(value)); + } + + @Override + public LocalDate read(JsonReader in) throws IOException { + return LocalDate.parse(in.nextString(), dateFormatter); + } + + }) + .create(); + try (SqlSession session = sqlSessionFactory.openSession()) { AccountQueryParams params = gson.fromJson(JSON_STR, AccountQueryParams.class); log.info(params.toString()); PagingParams pagingParams = params.buildPagingParams(); log.info("pagingParams: {}", pagingParams); + AccountQueries accountQueries = session.getMapper(AccountQueries.class); + List list = accountQueries.queryAccountList(params, pagingParams); + long count = accountQueries.countAccount(params); + PageResult accountPageResult = PageResult.of(list, count); + log.info(gson.toJson(accountPageResult)); + + assertEquals(Lists.newArrayList( + new AccountVO(14L, "zhouxy14", "zhouxy14@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 14, 13, 15), 0L), + new AccountVO(15L, "zhouxy15", "zhouxy15@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 15, 13, 15), 0L), + new AccountVO(16L, "zhouxy16", "zhouxy16@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 16, 13, 15), 0L) + ), accountPageResult.getContent()); + assertEquals(13, accountPageResult.getTotal()); } catch (Exception e) { log.error("测试不通过", e); throw e; } + + AccountQueryParams queryParams = gson.fromJson(WRONG_JSON_STR, AccountQueryParams.class); + assertThrows(IllegalArgumentException.class, () -> queryParams.buildPagingParams()); // NOSONAR } } @@ -113,15 +242,7 @@ class AccountQueryParams extends PagingAndSortingQueryParams { private static final Map PROPERTY_COLUMN_MAP = ImmutableMap.builder() .put("id", "id") .put("username", "username") - .put("email", "email") - .put("mobilePhone", "mobile_phone") - .put("status", "status") - .put("nickname", "nickname") - .put("sex", "sex") - .put("createdBy", "created_by") .put("createTime", "create_time") - .put("updatedBy", "updated_by") - .put("updateTime", "update_time") .build(); public AccountQueryParams() { @@ -131,17 +252,10 @@ class AccountQueryParams extends PagingAndSortingQueryParams { private @Getter @Setter Long id; private @Getter @Setter String username; private @Getter @Setter String email; - private @Getter @Setter String mobilePhone; private @Getter @Setter Integer status; - private @Getter @Setter String nickname; - private @Getter @Setter Integer sex; private @Getter @Setter Long createdBy; private @Getter @Setter LocalDate createTimeStart; private @Setter LocalDate createTimeEnd; - private @Getter @Setter Long updatedBy; - private @Getter @Setter LocalDate updateTimeStart; - private @Setter LocalDate updateTimeEnd; - private @Getter @Setter Long roleId; public LocalDate getCreateTimeEnd() { if (this.createTimeEnd == null) { @@ -149,11 +263,26 @@ class AccountQueryParams extends PagingAndSortingQueryParams { } return this.createTimeEnd.plusDays(1); } - - public LocalDate getUpdateTimeEnd() { - if (this.updateTimeEnd == null) { - return null; - } - return this.updateTimeEnd.plusDays(1); - } +} + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +class AccountVO { + private Long id; + private String username; + private String email; + private Integer status; + private Long createdBy; + private LocalDateTime createTime; + private Long version; +} + +interface AccountQueries { + + List queryAccountList(@Param("query") AccountQueryParams query, + @Param("page") PagingParams page); + + long countAccount(@Param("query") AccountQueryParams query); } diff --git a/src/test/resources/mybatis-config.xml b/src/test/resources/mybatis-config.xml new file mode 100644 index 0000000..903a7bb --- /dev/null +++ b/src/test/resources/mybatis-config.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/xyz/zhouxy/plusone/commons/model/dto/test/AccountQueries/AccountQueries.xml b/src/test/resources/xyz/zhouxy/plusone/commons/model/dto/test/AccountQueries/AccountQueries.xml new file mode 100644 index 0000000..af25f08 --- /dev/null +++ b/src/test/resources/xyz/zhouxy/plusone/commons/model/dto/test/AccountQueries/AccountQueries.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + +