From a375f3bfc5b37c2db68780363713f5d23a4c6de0 Mon Sep 17 00:00:00 2001 From: zhanghuang090 <78581375+zhanghuang090@users.noreply.github.com> Date: Wed, 9 Sep 2026 09:33:32 +0800 Subject: [PATCH 1/4] feat: add UUID string converter (#1017) --- .../converters/DefaultConverterLoader.java | 4 + .../converters/uuid/UuidStringConverter.java | 58 +++++++ .../sheet/converter/UuidConverterTest.java | 152 ++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/UuidStringConverter.java create mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java index f17d9b62c..9fc87f2b6 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java @@ -78,6 +78,7 @@ import org.apache.fesod.sheet.converters.string.StringNumberConverter; import org.apache.fesod.sheet.converters.string.StringStringConverter; import org.apache.fesod.sheet.converters.url.UrlImageConverter; +import org.apache.fesod.sheet.converters.uuid.UuidStringConverter; /** * Load default handler @@ -147,6 +148,7 @@ private static void initAllConverter() { putAllConverter(new StringNumberConverter()); putAllConverter(new StringStringConverter()); putAllConverter(new StringErrorConverter()); + putAllConverter(new UuidStringConverter()); allConverter = Collections.unmodifiableMap(allConverter); } @@ -171,6 +173,7 @@ private static void initDefaultWriteConverter() { putWriteConverter(new ByteArrayImageConverter()); putWriteConverter(new BoxingByteArrayImageConverter()); putWriteConverter(new UrlImageConverter()); + putWriteConverter(new UuidStringConverter()); // In some cases, it must be converted to string putWriteStringConverter(new BigDecimalStringConverter()); @@ -187,6 +190,7 @@ private static void initDefaultWriteConverter() { putWriteStringConverter(new LongStringConverter()); putWriteStringConverter(new ShortStringConverter()); putWriteStringConverter(new StringStringConverter()); + putWriteStringConverter(new UuidStringConverter()); defaultWriteConverter = Collections.unmodifiableMap(defaultWriteConverter); } diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/UuidStringConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/UuidStringConverter.java new file mode 100644 index 000000000..822556e7a --- /dev/null +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/UuidStringConverter.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.fesod.sheet.converters.uuid; + +import java.util.UUID; +import org.apache.fesod.sheet.converters.Converter; +import org.apache.fesod.sheet.enums.CellDataTypeEnum; +import org.apache.fesod.sheet.metadata.GlobalConfiguration; +import org.apache.fesod.sheet.metadata.data.ReadCellData; +import org.apache.fesod.sheet.metadata.data.WriteCellData; +import org.apache.fesod.sheet.metadata.property.ExcelContentProperty; + +/** + * Converts UUID values to canonical lowercase strings and reads strings using {@link UUID#fromString(String)}. + * Empty strings are treated as missing values, like blank cells. + */ +public class UuidStringConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return UUID.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public UUID convertToJavaData( + ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + String value = cellData.getStringValue(); + return value == null || value.isEmpty() ? null : UUID.fromString(value); + } + + @Override + public WriteCellData convertToExcelData( + UUID value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + return new WriteCellData<>(value.toString()); + } +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java new file mode 100644 index 000000000..63872d684 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.fesod.sheet.converter; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import lombok.Data; +import org.apache.fesod.sheet.converters.ConverterKeyBuild; +import org.apache.fesod.sheet.converters.DefaultConverterLoader; +import org.apache.fesod.sheet.converters.uuid.UuidStringConverter; +import org.apache.fesod.sheet.enums.CellDataTypeEnum; +import org.apache.fesod.sheet.exception.ExcelDataConvertException; +import org.apache.fesod.sheet.metadata.GlobalConfiguration; +import org.apache.fesod.sheet.metadata.data.ReadCellData; +import org.apache.fesod.sheet.metadata.data.WriteCellData; +import org.apache.fesod.sheet.testkit.base.AbstractExcelTest; +import org.apache.fesod.sheet.testkit.enums.ExcelFormat; +import org.apache.fesod.sheet.testkit.helpers.RoundTripHelper; +import org.apache.fesod.sheet.testkit.params.ExcelFormatSource; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class UuidConverterTest extends AbstractExcelTest { + + private static final String TEXT = "123e4567-e89b-12d3-a456-426614174000"; + private final UuidStringConverter converter = new UuidStringConverter(); + private final GlobalConfiguration configuration = new GlobalConfiguration(); + + @Test + void supportsUuidStringsAndRegistersAllLookupPaths() { + Assertions.assertEquals(UUID.class, converter.supportJavaTypeKey()); + Assertions.assertEquals(CellDataTypeEnum.STRING, converter.supportExcelTypeKey()); + Assertions.assertInstanceOf( + UuidStringConverter.class, + DefaultConverterLoader.loadDefaultReadConverter() + .get(ConverterKeyBuild.buildKey(UUID.class, CellDataTypeEnum.STRING))); + Assertions.assertInstanceOf( + UuidStringConverter.class, + DefaultConverterLoader.loadDefaultWriteConverter().get(ConverterKeyBuild.buildKey(UUID.class))); + Assertions.assertInstanceOf( + UuidStringConverter.class, + DefaultConverterLoader.loadDefaultWriteConverter() + .get(ConverterKeyBuild.buildKey(UUID.class, CellDataTypeEnum.STRING))); + } + + @ParameterizedTest + @ValueSource( + strings = { + TEXT, + "123E4567-E89B-12D3-A456-426614174000", + "00000000-0000-0000-0000-000000000000", + "ffffffff-ffff-ffff-ffff-ffffffffffff" + }) + void readsAndWritesCanonicalStrings(String input) { + UUID value = converter.convertToJavaData(new ReadCellData<>(input), null, configuration); + WriteCellData cell = converter.convertToExcelData(value, null, configuration); + Assertions.assertEquals(CellDataTypeEnum.STRING, cell.getType()); + Assertions.assertEquals(input.toLowerCase(java.util.Locale.ROOT), cell.getStringValue()); + } + + @ParameterizedTest + @ValueSource(strings = {"not-a-uuid", "123e4567-e89b-12d3-a456-42661417400g", "123e4567e89b12d3a456426614174000"}) + void rejectsInvalidInput(String input) { + Assertions.assertThrows( + IllegalArgumentException.class, + () -> converter.convertToJavaData(new ReadCellData<>(input), null, configuration)); + } + + @Test + void readsEmptyStringAsMissingValue() { + Assertions.assertNull(converter.convertToJavaData(new ReadCellData<>(""), null, configuration)); + } + + @ParameterizedTest + @ExcelFormatSource + void roundTripsUuidAndNullFieldsWithoutCustomRegistration(ExcelFormat format) throws Exception { + File file = createTempFile(format); + UuidData first = new UuidData(); + first.setId(UUID.fromString(TEXT)); + first.setLabel("populated"); + UuidData second = new UuidData(); + second.setLabel("null UUID"); + List expected = Arrays.asList(first, second); + Assertions.assertEquals(expected, RoundTripHelper.writeAndRead(file, UuidData.class, expected)); + List strings = RoundTripHelper.read(file, StringData.class); + Assertions.assertEquals(TEXT, strings.get(0).getId()); + } + + @ParameterizedTest + @ExcelFormatSource + void readsUppercaseAndBlankCells(ExcelFormat format) throws Exception { + File file = createTempFile(format); + StringData first = new StringData(); + first.setId(TEXT.toUpperCase(java.util.Locale.ROOT)); + first.setLabel("uppercase"); + StringData second = new StringData(); + second.setId(""); + second.setLabel("blank UUID"); + RoundTripHelper.write(file, StringData.class, Arrays.asList(first, second)); + List rows = RoundTripHelper.read(file, UuidData.class); + Assertions.assertEquals(2, rows.size()); + Assertions.assertEquals(UUID.fromString(TEXT), rows.get(0).getId()); + Assertions.assertNull(rows.get(1).getId()); + } + + @ParameterizedTest + @ExcelFormatSource + void reportsInvalidCellAsConversionFailure(ExcelFormat format) throws Exception { + File file = createTempFile(format); + StringData row = new StringData(); + row.setId("not-a-uuid"); + row.setLabel("invalid"); + RoundTripHelper.write(file, StringData.class, Collections.singletonList(row)); + ExcelDataConvertException error = Assertions.assertThrows( + ExcelDataConvertException.class, () -> RoundTripHelper.read(file, UuidData.class)); + Assertions.assertInstanceOf(IllegalArgumentException.class, error.getCause()); + } + + @Data + public static class UuidData { + private UUID id; + private String label; + } + + @Data + public static class StringData { + private String id; + private String label; + } +} From dbaada63f241f3e021b8a1d82ad5bc6f8fd3dd76 Mon Sep 17 00:00:00 2001 From: zhanghuang090 <78581375+zhanghuang090@users.noreply.github.com> Date: Wed, 16 Sep 2026 10:01:40 +0800 Subject: [PATCH 2/4] test: address UUID converter review feedback --- .../apache/fesod/sheet/converter/UuidConverterTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java index 63872d684..1e25ef899 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.UUID; import lombok.Data; import org.apache.fesod.sheet.converters.ConverterKeyBuild; @@ -33,15 +34,18 @@ import org.apache.fesod.sheet.metadata.GlobalConfiguration; import org.apache.fesod.sheet.metadata.data.ReadCellData; import org.apache.fesod.sheet.metadata.data.WriteCellData; +import org.apache.fesod.sheet.testkit.Tags; import org.apache.fesod.sheet.testkit.base.AbstractExcelTest; import org.apache.fesod.sheet.testkit.enums.ExcelFormat; import org.apache.fesod.sheet.testkit.helpers.RoundTripHelper; import org.apache.fesod.sheet.testkit.params.ExcelFormatSource; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +@Tag(Tags.ROUND_TRIP) class UuidConverterTest extends AbstractExcelTest { private static final String TEXT = "123e4567-e89b-12d3-a456-426614174000"; @@ -77,7 +81,7 @@ void readsAndWritesCanonicalStrings(String input) { UUID value = converter.convertToJavaData(new ReadCellData<>(input), null, configuration); WriteCellData cell = converter.convertToExcelData(value, null, configuration); Assertions.assertEquals(CellDataTypeEnum.STRING, cell.getType()); - Assertions.assertEquals(input.toLowerCase(java.util.Locale.ROOT), cell.getStringValue()); + Assertions.assertEquals(input.toLowerCase(Locale.ROOT), cell.getStringValue()); } @ParameterizedTest @@ -113,7 +117,7 @@ void roundTripsUuidAndNullFieldsWithoutCustomRegistration(ExcelFormat format) th void readsUppercaseAndBlankCells(ExcelFormat format) throws Exception { File file = createTempFile(format); StringData first = new StringData(); - first.setId(TEXT.toUpperCase(java.util.Locale.ROOT)); + first.setId(TEXT.toUpperCase(Locale.ROOT)); first.setLabel("uppercase"); StringData second = new StringData(); second.setId(""); From 13fe9b0ce8bc5ac64b82f58af5429abc5205e31b Mon Sep 17 00:00:00 2001 From: zhanghuang090 <78581375+zhanghuang090@users.noreply.github.com> Date: Wed, 16 Sep 2026 10:48:51 +0800 Subject: [PATCH 3/4] fix: align UUID converter naming and handle blank input --- .../converters/DefaultConverterLoader.java | 8 +-- ...onverter.java => UUIDStringConverter.java} | 7 +-- .../sheet/converter/UuidConverterTest.java | 49 ++++++++++++++----- 3 files changed, 46 insertions(+), 18 deletions(-) rename fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/{UuidStringConverter.java => UUIDStringConverter.java} (86%) diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java index 9fc87f2b6..9498dd112 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java @@ -78,7 +78,7 @@ import org.apache.fesod.sheet.converters.string.StringNumberConverter; import org.apache.fesod.sheet.converters.string.StringStringConverter; import org.apache.fesod.sheet.converters.url.UrlImageConverter; -import org.apache.fesod.sheet.converters.uuid.UuidStringConverter; +import org.apache.fesod.sheet.converters.uuid.UUIDStringConverter; /** * Load default handler @@ -148,7 +148,7 @@ private static void initAllConverter() { putAllConverter(new StringNumberConverter()); putAllConverter(new StringStringConverter()); putAllConverter(new StringErrorConverter()); - putAllConverter(new UuidStringConverter()); + putAllConverter(new UUIDStringConverter()); allConverter = Collections.unmodifiableMap(allConverter); } @@ -173,7 +173,7 @@ private static void initDefaultWriteConverter() { putWriteConverter(new ByteArrayImageConverter()); putWriteConverter(new BoxingByteArrayImageConverter()); putWriteConverter(new UrlImageConverter()); - putWriteConverter(new UuidStringConverter()); + putWriteConverter(new UUIDStringConverter()); // In some cases, it must be converted to string putWriteStringConverter(new BigDecimalStringConverter()); @@ -190,7 +190,7 @@ private static void initDefaultWriteConverter() { putWriteStringConverter(new LongStringConverter()); putWriteStringConverter(new ShortStringConverter()); putWriteStringConverter(new StringStringConverter()); - putWriteStringConverter(new UuidStringConverter()); + putWriteStringConverter(new UUIDStringConverter()); defaultWriteConverter = Collections.unmodifiableMap(defaultWriteConverter); } diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/UuidStringConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/UUIDStringConverter.java similarity index 86% rename from fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/UuidStringConverter.java rename to fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/UUIDStringConverter.java index 822556e7a..68c2a14a2 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/UuidStringConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/uuid/UUIDStringConverter.java @@ -20,6 +20,7 @@ package org.apache.fesod.sheet.converters.uuid; import java.util.UUID; +import org.apache.fesod.common.util.StringUtils; import org.apache.fesod.sheet.converters.Converter; import org.apache.fesod.sheet.enums.CellDataTypeEnum; import org.apache.fesod.sheet.metadata.GlobalConfiguration; @@ -29,9 +30,9 @@ /** * Converts UUID values to canonical lowercase strings and reads strings using {@link UUID#fromString(String)}. - * Empty strings are treated as missing values, like blank cells. + * Blank strings are treated as missing values, like blank cells. Leading and trailing whitespace is trimmed before parsing. */ -public class UuidStringConverter implements Converter { +public class UUIDStringConverter implements Converter { @Override public Class supportJavaTypeKey() { @@ -47,7 +48,7 @@ public CellDataTypeEnum supportExcelTypeKey() { public UUID convertToJavaData( ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { String value = cellData.getStringValue(); - return value == null || value.isEmpty() ? null : UUID.fromString(value); + return StringUtils.isBlank(value) ? null : UUID.fromString(value.trim()); } @Override diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java index 1e25ef899..53319073d 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java @@ -28,7 +28,7 @@ import lombok.Data; import org.apache.fesod.sheet.converters.ConverterKeyBuild; import org.apache.fesod.sheet.converters.DefaultConverterLoader; -import org.apache.fesod.sheet.converters.uuid.UuidStringConverter; +import org.apache.fesod.sheet.converters.uuid.UUIDStringConverter; import org.apache.fesod.sheet.enums.CellDataTypeEnum; import org.apache.fesod.sheet.exception.ExcelDataConvertException; import org.apache.fesod.sheet.metadata.GlobalConfiguration; @@ -43,13 +43,14 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; @Tag(Tags.ROUND_TRIP) class UuidConverterTest extends AbstractExcelTest { private static final String TEXT = "123e4567-e89b-12d3-a456-426614174000"; - private final UuidStringConverter converter = new UuidStringConverter(); + private final UUIDStringConverter converter = new UUIDStringConverter(); private final GlobalConfiguration configuration = new GlobalConfiguration(); @Test @@ -57,14 +58,14 @@ void supportsUuidStringsAndRegistersAllLookupPaths() { Assertions.assertEquals(UUID.class, converter.supportJavaTypeKey()); Assertions.assertEquals(CellDataTypeEnum.STRING, converter.supportExcelTypeKey()); Assertions.assertInstanceOf( - UuidStringConverter.class, + UUIDStringConverter.class, DefaultConverterLoader.loadDefaultReadConverter() .get(ConverterKeyBuild.buildKey(UUID.class, CellDataTypeEnum.STRING))); Assertions.assertInstanceOf( - UuidStringConverter.class, + UUIDStringConverter.class, DefaultConverterLoader.loadDefaultWriteConverter().get(ConverterKeyBuild.buildKey(UUID.class))); Assertions.assertInstanceOf( - UuidStringConverter.class, + UUIDStringConverter.class, DefaultConverterLoader.loadDefaultWriteConverter() .get(ConverterKeyBuild.buildKey(UUID.class, CellDataTypeEnum.STRING))); } @@ -85,16 +86,34 @@ void readsAndWritesCanonicalStrings(String input) { } @ParameterizedTest - @ValueSource(strings = {"not-a-uuid", "123e4567-e89b-12d3-a456-42661417400g", "123e4567e89b12d3a456426614174000"}) + @ValueSource( + strings = { + "not-a-uuid", + "123e4567-e89b-12d3-a456-42661417400g", + "123e4567e89b12d3a456426614174000", + " not-a-uuid\t", + "123e4567-e89b-12d3-a456-42661417 4000" + }) void rejectsInvalidInput(String input) { Assertions.assertThrows( IllegalArgumentException.class, () -> converter.convertToJavaData(new ReadCellData<>(input), null, configuration)); } - @Test - void readsEmptyStringAsMissingValue() { - Assertions.assertNull(converter.convertToJavaData(new ReadCellData<>(""), null, configuration)); + @ParameterizedTest + @NullSource + @ValueSource(strings = {"", " ", "\t\r\n", "\u2003"}) + void readsBlankStringAsMissingValue(String input) { + ReadCellData cell = new ReadCellData<>(); + cell.setStringValue(input); + Assertions.assertNull(converter.convertToJavaData(cell, null, configuration)); + } + + @ParameterizedTest + @ValueSource(strings = {" " + TEXT, TEXT + " ", "\t" + TEXT + "\r\n"}) + void trimsWhitespaceBeforeParsing(String input) { + Assertions.assertEquals( + UUID.fromString(TEXT), converter.convertToJavaData(new ReadCellData<>(input), null, configuration)); } @ParameterizedTest @@ -122,11 +141,19 @@ void readsUppercaseAndBlankCells(ExcelFormat format) throws Exception { StringData second = new StringData(); second.setId(""); second.setLabel("blank UUID"); - RoundTripHelper.write(file, StringData.class, Arrays.asList(first, second)); + StringData padded = new StringData(); + padded.setId(" \t" + TEXT + "\r\n"); + padded.setLabel("padded UUID"); + StringData whitespace = new StringData(); + whitespace.setId(" \t "); + whitespace.setLabel("whitespace UUID"); + RoundTripHelper.write(file, StringData.class, Arrays.asList(first, second, padded, whitespace)); List rows = RoundTripHelper.read(file, UuidData.class); - Assertions.assertEquals(2, rows.size()); + Assertions.assertEquals(4, rows.size()); Assertions.assertEquals(UUID.fromString(TEXT), rows.get(0).getId()); Assertions.assertNull(rows.get(1).getId()); + Assertions.assertEquals(UUID.fromString(TEXT), rows.get(2).getId()); + Assertions.assertNull(rows.get(3).getId()); } @ParameterizedTest From a9f42625df0cba92d2def07df36ad6dd462576fe Mon Sep 17 00:00:00 2001 From: zhanghuang090 <78581375+zhanghuang090@users.noreply.github.com> Date: Sun, 20 Sep 2026 17:38:24 +0800 Subject: [PATCH 4/4] test: separate UUID unit and round-trip coverage --- .../converter/UUIDConverterRoundTripTest.java | 100 ++++++++++ .../sheet/converter/UUIDConverterTest.java | 95 +++++++++ .../sheet/converter/UuidConverterTest.java | 183 ------------------ .../DefaultConverterLoaderTest.java | 17 ++ 4 files changed, 212 insertions(+), 183 deletions(-) create mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UUIDConverterRoundTripTest.java create mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UUIDConverterTest.java delete mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UUIDConverterRoundTripTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UUIDConverterRoundTripTest.java new file mode 100644 index 000000000..6a0c48881 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UUIDConverterRoundTripTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.fesod.sheet.converter; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.UUID; +import lombok.Data; +import org.apache.fesod.sheet.exception.ExcelDataConvertException; +import org.apache.fesod.sheet.testkit.Tags; +import org.apache.fesod.sheet.testkit.base.AbstractExcelTest; +import org.apache.fesod.sheet.testkit.enums.ExcelFormat; +import org.apache.fesod.sheet.testkit.helpers.RoundTripHelper; +import org.apache.fesod.sheet.testkit.params.ExcelFormatSource; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; + +@Tag(Tags.ROUND_TRIP) +class UUIDConverterRoundTripTest extends AbstractExcelTest { + + private static final String TEXT = "123e4567-e89b-12d3-a456-426614174000"; + + @ParameterizedTest + @ExcelFormatSource + void roundTripsUuidAndNullFieldsWithoutCustomRegistration(ExcelFormat format) throws Exception { + File file = createTempFile(format); + UuidData first = new UuidData(); + first.setId(UUID.fromString(TEXT)); + first.setLabel("populated"); + UuidData second = new UuidData(); + second.setLabel("null UUID"); + List expected = Arrays.asList(first, second); + Assertions.assertEquals(expected, RoundTripHelper.writeAndRead(file, UuidData.class, expected)); + List strings = RoundTripHelper.read(file, StringData.class); + Assertions.assertEquals(TEXT, strings.get(0).getId()); + } + + @ParameterizedTest + @ExcelFormatSource + void readsUppercaseAndBlankCells(ExcelFormat format) throws Exception { + File file = createTempFile(format); + StringData first = new StringData(); + first.setId(TEXT.toUpperCase(Locale.ROOT)); + first.setLabel("uppercase"); + StringData second = new StringData(); + second.setId(""); + second.setLabel("blank UUID"); + RoundTripHelper.write(file, StringData.class, Arrays.asList(first, second)); + List rows = RoundTripHelper.read(file, UuidData.class); + Assertions.assertEquals(2, rows.size()); + Assertions.assertEquals(UUID.fromString(TEXT), rows.get(0).getId()); + Assertions.assertNull(rows.get(1).getId()); + } + + @ParameterizedTest + @ExcelFormatSource + void reportsInvalidCellAsConversionFailure(ExcelFormat format) throws Exception { + File file = createTempFile(format); + StringData row = new StringData(); + row.setId("not-a-uuid"); + row.setLabel("invalid"); + RoundTripHelper.write(file, StringData.class, Collections.singletonList(row)); + ExcelDataConvertException error = Assertions.assertThrows( + ExcelDataConvertException.class, () -> RoundTripHelper.read(file, UuidData.class)); + Assertions.assertInstanceOf(IllegalArgumentException.class, error.getCause()); + } + + @Data + public static class UuidData { + private UUID id; + private String label; + } + + @Data + public static class StringData { + private String id; + private String label; + } +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UUIDConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UUIDConverterTest.java new file mode 100644 index 000000000..71fbb1558 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UUIDConverterTest.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.fesod.sheet.converter; + +import java.util.Locale; +import java.util.UUID; +import org.apache.fesod.sheet.converters.uuid.UUIDStringConverter; +import org.apache.fesod.sheet.enums.CellDataTypeEnum; +import org.apache.fesod.sheet.metadata.GlobalConfiguration; +import org.apache.fesod.sheet.metadata.data.ReadCellData; +import org.apache.fesod.sheet.metadata.data.WriteCellData; +import org.apache.fesod.sheet.testkit.Tags; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +@Tag(Tags.UNIT) +class UUIDConverterTest { + + private static final String TEXT = "123e4567-e89b-12d3-a456-426614174000"; + private final UUIDStringConverter converter = new UUIDStringConverter(); + private final GlobalConfiguration configuration = new GlobalConfiguration(); + + @Test + void supportsUuidStrings() { + Assertions.assertEquals(UUID.class, converter.supportJavaTypeKey()); + Assertions.assertEquals(CellDataTypeEnum.STRING, converter.supportExcelTypeKey()); + } + + @ParameterizedTest + @ValueSource( + strings = { + TEXT, + "123E4567-E89B-12D3-A456-426614174000", + "00000000-0000-0000-0000-000000000000", + "ffffffff-ffff-ffff-ffff-ffffffffffff" + }) + void readsAndWritesCanonicalStrings(String input) { + UUID value = converter.convertToJavaData(new ReadCellData<>(input), null, configuration); + WriteCellData cell = converter.convertToExcelData(value, null, configuration); + Assertions.assertEquals(CellDataTypeEnum.STRING, cell.getType()); + Assertions.assertEquals(input.toLowerCase(Locale.ROOT), cell.getStringValue()); + } + + @ParameterizedTest + @ValueSource( + strings = { + "not-a-uuid", + "123e4567-e89b-12d3-a456-42661417400g", + "123e4567e89b12d3a456426614174000", + " not-a-uuid\t", + "123e4567-e89b-12d3-a456-42661417 4000" + }) + void rejectsInvalidInput(String input) { + Assertions.assertThrows( + IllegalArgumentException.class, + () -> converter.convertToJavaData(new ReadCellData<>(input), null, configuration)); + } + + @ParameterizedTest + @NullSource + @ValueSource(strings = {"", " ", "\t\r\n", "\u2003"}) + void readsBlankStringAsMissingValue(String input) { + ReadCellData cell = new ReadCellData<>(); + cell.setStringValue(input); + Assertions.assertNull(converter.convertToJavaData(cell, null, configuration)); + } + + @ParameterizedTest + @ValueSource(strings = {" " + TEXT, TEXT + " ", "\t" + TEXT + "\r\n"}) + void trimsWhitespaceBeforeParsing(String input) { + Assertions.assertEquals( + UUID.fromString(TEXT), converter.convertToJavaData(new ReadCellData<>(input), null, configuration)); + } +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java deleted file mode 100644 index 53319073d..000000000 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/UuidConverterTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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.fesod.sheet.converter; - -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.UUID; -import lombok.Data; -import org.apache.fesod.sheet.converters.ConverterKeyBuild; -import org.apache.fesod.sheet.converters.DefaultConverterLoader; -import org.apache.fesod.sheet.converters.uuid.UUIDStringConverter; -import org.apache.fesod.sheet.enums.CellDataTypeEnum; -import org.apache.fesod.sheet.exception.ExcelDataConvertException; -import org.apache.fesod.sheet.metadata.GlobalConfiguration; -import org.apache.fesod.sheet.metadata.data.ReadCellData; -import org.apache.fesod.sheet.metadata.data.WriteCellData; -import org.apache.fesod.sheet.testkit.Tags; -import org.apache.fesod.sheet.testkit.base.AbstractExcelTest; -import org.apache.fesod.sheet.testkit.enums.ExcelFormat; -import org.apache.fesod.sheet.testkit.helpers.RoundTripHelper; -import org.apache.fesod.sheet.testkit.params.ExcelFormatSource; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullSource; -import org.junit.jupiter.params.provider.ValueSource; - -@Tag(Tags.ROUND_TRIP) -class UuidConverterTest extends AbstractExcelTest { - - private static final String TEXT = "123e4567-e89b-12d3-a456-426614174000"; - private final UUIDStringConverter converter = new UUIDStringConverter(); - private final GlobalConfiguration configuration = new GlobalConfiguration(); - - @Test - void supportsUuidStringsAndRegistersAllLookupPaths() { - Assertions.assertEquals(UUID.class, converter.supportJavaTypeKey()); - Assertions.assertEquals(CellDataTypeEnum.STRING, converter.supportExcelTypeKey()); - Assertions.assertInstanceOf( - UUIDStringConverter.class, - DefaultConverterLoader.loadDefaultReadConverter() - .get(ConverterKeyBuild.buildKey(UUID.class, CellDataTypeEnum.STRING))); - Assertions.assertInstanceOf( - UUIDStringConverter.class, - DefaultConverterLoader.loadDefaultWriteConverter().get(ConverterKeyBuild.buildKey(UUID.class))); - Assertions.assertInstanceOf( - UUIDStringConverter.class, - DefaultConverterLoader.loadDefaultWriteConverter() - .get(ConverterKeyBuild.buildKey(UUID.class, CellDataTypeEnum.STRING))); - } - - @ParameterizedTest - @ValueSource( - strings = { - TEXT, - "123E4567-E89B-12D3-A456-426614174000", - "00000000-0000-0000-0000-000000000000", - "ffffffff-ffff-ffff-ffff-ffffffffffff" - }) - void readsAndWritesCanonicalStrings(String input) { - UUID value = converter.convertToJavaData(new ReadCellData<>(input), null, configuration); - WriteCellData cell = converter.convertToExcelData(value, null, configuration); - Assertions.assertEquals(CellDataTypeEnum.STRING, cell.getType()); - Assertions.assertEquals(input.toLowerCase(Locale.ROOT), cell.getStringValue()); - } - - @ParameterizedTest - @ValueSource( - strings = { - "not-a-uuid", - "123e4567-e89b-12d3-a456-42661417400g", - "123e4567e89b12d3a456426614174000", - " not-a-uuid\t", - "123e4567-e89b-12d3-a456-42661417 4000" - }) - void rejectsInvalidInput(String input) { - Assertions.assertThrows( - IllegalArgumentException.class, - () -> converter.convertToJavaData(new ReadCellData<>(input), null, configuration)); - } - - @ParameterizedTest - @NullSource - @ValueSource(strings = {"", " ", "\t\r\n", "\u2003"}) - void readsBlankStringAsMissingValue(String input) { - ReadCellData cell = new ReadCellData<>(); - cell.setStringValue(input); - Assertions.assertNull(converter.convertToJavaData(cell, null, configuration)); - } - - @ParameterizedTest - @ValueSource(strings = {" " + TEXT, TEXT + " ", "\t" + TEXT + "\r\n"}) - void trimsWhitespaceBeforeParsing(String input) { - Assertions.assertEquals( - UUID.fromString(TEXT), converter.convertToJavaData(new ReadCellData<>(input), null, configuration)); - } - - @ParameterizedTest - @ExcelFormatSource - void roundTripsUuidAndNullFieldsWithoutCustomRegistration(ExcelFormat format) throws Exception { - File file = createTempFile(format); - UuidData first = new UuidData(); - first.setId(UUID.fromString(TEXT)); - first.setLabel("populated"); - UuidData second = new UuidData(); - second.setLabel("null UUID"); - List expected = Arrays.asList(first, second); - Assertions.assertEquals(expected, RoundTripHelper.writeAndRead(file, UuidData.class, expected)); - List strings = RoundTripHelper.read(file, StringData.class); - Assertions.assertEquals(TEXT, strings.get(0).getId()); - } - - @ParameterizedTest - @ExcelFormatSource - void readsUppercaseAndBlankCells(ExcelFormat format) throws Exception { - File file = createTempFile(format); - StringData first = new StringData(); - first.setId(TEXT.toUpperCase(Locale.ROOT)); - first.setLabel("uppercase"); - StringData second = new StringData(); - second.setId(""); - second.setLabel("blank UUID"); - StringData padded = new StringData(); - padded.setId(" \t" + TEXT + "\r\n"); - padded.setLabel("padded UUID"); - StringData whitespace = new StringData(); - whitespace.setId(" \t "); - whitespace.setLabel("whitespace UUID"); - RoundTripHelper.write(file, StringData.class, Arrays.asList(first, second, padded, whitespace)); - List rows = RoundTripHelper.read(file, UuidData.class); - Assertions.assertEquals(4, rows.size()); - Assertions.assertEquals(UUID.fromString(TEXT), rows.get(0).getId()); - Assertions.assertNull(rows.get(1).getId()); - Assertions.assertEquals(UUID.fromString(TEXT), rows.get(2).getId()); - Assertions.assertNull(rows.get(3).getId()); - } - - @ParameterizedTest - @ExcelFormatSource - void reportsInvalidCellAsConversionFailure(ExcelFormat format) throws Exception { - File file = createTempFile(format); - StringData row = new StringData(); - row.setId("not-a-uuid"); - row.setLabel("invalid"); - RoundTripHelper.write(file, StringData.class, Collections.singletonList(row)); - ExcelDataConvertException error = Assertions.assertThrows( - ExcelDataConvertException.class, () -> RoundTripHelper.read(file, UuidData.class)); - Assertions.assertInstanceOf(IllegalArgumentException.class, error.getCause()); - } - - @Data - public static class UuidData { - private UUID id; - private String label; - } - - @Data - public static class StringData { - private String id; - private String label; - } -} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/DefaultConverterLoaderTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/DefaultConverterLoaderTest.java index a30708a52..4b7278fb5 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/DefaultConverterLoaderTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/DefaultConverterLoaderTest.java @@ -21,10 +21,12 @@ import java.time.LocalTime; import java.util.Map; +import java.util.UUID; import org.apache.fesod.sheet.converters.ConverterKeyBuild.ConverterKey; import org.apache.fesod.sheet.converters.localtime.LocalTimeDateConverter; import org.apache.fesod.sheet.converters.localtime.LocalTimeNumberConverter; import org.apache.fesod.sheet.converters.localtime.LocalTimeStringConverter; +import org.apache.fesod.sheet.converters.uuid.UUIDStringConverter; import org.apache.fesod.sheet.enums.CellDataTypeEnum; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -67,6 +69,21 @@ void loadConvertersRegistersLocalTimeFamily() { writeConverter.get(ConverterKeyBuild.buildKey(LocalTime.class, CellDataTypeEnum.STRING))); } + @Test + void loadConvertersRegistersUuidFamily() { + Assertions.assertInstanceOf( + UUIDStringConverter.class, + DefaultConverterLoader.loadDefaultReadConverter() + .get(ConverterKeyBuild.buildKey(UUID.class, CellDataTypeEnum.STRING))); + Assertions.assertInstanceOf( + UUIDStringConverter.class, + DefaultConverterLoader.loadDefaultWriteConverter().get(ConverterKeyBuild.buildKey(UUID.class))); + Assertions.assertInstanceOf( + UUIDStringConverter.class, + DefaultConverterLoader.loadDefaultWriteConverter() + .get(ConverterKeyBuild.buildKey(UUID.class, CellDataTypeEnum.STRING))); + } + private static void assertLoadIsImmutableAndCopyIsMutable( Map> loaded, Map> copy) { Map.Entry> entry =