|
6 | 6 | #include <cudf_test/base_fixture.hpp> |
7 | 7 | #include <cudf_test/column_utilities.hpp> |
8 | 8 | #include <cudf_test/column_wrapper.hpp> |
| 9 | +#include <cudf_test/cudf_gtest.hpp> |
9 | 10 | #include <cudf_test/default_stream.hpp> |
10 | 11 | #include <cudf_test/iterator_utilities.hpp> |
11 | 12 | #include <cudf_test/memory_resource_utilities.hpp> |
@@ -1441,6 +1442,170 @@ TEST_F(CsvReaderTest, BlanksAndComments) |
1441 | 1442 | expect_column_data_equal(std::vector<int32_t>{1, 3, 4, 5, 8, 9}, view.column(0)); |
1442 | 1443 | } |
1443 | 1444 |
|
| 1445 | +struct CsvShortRecordTest : public CsvReaderTest, |
| 1446 | + public testing::WithParamInterface<std::tuple<std::string, bool>> { |
| 1447 | + auto read(std::string const& buffer) |
| 1448 | + { |
| 1449 | + auto const& [separator, infer_types] = GetParam(); |
| 1450 | + auto builder = cudf::io::csv_reader_options::builder( |
| 1451 | + cudf::io::source_info{cudf::host_span<std::byte const>{ |
| 1452 | + reinterpret_cast<std::byte const*>(buffer.data()), buffer.size()}}) |
| 1453 | + .header(0) |
| 1454 | + .comment('#') |
| 1455 | + .lineterminator(separator.back()) |
| 1456 | + .keep_default_na(false); |
| 1457 | + if (!infer_types) { builder.dtypes(std::vector<data_type>(3, dtype<cudf::string_view>())); } |
| 1458 | + return cudf::io::read_csv(builder.build()); |
| 1459 | + } |
| 1460 | + |
| 1461 | + void expect_short_rows(std::string const& skipped) |
| 1462 | + { |
| 1463 | + auto const& [separator, infer_types] = GetParam(); |
| 1464 | + std::string const buffer = "a,b,c" + separator + "1" + separator + skipped + "2,3,4" + |
| 1465 | + separator + "5" + separator + skipped + "6"; |
| 1466 | + auto const result = read(buffer); |
| 1467 | + if (infer_types) { |
| 1468 | + cudf::test::fixed_width_column_wrapper<int64_t> const a{1, 2, 5, 6}; |
| 1469 | + cudf::test::fixed_width_column_wrapper<int64_t> const b{{0, 3, 0, 0}, |
| 1470 | + {false, true, false, false}}; |
| 1471 | + cudf::test::fixed_width_column_wrapper<int64_t> const c{{0, 4, 0, 0}, |
| 1472 | + {false, true, false, false}}; |
| 1473 | + CUDF_TEST_EXPECT_TABLES_EQUIVALENT(table_view{{a, b, c}}, result.tbl->view()); |
| 1474 | + } else { |
| 1475 | + cudf::test::strings_column_wrapper const a{"1", "2", "5", "6"}; |
| 1476 | + cudf::test::strings_column_wrapper const b{{"", "3", "", ""}, {false, true, false, false}}; |
| 1477 | + cudf::test::strings_column_wrapper const c{{"", "4", "", ""}, {false, true, false, false}}; |
| 1478 | + CUDF_TEST_EXPECT_TABLES_EQUIVALENT(table_view{{a, b, c}}, result.tbl->view()); |
| 1479 | + } |
| 1480 | + } |
| 1481 | +}; |
| 1482 | + |
| 1483 | +INSTANTIATE_TEST_SUITE_P( |
| 1484 | + CsvShortRecords, |
| 1485 | + CsvShortRecordTest, |
| 1486 | + testing::Combine(testing::Values(std::string{"\n"}, std::string{"\r\n"}, std::string{";"}), |
| 1487 | + testing::Bool()), |
| 1488 | + [](auto const& info) { |
| 1489 | + auto const& separator = std::get<0>(info.param); |
| 1490 | + return std::string{separator == "\n" ? "Lf" |
| 1491 | + : separator == "\r\n" ? "CrLf" |
| 1492 | + : "Custom"} + |
| 1493 | + (std::get<1>(info.param) ? "Inferred" : "Strings"); |
| 1494 | + }); |
| 1495 | + |
| 1496 | +TEST_P(CsvShortRecordTest, MissingFieldsBeforeComments) |
| 1497 | +{ |
| 1498 | + auto const& separator = std::get<0>(GetParam()); |
| 1499 | + expect_short_rows("#not,a,column" + separator + "#another,ignored,line" + separator); |
| 1500 | +} |
| 1501 | + |
| 1502 | +TEST_P(CsvShortRecordTest, MissingFieldsBeforeBlanks) |
| 1503 | +{ |
| 1504 | + auto const& separator = std::get<0>(GetParam()); |
| 1505 | + expect_short_rows(separator + separator); |
| 1506 | +} |
| 1507 | + |
| 1508 | +TEST_P(CsvShortRecordTest, QuotedTerminatorsAndComments) |
| 1509 | +{ |
| 1510 | + auto const& [separator, infer_types] = GetParam(); |
| 1511 | + auto const multiline = "line" + separator + "#é,inside"; |
| 1512 | + std::string const buffer = "a,b,c" + separator + "1,\"" + multiline + "\",7" + separator + |
| 1513 | + "#ignored,comment,99" + separator + "2,\"#é,quoted\"" + separator + |
| 1514 | + "#skipped,fields,123" + separator + "3,tail,8"; |
| 1515 | + auto const result = read(buffer); |
| 1516 | + cudf::test::strings_column_wrapper const b{multiline, "#é,quoted", "tail"}; |
| 1517 | + if (infer_types) { |
| 1518 | + cudf::test::fixed_width_column_wrapper<int64_t> const a{1, 2, 3}; |
| 1519 | + cudf::test::fixed_width_column_wrapper<int64_t> const c{{7, 0, 8}, {true, false, true}}; |
| 1520 | + CUDF_TEST_EXPECT_TABLES_EQUIVALENT(table_view{{a, b, c}}, result.tbl->view()); |
| 1521 | + } else { |
| 1522 | + cudf::test::strings_column_wrapper const a{"1", "2", "3"}; |
| 1523 | + cudf::test::strings_column_wrapper const c{{"7", "", "8"}, {true, false, true}}; |
| 1524 | + CUDF_TEST_EXPECT_TABLES_EQUIVALENT(table_view{{a, b, c}}, result.tbl->view()); |
| 1525 | + } |
| 1526 | +} |
| 1527 | + |
| 1528 | +TEST_F(CsvReaderTest, ShortRecordBeforeCommentDelimWhitespace) |
| 1529 | +{ |
| 1530 | + for (auto const& trailing_space : {std::string{""}, std::string{" "}, std::string{" "}}) { |
| 1531 | + for (bool infer_types : {false, true}) { |
| 1532 | + SCOPED_TRACE(trailing_space); |
| 1533 | + SCOPED_TRACE(infer_types); |
| 1534 | + std::string const buffer = "a b c\n1" + trailing_space + "\n#x y z\n2 3 4\n"; |
| 1535 | + auto builder = cudf::io::csv_reader_options::builder( |
| 1536 | + cudf::io::source_info{cudf::host_span<std::byte const>{ |
| 1537 | + reinterpret_cast<std::byte const*>(buffer.data()), buffer.size()}}) |
| 1538 | + .delim_whitespace(true) |
| 1539 | + .comment('#') |
| 1540 | + .keep_default_na(false); |
| 1541 | + if (!infer_types) { builder.dtypes(std::vector<data_type>(3, dtype<cudf::string_view>())); } |
| 1542 | + auto const result = cudf::io::read_csv(builder.build()); |
| 1543 | + |
| 1544 | + if (infer_types) { |
| 1545 | + cudf::test::fixed_width_column_wrapper<int64_t> const a{1, 2}; |
| 1546 | + cudf::test::fixed_width_column_wrapper<int64_t> const b{{0, 3}, {false, true}}; |
| 1547 | + cudf::test::fixed_width_column_wrapper<int64_t> const c{{0, 4}, {false, true}}; |
| 1548 | + CUDF_TEST_EXPECT_TABLES_EQUIVALENT(table_view{{a, b, c}}, result.tbl->view()); |
| 1549 | + } else { |
| 1550 | + cudf::test::strings_column_wrapper const a{"1", "2"}; |
| 1551 | + cudf::test::strings_column_wrapper const b({"", "3"}, {false, true}); |
| 1552 | + cudf::test::strings_column_wrapper const c({"", "4"}, {false, true}); |
| 1553 | + CUDF_TEST_EXPECT_TABLES_EQUIVALENT(table_view{{a, b, c}}, result.tbl->view()); |
| 1554 | + } |
| 1555 | + } |
| 1556 | + } |
| 1557 | +} |
| 1558 | + |
| 1559 | +TEST_F(CsvReaderTest, ShortRecordAllNullColumn) |
| 1560 | +{ |
| 1561 | + // Missing fields and explicit NA tokens must infer the same all-null type. |
| 1562 | + for (auto const& middle_row : {std::string{"2"}, std::string{"2,NA"}}) { |
| 1563 | + SCOPED_TRACE(middle_row); |
| 1564 | + std::string const buffer = "a,b\n1,NA\n" + middle_row + "\n#ignored\n3,NA\n"; |
| 1565 | + auto const result = |
| 1566 | + cudf::io::read_csv(cudf::io::csv_reader_options::builder( |
| 1567 | + cudf::io::source_info{cudf::host_span<std::byte const>{ |
| 1568 | + reinterpret_cast<std::byte const*>(buffer.data()), buffer.size()}}) |
| 1569 | + .comment('#')); |
| 1570 | + cudf::test::fixed_width_column_wrapper<int64_t> const a{1, 2, 3}; |
| 1571 | + cudf::test::fixed_width_column_wrapper<int8_t> const b{{0, 0, 0}, |
| 1572 | + cudf::test::iterators::all_nulls()}; |
| 1573 | + CUDF_TEST_EXPECT_TABLES_EQUIVALENT(table_view{{a, b}}, result.tbl->view()); |
| 1574 | + } |
| 1575 | +} |
| 1576 | + |
| 1577 | +TEST_F(CsvReaderTest, ShortRecordExplicitEmptyField) |
| 1578 | +{ |
| 1579 | + std::string const buffer = "a,b,c\n1,\n#ignored,comment,fields\n2,3,4\n"; |
| 1580 | + auto const result = |
| 1581 | + cudf::io::read_csv(cudf::io::csv_reader_options::builder( |
| 1582 | + cudf::io::source_info{cudf::host_span<std::byte const>{ |
| 1583 | + reinterpret_cast<std::byte const*>(buffer.data()), buffer.size()}}) |
| 1584 | + .comment('#') |
| 1585 | + .keep_default_na(false) |
| 1586 | + .dtypes(std::vector<data_type>(3, dtype<cudf::string_view>()))); |
| 1587 | + cudf::test::strings_column_wrapper const a{"1", "2"}; |
| 1588 | + cudf::test::strings_column_wrapper const b{"", "3"}; |
| 1589 | + cudf::test::strings_column_wrapper const c({"", "4"}, {false, true}); |
| 1590 | + CUDF_TEST_EXPECT_TABLES_EQUIVALENT(table_view{{a, b, c}}, result.tbl->view()); |
| 1591 | +} |
| 1592 | + |
| 1593 | +TEST_F(CsvReaderTest, ShortRecordBeforeCommentUseCols) |
| 1594 | +{ |
| 1595 | + std::string const buffer = "a,b,c\n1\n#x,y,z\n2,3,4\n"; |
| 1596 | + auto const in_opts = cudf::io::csv_reader_options::builder( |
| 1597 | + cudf::io::source_info{cudf::host_span<std::byte const>{ |
| 1598 | + reinterpret_cast<std::byte const*>(buffer.data()), buffer.size()}}) |
| 1599 | + .comment('#') |
| 1600 | + .use_cols_names({"a", "c"}) |
| 1601 | + .build(); |
| 1602 | + auto const result = cudf::io::read_csv(in_opts); |
| 1603 | + |
| 1604 | + cudf::test::fixed_width_column_wrapper<int64_t> const a{1, 2}; |
| 1605 | + cudf::test::fixed_width_column_wrapper<int64_t> const c{{0, 4}, {false, true}}; |
| 1606 | + CUDF_TEST_EXPECT_TABLES_EQUIVALENT(table_view{{a, c}}, result.tbl->view()); |
| 1607 | +} |
| 1608 | + |
1444 | 1609 | TEST_F(CsvReaderTest, EmptyFile) |
1445 | 1610 | { |
1446 | 1611 | auto filepath = temp_env->get_temp_dir() + "EmptyFile.csv"; |
|
0 commit comments