diff --git a/cpp/include/cudf/detail/gather.cuh b/cpp/include/cudf/detail/gather.cuh index 1dd0d472d0d..94433975744 100644 --- a/cpp/include/cudf/detail/gather.cuh +++ b/cpp/include/cudf/detail/gather.cuh @@ -470,9 +470,10 @@ struct column_gatherer_impl { mr); }); - auto const nullable = std::any_of(structs_column.child_begin(), - structs_column.child_end(), - [](auto const& col) { return col.nullable(); }); + auto const nullable = + nullify_out_of_bounds or std::any_of(structs_column.child_begin(), + structs_column.child_end(), + [](auto const& col) { return col.nullable(); }); if (nullable) { gather_bitmask( // Table view of struct column. diff --git a/cpp/tests/copying/gather_struct_tests.cpp b/cpp/tests/copying/gather_struct_tests.cpp index 96d72ee4e89..f6047cf14af 100644 --- a/cpp/tests/copying/gather_struct_tests.cpp +++ b/cpp/tests/copying/gather_struct_tests.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -33,26 +34,37 @@ #include -#include - #include using vector_of_columns = std::vector>; -using cudf::size_type; +using gather_map_t = std::vector; +using offsets = cudf::test::fixed_width_column_wrapper; +using structs = cudf::test::structs_column_wrapper; +using strings = cudf::test::strings_column_wrapper; +using bools = cudf::test::fixed_width_column_wrapper; -struct StructGatherTest : public cudf::test::BaseFixture { -}; +// Test validity iterator utilities. +using cudf::test::iterators::no_nulls; +using cudf::test::iterators::null_at; +using cudf::test::iterators::nulls_at; template -struct TypedStructGatherTest : public cudf::test::BaseFixture { +using numerics = cudf::test::fixed_width_column_wrapper; + +template +using lists = cudf::test::lists_column_wrapper; + +auto constexpr null_index = std::numeric_limits::max(); + +namespace cudf::test { +struct StructGatherTest : public BaseFixture { }; -using FixedWidthTypes = cudf::test::Concat; +template +struct TypedStructGatherTest : public BaseFixture { +}; -TYPED_TEST_CASE(TypedStructGatherTest, FixedWidthTypes); +TYPED_TEST_SUITE(TypedStructGatherTest, FixedWidthTypes); namespace { template @@ -76,384 +88,336 @@ struct column_wrapper_constructor { } }; -template +template auto get_expected_column(std::vector const& input_values, - std::vector const& input_validity, - std::vector const& struct_validity, + InputValidityIter input_validity, + StructValidityIter struct_validity, std::vector const& gather_map) { auto is_valid = // Validity predicate. [&input_values, &input_validity, &struct_validity, &gather_map](auto gather_index) { assert( - (gather_index >= 0 && gather_index < static_cast(gather_map.size())) || + (gather_index >= 0 && gather_index < static_cast(gather_map.size())) && "Gather-index out of range."); - auto i{gather_map[gather_index]}; // Index into input_values. + auto i = gather_map[gather_index]; // Index into input_values. - return (i >= 0 && i < static_cast(input_values.size())) && - (struct_validity.empty() || struct_validity[i]) && - (input_validity.empty() || input_validity[i]); + return (i >= 0 && i < static_cast(input_values.size())) && struct_validity[i] && + input_validity[i]; }; - auto expected_row_count{gather_map.size()}; - auto gather_iter = cudf::detail::make_counting_transform_iterator( + auto expected_row_count = gather_map.size(); + auto gather_iter = cudf::detail::make_counting_transform_iterator( 0, [is_valid, &input_values, &gather_map](auto i) { return is_valid(i) ? input_values[gather_map[i]] : SourceElementT{}; }); return column_wrapper_constructor()( - gather_iter, - gather_iter + expected_row_count, - cudf::detail::make_counting_transform_iterator(0, is_valid)) - .release(); + gather_iter, + gather_iter + expected_row_count, + cudf::detail::make_counting_transform_iterator(0, is_valid)); +} + +auto do_gather(column_view const& input, gather_map_t const& gather_map) +{ + auto result = gather(table_view{{input}}, + offsets(gather_map.begin(), gather_map.end()), + out_of_bounds_policy::NULLIFY); + return std::move(result->release()[0]); } } // namespace TYPED_TEST(TypedStructGatherTest, TestSimpleStructGather) { - using namespace cudf::test; - // Testing gather() on struct. // 1. String "names" column. auto const names = std::vector{"Vimes", "Carrot", "Angua", "Cheery", "Detritus", "Slant"}; - auto const names_validity = std::vector{1, 1, 1, 1, 1, 1}; - auto names_column = strings_column_wrapper{names.begin(), names.end(), names_validity.begin()}; + auto const names_validity = no_nulls(); // 2. Numeric "ages" column. auto const ages = std::vector{5, 10, 15, 20, 25, 30}; - auto const ages_validity = std::vector{1, 1, 1, 1, 0, 1}; - auto ages_column = - fixed_width_column_wrapper{ages.begin(), ages.end(), ages_validity.begin()}; + auto const ages_validity = null_at(4); // 3. Boolean "is_human" column. auto const is_human = {true, true, false, false, false, false}; - auto const is_human_validity = std::vector{1, 1, 1, 0, 1, 1}; - auto is_human_col = - fixed_width_column_wrapper{is_human.begin(), is_human.end(), is_human_validity.begin()}; + auto const is_human_validity = null_at(3); // Assemble struct column. - auto const struct_validity = std::vector{1, 1, 1, 1, 1, 0}; - auto struct_column = - structs_column_wrapper{{names_column, ages_column, is_human_col}, struct_validity.begin()} - .release(); + auto const struct_validity = null_at(5); + auto const struct_column = [&] { + auto names_member = ::strings(names.begin(), names.end(), names_validity); + auto ages_member = ::numerics(ages.begin(), ages.end(), ages_validity); + auto is_human_member = ::bools(is_human.begin(), is_human.end(), is_human_validity); + return structs{{names_member, ages_member, is_human_member}, struct_validity}; + }(); // Gather to new struct column. - auto const gather_map = std::vector{-1, 4, 3, 2, 1}; - auto const gather_map_col = - fixed_width_column_wrapper(gather_map.begin(), gather_map.end()).release(); - - auto const gathered_table = - cudf::gather(cudf::table_view{std::vector{struct_column->view()}}, - gather_map_col->view()); - - auto const gathered_struct_col = gathered_table->get_column(0); - auto const gathered_struct_col_view = cudf::structs_column_view{gathered_struct_col}; - - // Verify that the gathered struct's fields are as expected. - - auto expected_names_column = - get_expected_column(names, names_validity, struct_validity, gather_map); - expect_columns_equivalent(*expected_names_column, gathered_struct_col.child(0)); - - auto expected_ages_column = - get_expected_column(ages, ages_validity, struct_validity, gather_map); - expect_columns_equivalent(*expected_ages_column, gathered_struct_col.child(1)); - - auto expected_bool_column = - get_expected_column(std::vector(is_human.begin(), is_human.end()), - is_human_validity, - struct_validity, - gather_map); - expect_columns_equivalent(*expected_bool_column, gathered_struct_col.child(2)); - - std::vector> expected_columns; - expected_columns.push_back(std::move(expected_names_column)); - expected_columns.push_back(std::move(expected_ages_column)); - expected_columns.push_back(std::move(expected_bool_column)); - auto const expected_struct_column = - structs_column_wrapper{std::move(expected_columns), std::vector{0, 1, 1, 1, 1}}.release(); - - expect_columns_equivalent(*expected_struct_column, gathered_struct_col); + auto const gather_map = gather_map_t{null_index, 4, 3, 2, 1}; + + auto const output = do_gather(struct_column, gather_map); + + auto const expected_output = [&] { + auto names_member = + get_expected_column(names, names_validity, struct_validity, gather_map); + auto ages_member = + get_expected_column(ages, ages_validity, struct_validity, gather_map); + auto is_human_member = + get_expected_column(std::vector(is_human.begin(), is_human.end()), + is_human_validity, + struct_validity, + gather_map); + return structs{{names_member, ages_member, is_human_member}, null_at(0)}; + }(); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(output->view(), expected_output); } -TYPED_TEST(TypedStructGatherTest, TestGatherStructOfLists) +TYPED_TEST(TypedStructGatherTest, TestNullifyOnNonNullInput) { - using namespace cudf::test; + // Test that the null masks of the struct output (and its children) are set correctly, + // for an input struct column whose members are not nullable. + // 1. String "names" column. + auto const names = + std::vector{"Vimes", "Carrot", "Angua", "Cheery", "Detritus", "Slant"}; + + // 2. Numeric "ages" column. + auto const ages = std::vector{5, 10, 15, 20, 25, 30}; + + // 3. Boolean "is_human" column. + auto const is_human = {true, true, false, false, false, false}; + + // Assemble struct column. + auto const struct_column = [&] { + auto names_member = ::strings(names.begin(), names.end()); + auto ages_member = ::numerics(ages.begin(), ages.end()); + auto is_human_member = ::bools(is_human.begin(), is_human.end()); + return structs({names_member, ages_member, is_human_member}); + }(); + + // Gather to new struct column. + auto const gather_map = gather_map_t{null_index, 4, 3, 2, 1}; + + auto const output = do_gather(struct_column, gather_map); + + auto const expected_output = [&] { + auto names_member = get_expected_column(names, no_nulls(), no_nulls(), gather_map); + auto ages_member = + get_expected_column(ages, no_nulls(), no_nulls(), gather_map); + auto is_human_member = get_expected_column( + std::vector(is_human.begin(), is_human.end()), no_nulls(), no_nulls(), gather_map); + return structs_column_wrapper{{names_member, ages_member, is_human_member}, null_at(0)}; + }(); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(output->view(), expected_output); +} + +TYPED_TEST(TypedStructGatherTest, TestGatherStructOfLists) +{ // Testing gather() on struct> - auto lists_column_exemplar = []() { - return lists_column_wrapper{ + auto lists_column_exemplar = [] { + return lists{ {{5}, {10, 15}, {20, 25, 30}, {35, 40, 45, 50}, {55, 60, 65}, {70, 75}, {80}, {}, {}}, - cudf::detail::make_counting_transform_iterator(0, [](auto i) { return !(i % 3); })}; + nulls_at({0, 3, 6, 9})}; }; - auto lists_column = - std::make_unique(cudf::column(lists_column_exemplar(), rmm::cuda_stream_default)); - // Assemble struct column. - std::vector> vector_of_columns; - vector_of_columns.push_back(std::move(lists_column)); - auto const struct_column = structs_column_wrapper{std::move(vector_of_columns)}.release(); + auto const structs_column = [&] { + auto lists_column = lists_column_exemplar(); + return structs_column_wrapper{{lists_column}}; + }(); // Gather to new struct column. - auto const gather_map = std::vector{-1, 4, 3, 2, 1, 7, 3}; - auto const gather_map_col = - fixed_width_column_wrapper(gather_map.begin(), gather_map.end()).release(); + auto const gather_map = gather_map_t{null_index, 4, 3, 2, 1, 7, 3}; - auto const gathered_table = - cudf::gather(cudf::table_view{std::vector{struct_column->view()}}, - gather_map_col->view()); - - auto const gathered_struct_col = gathered_table->get_column(0); - auto const gathered_struct_col_view = cudf::structs_column_view{gathered_struct_col}; + auto const gathered_structs = do_gather(structs_column, gather_map); // Verify that the gathered struct column's list member presents as if // it had itself been gathered individually. - auto const list_column_before_gathering = lists_column_exemplar().release(); - - auto const expected_gathered_list_column = - cudf::gather( - cudf::table_view{std::vector{list_column_before_gathering->view()}}, - gather_map_col->view()) - ->get_column(0); + auto const expected_gathered_list_column = [&] { + auto const list_column_before_gathering = lists_column_exemplar(); + return do_gather(list_column_before_gathering, gather_map); + }(); - expect_columns_equivalent(expected_gathered_list_column.view(), gathered_struct_col.child(0)); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(expected_gathered_list_column->view(), + gathered_structs->view().child(0)); } TYPED_TEST(TypedStructGatherTest, TestGatherStructOfListsOfLists) { - using namespace cudf::test; - // Testing gather() on struct>> auto const lists_column_exemplar = []() { - return lists_column_wrapper{ - {{{5, 5}}, - {{10, 15}}, - {{20, 25}, {30}}, - {{35, 40}, {45, 50}}, - {{55}, {60, 65}}, - {{70, 75}}, - {{80, 80}}, - {}, - {}}, - cudf::detail::make_counting_transform_iterator(0, [](auto i) { return !(i % 3); })}; + return lists{{{{5, 5}}, + {{10, 15}}, + {{20, 25}, {30}}, + {{35, 40}, {45, 50}}, + {{55}, {60, 65}}, + {{70, 75}}, + {{80, 80}}, + {}, + {}}, + nulls_at({0, 3, 6, 9})}; }; - auto lists_column = - std::make_unique(cudf::column(lists_column_exemplar(), rmm::cuda_stream_default)); - - // Assemble struct column. - std::vector> vector_of_columns; - vector_of_columns.push_back(std::move(lists_column)); - auto const struct_column = structs_column_wrapper{std::move(vector_of_columns)}.release(); + auto const structs_column = [&] { + auto lists_column = lists_column_exemplar(); + return structs_column_wrapper{{lists_column}}; + }(); // Gather to new struct column. - auto const gather_map = std::vector{-1, 4, 3, 2, 1, 7, 3}; - auto const gather_map_col = - fixed_width_column_wrapper(gather_map.begin(), gather_map.end()).release(); + auto const gather_map = gather_map_t{null_index, 4, 3, 2, 1, 7, 3}; - auto const gathered_table = - cudf::gather(cudf::table_view{std::vector{struct_column->view()}}, - gather_map_col->view()); - - auto const gathered_struct_col = gathered_table->get_column(0); - auto const gathered_struct_col_view = cudf::structs_column_view{gathered_struct_col}; + auto const gathered_structs = do_gather(structs_column, gather_map); // Verify that the gathered struct column's list member presents as if // it had itself been gathered individually. - auto const list_column_before_gathering = lists_column_exemplar().release(); - - auto const expected_gathered_list_column = - cudf::gather( - cudf::table_view{std::vector{list_column_before_gathering->view()}}, - gather_map_col->view()) - ->get_column(0); + auto const expected_gathered_list_column = [&] { + auto const list_column_before_gathering = lists_column_exemplar(); + return do_gather(list_column_before_gathering, gather_map); + }(); - expect_columns_equivalent(expected_gathered_list_column.view(), gathered_struct_col.child(0)); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(expected_gathered_list_column->view(), + gathered_structs->view().child(0)); } TYPED_TEST(TypedStructGatherTest, TestGatherStructOfStructs) { - using namespace cudf::test; - // Testing gather() on struct> auto const numeric_column_exemplar = []() { - return fixed_width_column_wrapper{ - {5, 10, 15, 20, 25, 30, 35, 45, 50, 55, 60, 65, 70, 75}, - cudf::detail::make_counting_transform_iterator(0, [](auto i) { return !(i % 3); })}; + return numerics{{5, 10, 15, 20, 25, 30, 35, 45, 50, 55, 60, 65, 70, 75}, + nulls_at({0, 3, 6, 9, 12, 15})}; }; - auto numeric_column = numeric_column_exemplar(); - auto structs_column = structs_column_wrapper{{numeric_column}}; - - auto const struct_of_structs_column = structs_column_wrapper{{structs_column}}.release(); + auto const struct_of_structs_column = [&] { + auto numeric_column = numeric_column_exemplar(); + auto structs_column = structs_column_wrapper{{numeric_column}}; + return structs_column_wrapper{{structs_column}}; + }(); // Gather to new struct column. - auto const gather_map = std::vector{-1, 4, 3, 2, 1, 7, 3}; - auto const gather_map_col = - fixed_width_column_wrapper(gather_map.begin(), gather_map.end()).release(); - - auto const gathered_table = - cudf::gather(cudf::table_view{std::vector{struct_of_structs_column->view()}}, - gather_map_col->view()); - - auto const gathered_struct_col = gathered_table->get_column(0); - auto const gathered_struct_col_view = cudf::structs_column_view{gathered_struct_col}; + auto const gather_map = gather_map_t{null_index, 4, 3, 2, 1, 7, 3}; + auto const gathered_structs = do_gather(struct_of_structs_column, gather_map); // Verify that the underlying numeric column presents as if // it had itself been gathered individually. - auto const numeric_column_before_gathering = numeric_column_exemplar().release(); - auto const expected_gathered_column = - cudf::gather( - cudf::table_view{std::vector{numeric_column_before_gathering->view()}}, - gather_map_col->view()) - ->get_column(0); + auto const expected_gathered_column = [&] { + auto const numeric_column_before_gathering = numeric_column_exemplar(); + return do_gather(numeric_column_before_gathering, gather_map); + }(); - expect_columns_equivalent(expected_gathered_column, gathered_struct_col.child(0).child(0).view()); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(expected_gathered_column->view(), + gathered_structs->view().child(0).child(0)); } TYPED_TEST(TypedStructGatherTest, TestGatherStructOfListOfStructs) { - using namespace cudf::test; - - // Testing gather() on struct> - - auto const numeric_column_exemplar = []() { - return fixed_width_column_wrapper{ - {5, 10, 15, 20, 25, 30, 35, 45, 50, 55, 60, 65, 70, 75}}; - }; - - auto numeric_column = numeric_column_exemplar(); - auto structs_column = structs_column_wrapper{{numeric_column}}.release(); - auto list_of_structs_column = cudf::make_lists_column( - 7, - fixed_width_column_wrapper{0, 2, 4, 6, 8, 10, 12, 14}.release(), - std::move(structs_column), - cudf::UNKNOWN_NULL_COUNT, - {}); - - std::vector> vector_of_columns; - vector_of_columns.push_back(std::move(list_of_structs_column)); - auto const struct_of_list_of_structs = - structs_column_wrapper{std::move(vector_of_columns)}.release(); + // Testing gather() on struct>> + + auto const struct_of_list_of_structs = [&] { + auto numeric_column = + numerics{{5, 10, 15, 20, 25, 30, 35, 45, 50, 55, 60, 65, 70, 75}}; + auto structs_column = structs{{numeric_column}}.release(); + auto list_of_structs_column = + cudf::make_lists_column(7, + offsets{0, 2, 4, 6, 8, 10, 12, 14}.release(), + std::move(structs_column), + cudf::UNKNOWN_NULL_COUNT, + {}); + + std::vector> vector_of_columns; + vector_of_columns.push_back(std::move(list_of_structs_column)); + return structs{std::move(vector_of_columns)}; + }(); // Gather to new struct column. - auto const gather_map = std::vector{-1, 4, 3, 2, 1}; - auto const gather_map_col = - fixed_width_column_wrapper(gather_map.begin(), gather_map.end()).release(); - - auto const gathered_table = cudf::gather( - cudf::table_view{std::vector{struct_of_list_of_structs->view()}}, - gather_map_col->view()); - - auto const gathered_struct_col = gathered_table->get_column(0); - auto const gathered_struct_col_view = cudf::structs_column_view{gathered_struct_col}; + auto const gather_map = gather_map_t{null_index, 4, 3, 2, 1}; + auto const gathered_structs = do_gather(struct_of_list_of_structs, gather_map); // Construct expected gather result. - auto expected_numeric_col = - fixed_width_column_wrapper{{70, 75, 50, 55, 35, 45, 25, 30, 15, 20}}; - auto expected_struct_col = structs_column_wrapper{{expected_numeric_col}}.release(); - auto expected_list_of_structs_column = - cudf::make_lists_column(5, - fixed_width_column_wrapper{0, 2, 4, 6, 8, 10}.release(), - std::move(expected_struct_col), - cudf::UNKNOWN_NULL_COUNT, - {}); - std::vector> expected_vector_of_columns; - expected_vector_of_columns.push_back(std::move(expected_list_of_structs_column)); - auto const expected_struct_of_list_of_struct = - structs_column_wrapper{std::move(expected_vector_of_columns)}.release(); - - expect_columns_equivalent(expected_struct_of_list_of_struct->view(), gathered_struct_col.view()); + auto expected_gather_result = [&] { + auto expected_numeric_col = numerics{{70, 75, 50, 55, 35, 45, 25, 30, 15, 20}}; + auto expected_struct_col = structs{{expected_numeric_col}}.release(); + auto expected_list_of_structs_column = + cudf::make_lists_column(5, + offsets{0, 2, 4, 6, 8, 10}.release(), + std::move(expected_struct_col), + cudf::UNKNOWN_NULL_COUNT, + {}); + std::vector> expected_vector_of_columns; + expected_vector_of_columns.push_back(std::move(expected_list_of_structs_column)); + return structs{std::move(expected_vector_of_columns), {0, 1, 1, 1, 1}}; + }(); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(expected_gather_result, gathered_structs->view()); } TYPED_TEST(TypedStructGatherTest, TestGatherStructOfStructsWithValidity) { - using namespace cudf::test; - // Testing gather() on struct> + using validity_iter_t = decltype(nulls_at({0})); + // Factory to construct numeric column with configurable null-mask. - auto const numeric_column_exemplar = [](nvstd::function pred) { - return fixed_width_column_wrapper{ - {5, 10, 15, 20, 25, 30, 35, 45, 50, 55, 60, 65, 70, 75}, - cudf::detail::make_counting_transform_iterator(0, [=](auto i) { return pred(i); })}; + auto const numeric_column_exemplar = [](validity_iter_t validity) { + return numerics{{5, 10, 15, 20, 25, 30, 35, 45, 50, 55, 60, 65, 70, 75}, validity}; }; - // Validity predicates. - auto const every_3rd_element_null = [](size_type i) { return !(i % 3); }; - auto const twelfth_element_null = [](size_type i) { return i != 11; }; - // Construct struct-of-struct-of-numerics. - auto numeric_column = numeric_column_exemplar(every_3rd_element_null); - auto structs_column = structs_column_wrapper{ - {numeric_column}, cudf::detail::make_counting_transform_iterator(0, twelfth_element_null)}; - auto struct_of_structs_column = structs_column_wrapper{{structs_column}}.release(); + auto struct_of_structs_column = [&] { + // Every 3rd element is null. + auto numeric_column = numeric_column_exemplar(nulls_at({0, 3, 6, 9, 12, 15})); + // 12th element is null. + auto structs_column = structs_column_wrapper{{numeric_column}, nulls_at({11})}; + return structs_column_wrapper{{structs_column}}; + }(); // Gather to new struct column. - auto const gather_map = std::vector{-1, 4, 3, 2, 1, 7, 3}; - auto const gather_map_col = - fixed_width_column_wrapper(gather_map.begin(), gather_map.end()).release(); - - auto const gathered_table = - cudf::gather(cudf::table_view{std::vector{struct_of_structs_column->view()}}, - gather_map_col->view()); - - auto const gathered_struct_col = gathered_table->get_column(0); - auto const gathered_struct_col_view = cudf::structs_column_view{gathered_struct_col}; + auto const gather_map = gather_map_t{null_index, 4, 3, 2, 1, 7, 3}; + auto const gathered_structs = do_gather(struct_of_structs_column, gather_map); // Verify that the underlying numeric column presents as if // it had itself been gathered individually. - auto const final_predicate = [=](size_type i) { - return every_3rd_element_null(i) && twelfth_element_null(i); - }; - auto const numeric_column_before_gathering = numeric_column_exemplar(final_predicate).release(); - auto const expected_gathered_column = - cudf::gather( - cudf::table_view{std::vector{numeric_column_before_gathering->view()}}, - gather_map_col->view()) - ->get_column(0); - - expect_columns_equivalent(expected_gathered_column, gathered_struct_col.child(0).child(0).view()); + auto const expected_gathered_column = [&] { + // Every 3rd element *and* the 12th element are null. + auto const final_validity = nulls_at({0, 3, 6, 9, 11, 12, 15}); + auto const numeric_column_before_gathering = numeric_column_exemplar(final_validity); + return do_gather(numeric_column_before_gathering, gather_map); + }(); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(expected_gathered_column->view(), + gathered_structs->view().child(0).child(0)); } TYPED_TEST(TypedStructGatherTest, TestEmptyGather) { - using namespace cudf::test; - - auto const ages = std::vector{5, 10, 15, 20, 25, 30}; - auto const ages_validity = std::vector{1, 1, 1, 1, 0, 1}; - auto ages_column = - fixed_width_column_wrapper{ages.begin(), ages.end(), ages_validity.begin()}; + auto const struct_column = [&] { + auto ages = numerics{{5, 10, 15, 20, 25, 30}, null_at(4)}; + return structs_column_wrapper{{ages}, null_at(5)}; + }(); - auto const struct_validity = std::vector{1, 1, 1, 1, 1, 0}; - auto const struct_column = - structs_column_wrapper{{ages_column}, struct_validity.begin()}.release(); - - auto const gather_map = std::vector{}; - auto const gather_map_col = - fixed_width_column_wrapper(gather_map.begin(), gather_map.end()).release(); - - auto const gathered_table = - cudf::gather(cudf::table_view{std::vector{struct_column->view()}}, - gather_map_col->view()); - - auto const gathered_struct_col = gathered_table->get_column(0); - auto const gathered_struct_col_view = cudf::structs_column_view{gathered_struct_col}; + auto const empty_gather_map = gather_map_t{}; + auto const gathered_structs = do_gather(struct_column, empty_gather_map); // Expect empty struct column gathered. - auto expected_ages_column = fixed_width_column_wrapper{}; - auto const expected_structs_column = structs_column_wrapper{{expected_ages_column}}.release(); + auto const expected_empty_column = [&] { + auto expected_empty_numerics = numerics{}; + return structs_column_wrapper{{expected_empty_numerics}}; + }(); - expect_columns_equivalent(*expected_structs_column, gathered_struct_col); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(expected_empty_column, gathered_structs->view()); } + +} // namespace cudf::test