You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2020/01/21 13:00:07 UTC

[commons-csv] 01/04: [CSV-248] Test the parser and map functionality after deserialization

This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-csv.git

commit 8d6772a3209b124af7e2430f69cdf6a7c765fd44
Author: aherbert <ah...@apache.org>
AuthorDate: Tue Jan 21 12:22:48 2020 +0000

    [CSV-248] Test the parser and map functionality after deserialization
    
    Methods with unexpected return values (null or exceptions) have been
    documented. All other methods will just fail as if the record came from
    a parser without a header.
---
 .../java/org/apache/commons/csv/CSVRecord.java     | 13 +++++-
 .../java/org/apache/commons/csv/CSVRecordTest.java | 50 +++++++++++++++++++++-
 2 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java
index 471e94d..e32cd5a 100644
--- a/src/main/java/org/apache/commons/csv/CSVRecord.java
+++ b/src/main/java/org/apache/commons/csv/CSVRecord.java
@@ -83,6 +83,12 @@ public final class CSVRecord implements Serializable, Iterable<String> {
     /**
      * Returns a value by name.
      *
+     * <p>Note: This requires a field mapping obtained from the original parser.
+     * A check using {@link #isMapped(String)} should be used to determine if a
+     * mapping exists from the provide {@code name} to a field index. In this case an
+     * exception will only be thrown if the record does not contain a field corresponding
+     * to the mapping, that is the record length is not consistent with the mapping size.
+     *
      * @param name
      *            the name of the column to be retrieved.
      * @return the column value, maybe null depending on {@link CSVFormat#getNullString()}.
@@ -90,7 +96,9 @@ public final class CSVRecord implements Serializable, Iterable<String> {
      *             if no header mapping was provided
      * @throws IllegalArgumentException
      *             if {@code name} is not mapped or if the record is inconsistent
+     * @see #isMapped(String)
      * @see #isConsistent()
+     * @see #getParser()
      * @see CSVFormat#withNullString(String)
      */
     public String get(final String name) {
@@ -136,12 +144,15 @@ public final class CSVRecord implements Serializable, Iterable<String> {
     }
 
     private Map<String, Integer> getHeaderMapRaw() {
-        return parser.getHeaderMapRaw();
+        return parser == null ? null : parser.getHeaderMapRaw();
     }
 
     /**
      * Returns the parser.
      *
+     * <p>Note: The parser is not part of the serialized state of the record. A null check
+     * should be used when the record may have originated from a serialized form. 
+     *
      * @return the parser.
      * @since 1.7
      */
diff --git a/src/test/java/org/apache/commons/csv/CSVRecordTest.java b/src/test/java/org/apache/commons/csv/CSVRecordTest.java
index 476bac2..8d11d53 100644
--- a/src/test/java/org/apache/commons/csv/CSVRecordTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVRecordTest.java
@@ -23,12 +23,15 @@ 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.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
@@ -192,15 +195,58 @@ public class CSVRecordTest {
     }
 
     @Test
-    public void testSerialization() throws IOException {
+    public void testSerialization() throws IOException, ClassNotFoundException {
         CSVRecord shortRec;
-        try (final CSVParser parser = CSVParser.parse("a,b", CSVFormat.newFormat(','))) {
+        try (final CSVParser parser = CSVParser.parse("A,B\n#my comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
             shortRec = parser.iterator().next();
         }
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
             oos.writeObject(shortRec);
         }
+        final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+        try (ObjectInputStream ois = new ObjectInputStream(in)) {
+            final Object object = ois.readObject();
+            assertTrue(object instanceof CSVRecord);
+            final CSVRecord rec = (CSVRecord) object;
+            assertEquals(1L, rec.getRecordNumber());
+            assertEquals("One", rec.get(0));
+            assertEquals("Two", rec.get(1));
+            assertEquals(2, rec.size());
+            assertEquals(shortRec.getCharacterPosition(), rec.getCharacterPosition());
+            assertEquals("my comment", rec.getComment());
+            // The parser is not serialized
+            assertNull(rec.getParser());
+            // Check all header map functionality is absent
+            assertTrue(rec.isConsistent());
+            assertFalse(rec.isMapped("A"));
+            assertFalse(rec.isSet("A"));
+            assertEquals(0, rec.toMap().size());
+            // This will throw
+            try {
+                rec.get("A");
+                org.junit.jupiter.api.Assertions.fail("Access by name is not expected after deserialisation");
+            } catch (IllegalStateException expected) {
+                // OK
+            }
+        }
+    }
+
+    /**
+     * Test deserialisation of a record created using version 1.6.
+     *
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    @Test
+    public void testDeserialisation() throws IOException {
+        CSVRecord shortRec;
+        try (final CSVParser parser = CSVParser.parse("A,B\n#my comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
+            shortRec = parser.iterator().next();
+        }
+        try (FileOutputStream out = new FileOutputStream("/tmp/csvRecord.ser");
+            ObjectOutputStream oos = new ObjectOutputStream(out)) {
+            oos.writeObject(shortRec);
+        }
     }
 
     @Test


Re: [commons-csv] 01/04: [CSV-248] Test the parser and map functionality after deserialization

Posted by Alex Herbert <al...@gmail.com>.
On 21/01/2020 15:35, Gary Gregory wrote:
>>
>>
>> diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java
>> b/src/main/java/org/apache/commons/csv/CSVRecord.java
>> index 471e94d..e32cd5a 100644
>> --- a/src/main/java/org/apache/commons/csv/CSVRecord.java
>> +++ b/src/main/java/org/apache/commons/csv/CSVRecord.java
>> @@ -83,6 +83,12 @@ public final class CSVRecord implements Serializable,
>> Iterable<String> {
>>       /**
>>        * Returns a value by name.
>>        *
>> +     * <p>Note: This requires a field mapping obtained from the original
>> parser.
>> +     * A check using {@link #isMapped(String)} should be used to
>> determine if a
>> +     * mapping exists from the provide {@code name} to a field index. In
>> this case an
>> +     * exception will only be thrown if the record does not contain a
>> field corresponding
>> +     * to the mapping, that is the record length is not consistent with
>> the mapping size.
>> +     *
>>        * @param name
>>
>>
> Please close all HTML tags.

Done in a later commit.

On 21/01/2020 15:37, Gary Gregory wrote:
>> +    @Test
>> +    public void testDeserialisation() throws IOException {
>> +        CSVRecord shortRec;
>> +        try (final CSVParser parser = CSVParser.parse("A,B\n#my
>> comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
>> +            shortRec = parser.iterator().next();
>> +        }
>> +        try (FileOutputStream out = new
>> FileOutputStream("/tmp/csvRecord.ser");
>> +            ObjectOutputStream oos = new ObjectOutputStream(out)) {
>> +            oos.writeObject(shortRec);
>> +        }
>>       }
>>
>   This can't be right: "/tmp/csvRecord.ser"

It wasn't meant to be there. It is how to create the serialisation 
binary file. I wrote it in version 1.8 then took the same code to 1.6 
and forgot to remove it from 1.8.

A later one removed this. When I noticed it in the commit summary I had 
already pushed and it was too late to rebase and fix up the commit history.

>
> @@ -77,4 +77,4 @@ public class JiraCsv248Test {
>       private static InputStream getTestInput() {
>           return
> ClassLoader.getSystemClassLoader().getResourceAsStream("CSV-248/csvRecord.bin");
>       }
> -}
> \ No newline at end of file
> +}
This has been fixed.

2) Should we have one file and test per version 1.7 back to 1.0?

You cannot test version 1.7 as that could not serialize the CSVRecord.

I tried 1.6 as that was the most recent. I suppose should have used 1.0. Any users wanting to use an intermediate release and find it to be broken for serialisation can upgrade to 1.8 where it should be fixed.

I've just generated the data using the code under tag CSV_1.0 which requires pre-1.7 code (not try with resources):

@Test
public void testDeserialisation() throws IOException {
     CSVRecord shortRec;
     CSVParser parser = CSVParser.parse("A,B\n#my comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'));
     shortRec = parser.iterator().next();
     parser.close();
     FileOutputStream out = new FileOutputStream("/tmp/csvRecord.ser");
     ObjectOutputStream oos = new ObjectOutputStream(out);
     oos.writeObject(shortRec);
     oos.close();
     out.close();
  }

The file is binary different from the current file. The test passes. Shall I commit the old serialised version?

Alex



Re: [commons-csv] 01/04: [CSV-248] Test the parser and map functionality after deserialization

Posted by Gary Gregory <ga...@gmail.com>.
>
>
>
> diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java
> b/src/main/java/org/apache/commons/csv/CSVRecord.java
> index 471e94d..e32cd5a 100644
> --- a/src/main/java/org/apache/commons/csv/CSVRecord.java
> +++ b/src/main/java/org/apache/commons/csv/CSVRecord.java
> @@ -83,6 +83,12 @@ public final class CSVRecord implements Serializable,
> Iterable<String> {
>      /**
>       * Returns a value by name.
>       *
> +     * <p>Note: This requires a field mapping obtained from the original
> parser.
> +     * A check using {@link #isMapped(String)} should be used to
> determine if a
> +     * mapping exists from the provide {@code name} to a field index. In
> this case an
> +     * exception will only be thrown if the record does not contain a
> field corresponding
> +     * to the mapping, that is the record length is not consistent with
> the mapping size.
> +     *
>       * @param name
>
>
Please close all HTML tags.

Gary

Re: [commons-csv] 01/04: [CSV-248] Test the parser and map functionality after deserialization

Posted by Gary Gregory <ga...@gmail.com>.
>
>
> +    @Test
> +    public void testDeserialisation() throws IOException {
> +        CSVRecord shortRec;
> +        try (final CSVParser parser = CSVParser.parse("A,B\n#my
> comment\nOne,Two", CSVFormat.DEFAULT.withHeader().withCommentMarker('#'))) {
> +            shortRec = parser.iterator().next();
> +        }
> +        try (FileOutputStream out = new
> FileOutputStream("/tmp/csvRecord.ser");
> +            ObjectOutputStream oos = new ObjectOutputStream(out)) {
> +            oos.writeObject(shortRec);
> +        }
>      }
>

 This can't be right: "/tmp/csvRecord.ser"

Gary