Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DASHBUILDE-172: Fail fast on invalid CSV data #276

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -73,7 +73,12 @@ protected DataSet load() throws Exception {
if (header == null) throw new IOException("CSV has no header: " + dataSetDef);

String[] firstRow = csvReader.readNext();
if (firstRow == null || firstRow.length < header.length) firstRow = null;
if (firstRow != null && firstRow.length < header.length) {
String exceptionMessage = String.format("CSV parse error : The first row has fewer columns (%d) than the header (%d)"
, firstRow.length, header.length);
// Fail fast - see DASHBUILDE-172
throw new IllegalArgumentException(exceptionMessage);
}

// Build the data set structure
List<Integer> _columnIdxs = new ArrayList<Integer>();
Expand Down
@@ -0,0 +1,88 @@
package org.dashbuilder.dataprovider.csv;


import org.dashbuilder.dataset.ColumnType;
import org.dashbuilder.dataset.DataSet;
import org.dashbuilder.dataset.def.CSVDataSetDef;
import org.dashbuilder.dataset.def.DataSetDefFactory;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import static org.junit.Assert.assertEquals;

public class CSVParserTest {

//Dummy DataSetDef just to get separator/quote/escape char definition for the parser
private final CSVDataSetDef csvDataSet = (CSVDataSetDef) DataSetDefFactory.newCSVDataSetDef()
.separatorChar(',')
.quoteChar('\'')
.escapeChar('\\')
.datePattern("YYYY-MM-dd")
.numberPattern("###.##")
.buildDef();


/* Reproducer of DASHBUILDE-172 */
@Test
public void exceptionThrown_whenFirstLineHasLessFieldsThanHeader() throws Exception {
//Header with 2 fields, 1st row missing comma -> has only 1 field
final String CSV_DATA = "'Name','Age'\n'Jan''15'";

CSVFileStorage mockStorage = new MockCSVFileStorage(CSV_DATA);
CSVParser testedParser = new CSVParser(csvDataSet, mockStorage);
try {
DataSet dataSetShouldNotLoad = testedParser.load();
Assert.fail("IllegalArgumentException should be thrown when 1st row of CSV data has less fields than the header");
} catch (IllegalArgumentException iae) {
String expectedExceptionMessage = "CSV parse error : The first row has fewer columns (1) than the header (2)";
assertEquals(expectedExceptionMessage, iae.getMessage());
}
}

@Test
public void dataSetCreated_whenCsvDataValid() throws Exception {
final String CSV_DATA = "'Name','Weight','Date of birth'\n'Jan','75.64','1950-01-20'";

CSVFileStorage mockStorage = new MockCSVFileStorage(CSV_DATA);
CSVParser testedParser = new CSVParser(csvDataSet, mockStorage);
DataSet dset = testedParser.load();

assertEquals(1, dset.getRowCount());
assertEquals(ColumnType.LABEL, dset.getColumnById("Name").getColumnType());
assertEquals(ColumnType.NUMBER, dset.getColumnById("Weight").getColumnType());
assertEquals(ColumnType.DATE, dset.getColumnById("Date of birth").getColumnType());
}


static class MockCSVFileStorage implements CSVFileStorage {

private final String csvData;

MockCSVFileStorage(String csvData) {
this.csvData = csvData;
}

@Override
public InputStream getCSVInputStream(CSVDataSetDef ignored) {
return new ByteArrayInputStream(csvData.getBytes(StandardCharsets.UTF_8));
}

@Override
public String getCSVString(CSVDataSetDef def) {
return null;
}

@Override
public void saveCSVFile(CSVDataSetDef def) {
}

@Override
public void deleteCSVFile(CSVDataSetDef def) {
}
}
}