@@ -1788,5 +1788,172 @@ TEST(ExecPlanExecution, UnalignedInput) {
17881788 ASSERT_LT (initial_bytes_allocated, default_memory_pool ()->total_bytes_allocated ());
17891789}
17901790
1791+ struct ExecPlanErrorReporting : public testing ::TestWithParam<DummyNodeStatusReporter> {};
1792+
1793+ #pragma GCC push_options
1794+ #pragma GCC optimize("O0")
1795+ TEST_P (ExecPlanErrorReporting, SourceSink) {
1796+ ASSERT_OK_AND_ASSIGN (auto plan, ExecPlan::Make ());
1797+ auto source = MakeDummyNode (plan.get (), " source" , /* inputs=*/ {}, /* is_sink=*/ false ,
1798+ /* start_producing =*/ {},
1799+ /* stop_producing =*/ {},
1800+ /* status_reporter =*/ GetParam ());
1801+ auto sink = MakeDummyNode (plan.get (), " sink" , /* inputs=*/ {source}, /* is_sink=*/ true ,
1802+ /* start_producing =*/ {},
1803+ /* stop_producing =*/ {},
1804+ /* status_reporter =*/ GetParam ());
1805+
1806+ ASSERT_OK (plan->Validate ());
1807+ EXPECT_THAT (plan->nodes (), ElementsAre (source, sink));
1808+
1809+ bool should_finish = GetParam ().start_producing .ok ();
1810+ plan->StartProducing ();
1811+ SleepABit ();
1812+ if (should_finish)
1813+ ASSERT_FINISHES_OK (plan->finished ());
1814+ else
1815+ ASSERT_FINISHES_AND_RAISES (Invalid, plan->finished ());
1816+ }
1817+
1818+ TEST_P (ExecPlanErrorReporting, InputReceived) {
1819+ ASSERT_OK_AND_ASSIGN (auto plan, ExecPlan::Make ());
1820+ auto basic_data = MakeBasicBatches ();
1821+
1822+ ASSERT_OK_AND_ASSIGN (
1823+ auto source,
1824+ Declaration (" source" ,
1825+ SourceNodeOptions{basic_data.schema , basic_data.gen (/* parallel=*/ false ,
1826+ /* slow=*/ false )})
1827+ .AddToPlan (plan.get ()));
1828+
1829+ MakeDummyNode (plan.get (), " sink" , /* inputs=*/ {source}, /* is_sink=*/ true ,
1830+ /* start_producing =*/ {},
1831+ /* stop_producing =*/ {},
1832+ /* status_reporter =*/ GetParam ());
1833+
1834+ bool should_finish = GetParam ().start_producing .ok () &&
1835+ GetParam ().input_received .ok () && GetParam ().input_finished .ok ();
1836+ plan->StartProducing ();
1837+ SleepABit ();
1838+ if (should_finish)
1839+ ASSERT_FINISHES_OK (plan->finished ());
1840+ else
1841+ ASSERT_FINISHES_AND_RAISES (Invalid, plan->finished ());
1842+ }
1843+
1844+ TEST_P (ExecPlanErrorReporting, Finish) {
1845+ std::shared_ptr<Schema> schema_ = schema ({field (" data" , uint32 ())});
1846+ std::optional<ExecBatch> batch =
1847+ ExecBatchFromJSON ({int32 (), boolean ()},
1848+ " [[4, false], [5, null], [6, false], [7, false], [null, true]]" );
1849+ PushGenerator<std::optional<ExecBatch>> batch_producer;
1850+ ASSERT_OK_AND_ASSIGN (auto plan, ExecPlan::Make ());
1851+ auto basic_data = MakeBasicBatches ();
1852+
1853+ ASSERT_OK_AND_ASSIGN (auto source,
1854+ Declaration (" source" , SourceNodeOptions (schema_, batch_producer))
1855+ .AddToPlan (plan.get ()));
1856+
1857+ MakeDummyNode (plan.get (), " sink" , /* inputs=*/ {source}, /* is_sink=*/ true ,
1858+ /* start_producing =*/ {},
1859+ /* stop_producing =*/ {},
1860+ /* status_reporter =*/ GetParam ());
1861+
1862+ bool should_start = GetParam ().start_producing .ok ();
1863+ plan->StartProducing ();
1864+ SleepABit ();
1865+ if (should_start) {
1866+ ASSERT_FALSE (plan->finished ().is_finished ());
1867+ } else {
1868+ ASSERT_FINISHES_AND_RAISES (Invalid, plan->finished ());
1869+ }
1870+ batch_producer.producer ().Push (batch);
1871+ SleepABit ();
1872+
1873+ bool should_receive = should_start && GetParam ().input_received .ok ();
1874+ if (should_receive) {
1875+ ASSERT_FALSE (plan->finished ().is_finished ());
1876+ } else {
1877+ ASSERT_FINISHES_AND_RAISES (Invalid, plan->finished ());
1878+ }
1879+
1880+ batch_producer.producer ().Push (std::nullopt );
1881+ SleepABit ();
1882+ bool should_finish = should_receive && GetParam ().input_finished .ok ();
1883+ if (should_finish) {
1884+ ASSERT_FINISHES_OK (plan->finished ());
1885+ } else {
1886+ ASSERT_FINISHES_AND_RAISES (Invalid, plan->finished ());
1887+ return ;
1888+ }
1889+ }
1890+
1891+ TEST_P (ExecPlanErrorReporting, StopProducing) {
1892+ std::shared_ptr<Schema> schema_ = schema ({field (" data" , uint32 ())});
1893+ std::optional<ExecBatch> batch =
1894+ ExecBatchFromJSON ({int32 (), boolean ()},
1895+ " [[4, false], [5, null], [6, false], [7, false], [null, true]]" );
1896+ PushGenerator<std::optional<ExecBatch>> batch_producer;
1897+ ASSERT_OK_AND_ASSIGN (auto plan, ExecPlan::Make ());
1898+ auto basic_data = MakeBasicBatches ();
1899+
1900+ ASSERT_OK_AND_ASSIGN (auto source,
1901+ Declaration (" source" , SourceNodeOptions (schema_, batch_producer))
1902+ .AddToPlan (plan.get ()));
1903+
1904+ MakeDummyNode (plan.get (), " sink" , /* inputs=*/ {source}, /* is_sink=*/ true ,
1905+ /* start_producing =*/ {},
1906+ /* stop_producing =*/ {},
1907+ /* status_reporter =*/ GetParam ());
1908+
1909+ bool should_start = GetParam ().start_producing .ok ();
1910+ plan->StartProducing ();
1911+ SleepABit ();
1912+ if (should_start) {
1913+ ASSERT_FALSE (plan->finished ().is_finished ());
1914+ } else {
1915+ ASSERT_FINISHES_AND_RAISES (Invalid, plan->finished ());
1916+ }
1917+ batch_producer.producer ().Push (batch);
1918+ SleepABit ();
1919+
1920+ bool should_receive = should_start && GetParam ().input_received .ok ();
1921+ if (should_receive) {
1922+ ASSERT_FALSE (plan->finished ().is_finished ());
1923+ } else {
1924+ ASSERT_FINISHES_AND_RAISES (Invalid, plan->finished ());
1925+ }
1926+ // this should not be needed
1927+ batch_producer.producer ().Push (std::nullopt );
1928+ SleepABit ();
1929+ plan->StopProducing ();
1930+ SleepABit ();
1931+ bool should_stop =
1932+ should_receive && GetParam ().stop_producing .ok () && GetParam ().input_finished .ok ();
1933+ if (should_stop) {
1934+ ASSERT_FINISHES_AND_RAISES (Cancelled, plan->finished ());
1935+ } else {
1936+ ASSERT_FINISHES_AND_RAISES (Invalid, plan->finished ());
1937+ return ;
1938+ }
1939+ }
1940+
1941+ INSTANTIATE_TEST_SUITE_P (
1942+ ExecPlan, ExecPlanErrorReporting,
1943+ testing::Values (DummyNodeStatusReporter{Status::OK (), Status::OK (), Status::OK (),
1944+ Status::OK ()},
1945+ DummyNodeStatusReporter{Status::Invalid (" 1" ), Status::OK (),
1946+ Status::OK (), Status::OK ()},
1947+ DummyNodeStatusReporter{Status::OK (), Status::Invalid (" 1" ),
1948+ Status::OK (), Status::OK ()},
1949+ DummyNodeStatusReporter{Status::OK (), Status::OK (),
1950+ Status::Invalid (" 1" ), Status::OK ()},
1951+ DummyNodeStatusReporter{Status::OK (), Status::OK (), Status::OK (),
1952+ Status::Invalid (" 1" )},
1953+ DummyNodeStatusReporter{Status::Invalid (" 1" ), Status::Invalid (" 1" ),
1954+ Status::Invalid (" 1" ), Status::Invalid (" 1" )}));
1955+
1956+ #pragma GCC pop_options
1957+
17911958} // namespace acero
17921959} // namespace arrow
0 commit comments