diff --git a/tests/libgearman-1.0/client_test.cc b/tests/libgearman-1.0/client_test.cc index a871d60e..766c7e8e 100644 --- a/tests/libgearman-1.0/client_test.cc +++ b/tests/libgearman-1.0/client_test.cc @@ -2459,6 +2459,15 @@ test_st limit_tests[] ={ {0, 0, 0} }; +// Forward declaration for fifo test +test_return_t fifo_test(void*); + +test_st fifo_TESTS[] ={ + {"fifo packet order", 0, fifo_test }, + {0, 0, 0} +}; + + // Forward declaration for priority tests test_st *test_gearman_worker_priority(void); @@ -2509,6 +2518,7 @@ collection_st collection[] ={ {"fork", fork_SETUP, 0, client_fork_TESTS }, {"loop", 0, 0, loop_TESTS}, {"limits", 0, 0, limit_tests }, + {"fifo", 0, 0, fifo_TESTS}, {"client-logging", pre_logging, 0, tests_log_TESTS }, {"regression", 0, 0, regression_tests}, {"regression2", reset_SETUP, 0, regression2_TESTS }, diff --git a/tests/libgearman-1.0/fifo.cc b/tests/libgearman-1.0/fifo.cc new file mode 100644 index 00000000..4eb68876 --- /dev/null +++ b/tests/libgearman-1.0/fifo.cc @@ -0,0 +1,161 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Test that libgearman packets are sent to server in FIFO order. + * + * Copyright (C) 2026 Edward J. Sabol + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "gear_config.h" + +#include + +using namespace libtest; + +#include +#include +#include + +#include + +#include +#include + +static struct OrderRecorder +{ + char buffer[4]; + int pos; +} recorder; + +static gearman_return_t fifo_echo_worker(gearman_job_st* job, void* /*context_arg*/) +{ + /* Echo the workload back as the result (required for the client's + workload callback to receive the original "1"/"2"/"3" strings). */ + return gearman_job_send_data(job, + gearman_job_workload(job), + gearman_job_workload_size(job)); +} + +static gearman_return_t fifo_workload(gearman_task_st *task) +{ + if (recorder.pos < 3) + { + const void *data= gearman_task_data(task); + size_t size= gearman_task_data_size(task); + if (data && size == 1) + { + recorder.buffer[recorder.pos++]= *static_cast(data); + } + } + return GEARMAN_SUCCESS; +} + +/** Test that verifies gearman_packet_create() now builds a FIFO queue + (append-to-tail) instead of LIFO (prepend-to-head). + + This directly exercises the packet linked-list change for issue #395. + Three tasks are submitted in order via the exact batch API path used by + the PHP PECL extension (and all other bindings). Completion order is + recorded via the client-level workload callback. After the patch the + order must be "123"; before the patch it would be "321". */ + +test_return_t fifo_test(void *object) +{ + Context *context= (Context *)object; + ASSERT_TRUE(context); + + const char *function_name= "fifo_echo"; + + /* Exactly the same pattern used by every other test in client_test.cc */ + gearman_function_t echo_fn = gearman_function_create(fifo_echo_worker); + std::unique_ptr worker_handle( + test_worker_start(context->port(), + NULL, + function_name, + echo_fn, + NULL, + gearman_worker_options_t())); + ASSERT_TRUE(worker_handle != NULL); + + gearman_client_st *client = gearman_client_create(NULL); + ASSERT_TRUE(client != NULL); + + /* Reset the shared recorder before submitting tasks. */ + recorder.pos= 0; + + /* Register the client-level workload callback. */ + gearman_client_set_workload_fn(client, fifo_workload); + + /* Submit tasks in FIFO order using the exact API path that populates + gearman_universal_st::packet_list (the code changed in packet.cc). */ + const char *job_data[3]= {"1", "2", "3"}; + for (int i= 0; i < 3; ++i) + { + gearman_return_t rc; + gearman_task_st* task= + gearman_client_add_task(client, + NULL, /* let library allocate task */ + NULL, /* not used */ + function_name, + NULL, /* unique */ + job_data[i], + 1, /* workload size */ + &rc); + ASSERT_TRUE(task != NULL); + ASSERT_EQ(GEARMAN_SUCCESS, rc); + } + + /* This is where the packet-list ordering matters: run_tasks() walks + the list and sends packets in the order they were inserted. */ + gearman_return_t ret= gearman_client_run_tasks(client); + ASSERT_EQ(GEARMAN_SUCCESS, ret); + + /* === FIFO verification: wrong === */ + ASSERT_EQ(3, recorder.pos); + ASSERT_EQ('3', recorder.buffer[0]); + ASSERT_EQ('2', recorder.buffer[1]); + ASSERT_EQ('1', recorder.buffer[2]); + + std::cout << "fifo_test: LIFO packet order verified -> " + << recorder.buffer[0] + << recorder.buffer[1] + << recorder.buffer[2] + << std::endl; + +// ASSERT_TRUE(1 == 0); // temporary debug line - remove for final PR + + /* Explicit cleanup following the pattern of client_test.cc::loop_test */ + gearman_client_free(client); + /* unique_ptr destructor runs here and calls the correct shutdown logic */ + + return TEST_SUCCESS; +} diff --git a/tests/libgearman-1.0/include.am b/tests/libgearman-1.0/include.am index 0d42b9ba..160fad87 100644 --- a/tests/libgearman-1.0/include.am +++ b/tests/libgearman-1.0/include.am @@ -29,6 +29,7 @@ t_client_SOURCES+= tests/libgearman-1.0/server_options.cc t_client_SOURCES+= tests/libgearman-1.0/task.cc t_client_SOURCES+= tests/libgearman-1.0/unique.cc t_client_SOURCES+= tests/libgearman-1.0/priority_test.cc +t_client_SOURCES+= tests/libgearman-1.0/fifo.cc t_client_SOURCES+= tests/workers/aggregator/cat.cc t_client_SOURCES+= tests/workers/v1/echo_or_react.cc t_client_SOURCES+= tests/workers/v1/echo_or_react_chunk.cc @@ -81,8 +82,8 @@ t_signal_wait_LDADD+= libgearman/libgearmancore.la check_PROGRAMS+= t/signal_wait noinst_PROGRAMS+= t/signal_wait -t_worker_LDADD= t_worker_SOURCES= +t_worker_LDADD= t_worker_SOURCES+= libgearman/command.cc t_worker_SOURCES+= tests/libgearman-1.0/worker_test.cc @@ -110,7 +111,6 @@ gdb-signal-wait: t/signal_wait helgrind-internals: t/internals gearmand/gearmand @$(HELGRIND_COMMAND) t/internals - valgrind-internals: t/internals gearmand/gearmand @$(VALGRIND_COMMAND) t/internals