Merge lp:~jan-kneschke/mysql-proxy/test-suite-refactoring into lp:mysql-proxy

Proposed by Jan Kneschke
Status: Merged
Merged at revision: 1137
Proposed branch: lp:~jan-kneschke/mysql-proxy/test-suite-refactoring
Merge into: lp:mysql-proxy
Diff against target: 2517 lines (+1267/-929)
19 files modified
configure.in (+1/-0)
lib/glib2.c (+12/-1)
lib/posix.c (+29/-1)
src/mysql-proxy-cli.c (+10/-1)
tests/suite/Makefile.am (+5/-1)
tests/suite/base/r/lineno.result (+1/-1)
tests/suite/base/r/pooling.result (+12/-9)
tests/suite/base/t/lineno.lua (+14/-1)
tests/suite/base/t/lineno.test (+5/-1)
tests/suite/base/t/load_multi.test (+9/-9)
tests/suite/base/t/pooling-mock.lua (+8/-24)
tests/suite/base/t/pooling.test (+19/-11)
tests/suite/run-tests.lua (+437/-869)
tests/suite/testenv/Makefile.am (+7/-0)
tests/suite/testenv/fileutils.lua (+84/-0)
tests/suite/testenv/process.lua (+179/-0)
tests/suite/testenv/processmanager.lua (+72/-0)
tests/suite/testenv/shellutils.lua (+92/-0)
tests/suite/testenv/testutils.lua (+271/-0)
To merge this branch: bzr merge lp:~jan-kneschke/mysql-proxy/test-suite-refactoring
Reviewer Review Type Date Requested Status
Leith (community) Approve
Registry Administrators Pending
Review via email: mp+34853@code.launchpad.net

This proposal supersedes a proposal from 2010-09-08.

Description of the change

As a side-effect of fixing http://bugs.mysql.com/37630 the test-suite had to be refactored to not call get_pid() on the PID-file for shutting the proxy down.

This rewrite changes the test-runner to use proper OO-style to store the state of:
* commands it wants to run
* processes that it tracks
* proxy's and their options and environments

The helper functions that check paths, shell, ... are split out into modules under testenv.*

To post a comment you must log in.
Revision history for this message
Leith (mleith) wrote : Posted in a previous version of this proposal

Looks good to me. A few very minor comments (with the first being the only one needing to be resolved):

 - No copyright/licenses headers have had the date updated.. Please do that

 - Can we keep this comment on one line:
  + /* only try to delete the PID if we created it
 + */

 - Why hard code the following to MySQLProxy:get_args()?:
 + opts["plugins"] = "proxy"
 + opts["daemon"] = true

 - This looks incomplete, what is the plan here?:
 + if extra_params then
 + -- FIXME: implement me
 + end

 - Bug#38415 has been fixed now (https://code.launchpad.net/~schuster/mysql-proxy/remove_unix_socket/+merge/23652), we can just remove the following now:

 + -- if we are supposed to listen on a UNIX socket, remove it first, because we don't clean it up on exit!
 + -- TODO: fix the code, instead of hacking around here! Bug#38415
 + if proxy_options['proxy-address'] == '/tmp/mysql-proxy-test.sock' then
 + os.remove(proxy_options['proxy-address'])
 + end

 - I see changes to Makefile.am, but none to any CMakeLists.txt files, I understand that the test suite does not run on Windows yet, however I'd also like us to move towards cmake as the one true source as well. Please consider making it so that cmake can still build all of this on *nix, and opening a new issue to get the test suite working on Windows as well.

review: Needs Fixing
Revision history for this message
Jan Kneschke (jan-kneschke) wrote : Posted in a previous version of this proposal

Am 07.09.2010 um 11:41 schrieb Leith:

> Review: Needs Fixing
> Looks good to me. A few very minor comments (with the first being the only one needing to be resolved):
>
> - No copyright/licenses headers have had the date updated.. Please do that

added the copyright headers to all the testenv/ files.

> - Can we keep this comment on one line:
> + /* only try to delete the PID if we created it
> + */

done.

> - Why hard code the following to MySQLProxy:get_args()?:
> + opts["plugins"] = "proxy"
> + opts["daemon"] = true

because we only want to run the 'proxy' plugin and want to run it all as daemon all the time.

We don't want the admin plugin to be loaded here as it would only make it more complicated to setup the ports for it. As we don't use it, we don't load it for these tests.

> - This looks incomplete, what is the plan here?:
> + if extra_params then
> + -- FIXME: implement me
> + end

I removed it.

> - Bug#38415 has been fixed now (https://code.launchpad.net/~schuster/mysql-proxy/remove_unix_socket/+merge/23652), we can just remove the following now:
>
> + -- if we are supposed to listen on a UNIX socket, remove it first, because we don't clean it up on exit!
> + -- TODO: fix the code, instead of hacking around here! Bug#38415
> + if proxy_options['proxy-address'] == '/tmp/mysql-proxy-test.sock' then
> + os.remove(proxy_options['proxy-address'])
> + end

Will post-pone remove this until #38415 is marked as "closed".

> - I see changes to Makefile.am, but none to any CMakeLists.txt files, I understand that the test suite does not run on Windows yet, however I'd also like us to move towards cmake as the one true source as well. Please consider making it so that cmake can still build all of this on *nix, and opening a new issue to get the test suite working on Windows as well.

Let's move this into an extra task. The current suite has only files for the automake case.

> --
> https://code.launchpad.net/~jan-kneschke/mysql-proxy/test-suite-refactoring/+merge/34720
> You are the owner of lp:~jan-kneschke/mysql-proxy/test-suite-refactoring.

Jan

Revision history for this message
Leith (mleith) wrote : Posted in a previous version of this proposal

The current files also need the year updating to 2010 where appropriate as well.

review: Needs Fixing
Revision history for this message
Leith (mleith) wrote : Posted in a previous version of this proposal

OK, still slightly wrong, the copyright header should be:

"Copyright (c) $start, $end, Oracle and/or its affiliates. All rights reserved."

So please change the likes of:

Copyright (c) 2007, 2008, 2010, Oracle and/or its affiliates. All rights reserved.

To:

Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.

review: Needs Fixing
Revision history for this message
Leith (mleith) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure.in'
2--- configure.in 2010-05-25 13:44:43 +0000
3+++ configure.in 2010-09-08 12:58:43 +0000
4@@ -407,6 +407,7 @@
5 lib/proxy/Makefile \
6 tests/Makefile \
7 tests/suite/Makefile \
8+ tests/suite/testenv/Makefile \
9 tests/suite/base/Makefile \
10 tests/suite/base/t/Makefile \
11 tests/suite/base/r/Makefile \
12
13=== modified file 'lib/glib2.c'
14--- lib/glib2.c 2010-08-20 15:02:44 +0000
15+++ lib/glib2.c 2010-09-08 12:58:43 +0000
16@@ -1,5 +1,5 @@
17 /* $%BEGINLICENSE%$
18- Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
19+ Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
20
21 This program is free software; you can redistribute it and/or
22 modify it under the terms of the GNU General Public License as
23@@ -33,6 +33,16 @@
24 return 0;
25 }
26
27+static int lua_g_setenv (lua_State *L) {
28+ const char * key = luaL_checkstring (L, 1);
29+ const char * value = luaL_checkstring (L, 2);
30+ int overwrite = luaL_optint(L, 3, 1);
31+
32+ lua_pushboolean(L, g_setenv(key, value, overwrite));
33+
34+ return 1;
35+}
36+
37 static int lua_g_get_current_time (lua_State *L) {
38 GTimeVal t;
39
40@@ -83,6 +93,7 @@
41 {"usleep", lua_g_usleep},
42 {"md5", lua_g_checksum_md5},
43 {"get_current_time", lua_g_get_current_time},
44+ {"setenv", lua_g_setenv},
45 {NULL, NULL},
46 };
47
48
49=== modified file 'lib/posix.c'
50--- lib/posix.c 2008-11-11 17:09:04 +0000
51+++ lib/posix.c 2010-09-08 12:58:43 +0000
52@@ -1,9 +1,28 @@
53-/* Copyright (C) 2008 MySQL AB */
54+/* $%BEGINLICENSE%$
55+ Copyright (c) 2008, 2010 Oracle and/or its affiliates. All rights reserved.
56+
57+ This program is free software; you can redistribute it and/or
58+ modify it under the terms of the GNU General Public License as
59+ published by the Free Software Foundation; version 2 of the
60+ License.
61+
62+ This program is distributed in the hope that it will be useful,
63+ but WITHOUT ANY WARRANTY; without even the implied warranty of
64+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
65+ GNU General Public License for more details.
66+
67+ You should have received a copy of the GNU General Public License
68+ along with this program; if not, write to the Free Software
69+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
70+ 02110-1301 USA
71+
72+ $%ENDLICENSE%$ */
73
74 #ifdef HAVE_CONFIG_H
75 #include "config.h"
76 #endif
77
78+#include <sys/param.h>
79 #include <sys/types.h>
80 #include <unistd.h>
81
82@@ -27,6 +46,14 @@
83 return 1;
84 }
85
86+static int lua_getcwd (lua_State *L) {
87+ char cwd[MAXPATHLEN];
88+
89+ lua_pushstring (L, getcwd(cwd, sizeof(cwd)));
90+
91+ return 1;
92+}
93+
94 static int lua_getpwuid(lua_State *L) {
95 struct passwd *p;
96 int uid = luaL_checkinteger (L, 1);
97@@ -76,6 +103,7 @@
98 {"getpid", lua_getpid},
99 {"getuid", lua_getuid},
100 {"getpwuid", lua_getpwuid},
101+ {"getcwd", lua_getcwd},
102 {NULL, NULL},
103 };
104
105
106=== modified file 'src/mysql-proxy-cli.c'
107--- src/mysql-proxy-cli.c 2010-09-02 12:05:35 +0000
108+++ src/mysql-proxy-cli.c 2010-09-08 12:58:43 +0000
109@@ -56,6 +56,7 @@
110 #endif
111
112 #include <glib.h>
113+#include <glib/gstdio.h> /* g_unlink() */
114 #include <gmodule.h>
115
116 #ifdef HAVE_LUA_H
117@@ -115,6 +116,7 @@
118 GOptionEntry *config_entries;
119
120 gchar *pid_file;
121+ gboolean pid_file_is_created;
122
123 gchar *plugin_dir;
124 gchar **plugin_names;
125@@ -166,7 +168,13 @@
126 if (frontend->user) g_free(frontend->user);
127 if (frontend->log_filename) g_free(frontend->log_filename);
128 if (frontend->log_config_filename) g_free(frontend->log_config_filename);
129- if (frontend->pid_file) g_free(frontend->pid_file);
130+ if (frontend->pid_file) {
131+ /* only try to delete the PID if we created it */
132+ if (frontend->pid_file_is_created) {
133+ g_unlink(frontend->pid_file);
134+ }
135+ g_free(frontend->pid_file);
136+ }
137 if (frontend->log_level) g_free(frontend->log_level);
138 if (frontend->plugin_dir) g_free(frontend->plugin_dir);
139
140@@ -591,6 +599,7 @@
141
142 GOTO_EXIT(EXIT_FAILURE);
143 }
144+ frontend->pid_file_is_created = TRUE; /* track that we created the PID file successfully and can delete it */
145 }
146
147 /* the message has to be _after_ the g_option_content_parse() to
148
149=== modified file 'tests/suite/Makefile.am'
150--- tests/suite/Makefile.am 2009-09-22 15:13:32 +0000
151+++ tests/suite/Makefile.am 2010-09-08 12:58:43 +0000
152@@ -1,8 +1,9 @@
153-SUBDIRS=base bugs
154+SUBDIRS=testenv base bugs
155
156 TESTS_ENVIRONMENT = \
157 MYSQL_TEST_BIN="${MYSQL_TEST_BIN}" \
158 LUA_USER_PATH="${top_srcdir}/lib/?.lua" \
159+ LUA_PATH="${top_srcdir}/tests/suite/?.lua" \
160 LUA_CPATH=".libs/?.@DYNLIB_LUA_SUFFIX@;${top_builddir}/lib/.libs/?.@DYNLIB_LUA_SUFFIX@" \
161 @DYNLIB_PATH_VAR@="${top_builddir}/src/.libs/:${@DYNLIB_PATH_VAR@}" \
162 MYSQL_PROXY_VERSION="${PACKAGE_VERSION}" \
163@@ -24,6 +25,9 @@
164
165 EXTRA_DIST=\
166 run-tests.lua \
167+ fileutils.lua \
168+ shellutils.lua \
169+ testutils.lua \
170 lua-tests-wrapper.sh.in \
171 CMakeLists.txt
172
173
174=== modified file 'tests/suite/base/r/lineno.result'
175--- tests/suite/base/r/lineno.result 2009-09-21 14:34:47 +0000
176+++ tests/suite/base/r/lineno.result 2010-09-08 12:58:43 +0000
177@@ -1,4 +1,4 @@
178 SELECT backtrace;
179 backtrace
180 stack traceback:
181- [string "/tmp/dummy.lua"]:48: in function <[string "/tmp/dummy.lua"]:32>
182+ [string "lineno.lua"]:48: in function <[string "lineno.lua"]:32>
183
184=== modified file 'tests/suite/base/r/pooling.result'
185--- tests/suite/base/r/pooling.result 2008-07-03 15:20:35 +0000
186+++ tests/suite/base/r/pooling.result 2010-09-08 12:58:43 +0000
187@@ -1,15 +1,18 @@
188-SELECT counter;
189+SELECT conn_id, stmt_id;
190 conn_id stmt_id
191 2 1
192-SELECT counter;
193-conn_id stmt_id
194-1 1
195-SELECT counter;
196-conn_id stmt_id
197-1 2
198-SELECT counter;
199+SELECT conn_id, stmt_id;
200 conn_id stmt_id
201 2 2
202-SELECT counter;
203+SELECT conn_id, stmt_id;
204+conn_id stmt_id
205+3 1
206+SELECT conn_id, stmt_id;
207+conn_id stmt_id
208+2 3
209+SELECT conn_id, stmt_id;
210+conn_id stmt_id
211+4 1
212+SELECT conn_id, stmt_id;
213 conn_id stmt_id
214 5 1
215
216=== modified file 'tests/suite/base/t/lineno.lua'
217--- tests/suite/base/t/lineno.lua 2010-04-06 15:25:06 +0000
218+++ tests/suite/base/t/lineno.lua 2010-09-08 12:58:43 +0000
219@@ -1,5 +1,5 @@
220 --[[ NEVER change the number of lines of this license header without also changing r/lineno.result. Thus we don't dynamically update the license header, either.
221- Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
222+ Copyright (c) 2009, 2010 Oracle and/or its affiliates. All rights reserved.
223
224 This program is free software; you can redistribute it and/or modify
225 it under the terms of the GNU General Public License as published by
226@@ -50,6 +50,19 @@
227
228 }
229 }
230+ elseif packet:sub(2) == "SELECT current_backtrace_filename" then
231+ proxy.response = {
232+ type = proxy.MYSQLD_PACKET_OK,
233+ resultset = {
234+ fields = {
235+ { name = "filename", type = proxy.MYSQL_TYPE_STRING }
236+ },
237+ rows = {
238+ { debug.getinfo(1, "S").short_src }
239+ }
240+
241+ }
242+ }
243 else
244 proxy.response = {
245 type = proxy.MYSQLD_PACKET_ERR,
246
247=== modified file 'tests/suite/base/t/lineno.test'
248--- tests/suite/base/t/lineno.test 2010-04-06 14:26:51 +0000
249+++ tests/suite/base/t/lineno.test 2010-09-08 12:58:43 +0000
250@@ -1,5 +1,5 @@
251 # $%BEGINLICENSE%$
252-# Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
253+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
254 #
255 # This program is free software; you can redistribute it and/or
256 # modify it under the terms of the GNU General Public License as
257@@ -17,4 +17,8 @@
258 # 02110-1301 USA
259 #
260 # $%ENDLICENSE%$
261+
262+# fetch the Lua version of the current file-name
263+--let current_backtrace_filename = query_get_value("SELECT current_backtrace_filename", "filename", 1)
264+--replace_result $current_backtrace_filename "[string \"lineno.lua\"]"
265 SELECT backtrace;
266
267=== modified file 'tests/suite/base/t/load_multi.test'
268--- tests/suite/base/t/load_multi.test 2010-04-06 14:26:51 +0000
269+++ tests/suite/base/t/load_multi.test 2010-09-08 12:58:43 +0000
270@@ -1,5 +1,5 @@
271 # $%BEGINLICENSE%$
272-# Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
273+# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
274 #
275 # This program is free software; you can redistribute it and/or
276 # modify it under the terms of the GNU General Public License as
277@@ -18,14 +18,14 @@
278 #
279 # $%ENDLICENSE%$
280 --disable_query_log
281---replace_result $srcdir <srcdir>
282---eval pload $srcdir/base/t/lm2.lua
283+--replace_result $abs_srcdir <srcdir>
284+--eval pload $abs_srcdir/base/t/lm2.lua
285 --enable_query_log
286 connect (conn1,127.0.0.1,$MYSQL_USER,$MYSQL_PASSWORD,test,$PROXY_PORT,);
287 select pload status;
288 --disable_query_log
289---replace_result $srcdir <srcdir>
290---eval pload $srcdir/base/t/lm1.lua
291+--replace_result $abs_srcdir <srcdir>
292+--eval pload $abs_srcdir/base/t/lm1.lua
293 --enable_query_log
294 connection conn1;
295 select 'first' as info;
296@@ -44,13 +44,13 @@
297 disconnect conn1;
298 disconnect conn2;
299 --disable_query_log
300---replace_result $srcdir <srcdir>
301---eval pload $srcdir/base/t/lm3.lua
302+--replace_result $abs_srcdir <srcdir>
303+--eval pload $abs_srcdir/base/t/lm3.lua
304 --enable_query_log
305 select 1000;
306 --disable_query_log
307---replace_result $srcdir <srcdir>
308---eval punload $srcdir/base/t/lm3.lua
309+--replace_result $abs_srcdir <srcdir>
310+--eval punload $abs_srcdir/base/t/lm3.lua
311 --enable_query_log
312 select 1000;
313
314
315=== modified file 'tests/suite/base/t/pooling-mock.lua'
316--- tests/suite/base/t/pooling-mock.lua 2010-04-06 14:26:51 +0000
317+++ tests/suite/base/t/pooling-mock.lua 2010-09-08 12:58:43 +0000
318@@ -1,5 +1,5 @@
319 --[[ $%BEGINLICENSE%$
320- Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
321+ Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
322
323 This program is free software; you can redistribute it and/or
324 modify it under the terms of the GNU General Public License as
325@@ -23,24 +23,8 @@
326 -- by comparing the statement-ids and the connection ids we can
327 -- track if the ro-pooling script was reusing a connection
328 --
329-function packet_auth(fields)
330- fields = fields or { }
331- return "\010" .. -- proto version
332- (fields.version or "5.0.45-proxy") .. -- version
333- "\000" .. -- term-null
334- "\001\000\000\000" .. -- thread-id
335- "\065\065\065\065" ..
336- "\065\065\065\065" .. -- challenge - part I
337- "\000" .. -- filler
338- "\001\130" .. -- server cap (long pass, 4.1 proto)
339- "\008" .. -- charset
340- "\002\000" .. -- status
341- ("\000"):rep(13) .. -- filler
342- "\065\065\065\065"..
343- "\065\065\065\065"..
344- "\065\065\065\065"..
345- "\000" -- challenge - part II
346-end
347+
348+local proto = require("mysql.proto")
349
350 -- will be called once after connect
351 stmt_id = 0
352@@ -60,7 +44,7 @@
353 proxy.response = {
354 type = proxy.MYSQLD_PACKET_RAW,
355 packets = {
356- packet_auth()
357+ proto.to_challenge_packet({})
358 }
359 }
360 return proxy.PROXY_SEND_RESULT
361@@ -78,15 +62,15 @@
362 stmt_id = stmt_id + 1
363
364 local query = packet:sub(2)
365- if query == 'SELECT counter' then
366+ if query == 'SELECT conn_id, stmt_id' then
367 proxy.response = {
368 type = proxy.MYSQLD_PACKET_OK,
369 resultset = {
370 fields = {
371- { name = 'conn_id' },
372- { name = 'stmt_id' },
373+ { name = 'conn_id', type = proxy.MYSQLD_TYPE_INT },
374+ { name = 'stmt_id', type = proxy.MYSQLD_TYPE_INT },
375 },
376- rows = { { tostring(conn_id), tostring(stmt_id) } }
377+ rows = { { conn_id, stmt_id } }
378 }
379 }
380 else
381
382=== modified file 'tests/suite/base/t/pooling.test'
383--- tests/suite/base/t/pooling.test 2010-04-06 14:26:51 +0000
384+++ tests/suite/base/t/pooling.test 2010-09-08 12:58:43 +0000
385@@ -1,5 +1,5 @@
386 # $%BEGINLICENSE%$
387-# Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
388+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
389 #
390 # This program is free software; you can redistribute it and/or
391 # modify it under the terms of the GNU General Public License as
392@@ -17,22 +17,30 @@
393 # 02110-1301 USA
394 #
395 # $%ENDLICENSE%$
396-connect (conn1,127.0.0.1,root,,,$PROXY_PORT);
397-SELECT counter;
398+
399+# open a few connections and test if they reuse connections that
400+# end up in the pool
401+
402+connect (conn1,$PROXY_HOST,root,,,$PROXY_PORT);
403+SELECT conn_id, stmt_id;
404 disconnect conn1;
405
406-connect (conn2,127.0.0.1,root,,,$PROXY_PORT);
407-SELECT counter;
408+connect (conn2,$PROXY_HOST,root,,,$PROXY_PORT);
409+SELECT conn_id, stmt_id;
410 disconnect conn2;
411
412-connect (conn3,127.0.0.1,root,,,$PROXY_PORT);
413-SELECT counter;
414+connect (conn3,$PROXY_HOST,root,,,$PROXY_PORT);
415+SELECT conn_id, stmt_id;
416 disconnect conn3;
417
418-connect (conn4,127.0.0.1,root,,,$PROXY_PORT);
419-SELECT counter;
420+connect (conn4,$PROXY_HOST,root,,,$PROXY_PORT);
421+SELECT conn_id, stmt_id;
422 disconnect conn4;
423
424-connect (conn5,127.0.0.1,root,,,$PROXY_PORT);
425-SELECT counter;
426+connect (conn5,$PROXY_HOST,root,,,$PROXY_PORT);
427+SELECT conn_id, stmt_id;
428 disconnect conn5;
429+
430+connect (conn6,$PROXY_HOST,root,,,$PROXY_PORT);
431+SELECT conn_id, stmt_id;
432+disconnect conn6;
433
434=== modified file 'tests/suite/run-tests.lua'
435--- tests/suite/run-tests.lua 2010-05-05 10:46:53 +0000
436+++ tests/suite/run-tests.lua 2010-09-08 12:58:43 +0000
437@@ -30,46 +30,156 @@
438 require("lfs")
439 require("glib2")
440 require("posix")
441-
442----
443--- get the directory-name of a path
444---
445--- @param filename path to create the directory name from
446-function dirname(filename)
447- local dirname = filename
448-
449- attr = assert(lfs.attributes(dirname))
450-
451- if attr.mode == "directory" then
452- return dirname
453- end
454-
455- dirname = filename:gsub("/[^/]+$", "")
456-
457- attr = assert(lfs.attributes(dirname))
458-
459- assert(attr.mode == "directory", "dirname("..filename..") failed: is ".. attr.mode)
460-
461- return dirname
462-end
463-
464----
465--- get the file-name of a path
466---
467--- @param filename path to create the directory name from
468-function basename(filename)
469- name = filename:gsub(".*/", "")
470-
471- return name
472-end
473+local fileutils = require("testenv.fileutils")
474+local shellutils = require("testenv.shellutils")
475+local testutils = require("testenv.testutils")
476+local process = require("testenv.process")
477+local processmanager = require("testenv.processmanager")
478
479 --
480 -- a set of user variables which can be overwritten from the environment
481 --
482
483-local testdir = dirname(arg[0])
484-
485-local USE_POPEN = os.getenv('USE_POPEN') or 1
486+TestRunner = {
487+ num_tests = 0,
488+ num_passes = 0,
489+ num_skipped = 0,
490+ num_fails = 0,
491+ failed_test = {},
492+ tests = {},
493+
494+ testenv = {
495+ },
496+
497+ force_on_error = true,
498+ tests_to_skip_filename = "tests_to_skip.lua",
499+ tests_regex = os.getenv("TESTS_REGEX")
500+}
501+
502+function TestRunner:new(o)
503+ -- create a new process object
504+ --
505+ o = o or {}
506+ setmetatable(o, self)
507+ self.__index = self
508+
509+ local port_base = testutils.get_port_base(os.getenv("MYSQL_PROXY_START_PORT"), os.getenv("MYSQL_PROXY_END_PORT"))
510+
511+ self.testenv.testdir = fileutils.dirname(arg[0])
512+ self.testenv.srcdir = os.getenv("srcdir") or self.testenv.testdir .. "/"
513+ self.testenv.top_builddir = os.getenv("top_builddir") or self.testenv.testdir .. "/../"
514+ self.testenv.builddir = os.getenv("builddir") or self.testenv.testdir .. "/" -- same as srcdir by default
515+ self.testenv.plugin_dir = self.testenv.top_builddir .. "/plugins/"
516+ self.testenv.basedir = lfs.currentdir()
517+ self.testenv.lua_path = self.testenv.basedir .. "/" .. self.testenv.srcdir .. "/../../lib/?.lua"
518+ self.testenv.lua_cpath = self.testenv.basedir .. "/../../lib/.libs/?.so"
519+
520+ self.testenv.PROXY_HOST = os.getenv("PROXY_HOST") or "127.0.0.1"
521+ self.testenv.PROXY_PORT = os.getenv("PROXY_PORT") or tostring(port_base + 0)
522+ self.testenv.MYSQL_TEST_BIN = os.getenv("MYSQL_TEST_BIN") or "mysqltest"
523+ self.testenv.MYSQL_CLIENT_BIN = os.getenv("MYSQL_CLIENT_BIN") or "mysql"
524+ self.testenv.PROXY_CHAIN_PORT = os.getenv("PROXY_CHAIN_PORT") or tostring(port_base + 30)
525+
526+ self.testenv.abs_srcdir = os.getenv("abs_srcdir")
527+
528+ if not self.testenv.abs_srcdir then
529+ if not fileutils.path_is_absolute(self.testenv.srcdir) then
530+ local abs_srcdir = posix.getcwd() .. "/" .. self.testenv.srcdir
531+ self.testenv.abs_srcdir = abs_srcdir
532+ else
533+ self.testenv.abs_srcdir = self.testenv.srcdir
534+ end
535+ glib2.setenv("abs_srcdir", self.testenv.abs_srcdir) -- expose the abs_srcdir again as we run the proxy as --daemon which chdir()s to /
536+ end
537+
538+ return o
539+end
540+
541+function TestRunner:register_tests(suites)
542+ for i, suite in ipairs(suites) do
543+ local suite_srcdir = self.testenv.srcdir .. '/' .. suite
544+ local suite_skipfile = suite_srcdir .. '/' .. self.tests_to_skip_filename
545+ local stat = assert(lfs.attributes(suite_srcdir))
546+
547+ -- if it is a directory, execute all of them
548+ if stat.mode == "directory" then
549+ if fileutils.file_exists(suite_skipfile) then
550+ assert(loadfile(suite_skipfile))()
551+ end
552+
553+ for file in lfs.dir(suite_srcdir .. "/t/") do
554+ local testname = file:match("(.+)\.test$")
555+
556+ if testname then
557+ local is_skipped = false
558+
559+ if (self.tests_regex and not testname:match(self.tests_regex)) or tests_to_skip[testname] then
560+ is_skipped = true
561+ end
562+
563+ local test = MySQLProxyTest:new()
564+ test.testname = testname
565+ test.suite_srcdir = suite_srcdir
566+ test.testenv = self.testenv
567+ test.skipped = is_skipped
568+
569+ self.tests[#self.tests + 1] = test
570+ end
571+ end
572+ end
573+ end
574+end
575+
576+function TestRunner:run_all()
577+ local exitcode = 0
578+
579+ for i, test in ipairs(self.tests) do
580+ shellutils.print_verbose("# >> " .. test.testname .. " started")
581+
582+ self.num_tests = self.num_tests + 1
583+ local r = test:run()
584+ if (r == 0) then
585+ self.num_passes = self.num_passes + 1
586+ elseif (r == 77) then
587+ self.num_skipped = self.num_skipped + 1
588+ else
589+ self.num_fails = self.num_fails + 1
590+ table.insert(self.failed_test, test)
591+ end
592+
593+ shellutils.print_verbose("# << (exitcode = " .. r .. ")" )
594+
595+ if r ~= 0 and r ~= 77 and exitcode == 0 then
596+ exitcode = r
597+ end
598+
599+ if self.num_fails > 0 and (not self.force_on_error) then
600+ break
601+ end
602+ end
603+
604+ if self.num_fails > 0 then
605+ print ("*** ERRORS OCCURRED - The following tests failed")
606+ for i, test in pairs(self.failed_test) do
607+ print(test.testname)
608+ end
609+ end
610+
611+ --
612+ -- prints test suite statistics
613+ shellutils.print_verbose (string.format('tests: %d - skipped: %d (%4.1f%%) - passed: %d (%4.1f%%) - failed: %d (%4.1f%%)',
614+ self.num_tests,
615+ self.num_skipped,
616+ self.num_skipped / self.num_tests * 100,
617+ self.num_passes,
618+ self.num_passes / (self.num_tests - self.num_skipped) * 100,
619+ self.num_fails,
620+ self.num_fails / (self.num_tests - self.num_skipped) * 100))
621+
622+ return exitcode
623+end
624+
625+
626
627 local VERBOSE = os.getenv('TEST_VERBOSE') or
628 os.getenv('VERBOSE') or
629@@ -77,860 +187,318 @@
630 VERBOSE = VERBOSE + 0
631
632 local FORCE_ON_ERROR = os.getenv('FORCE_ON_ERROR') or os.getenv('FORCE')
633+
634+
635 local MYSQL_USER = os.getenv("MYSQL_USER") or "root"
636 local MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD") or ""
637 local MYSQL_HOST = os.getenv("MYSQL_HOST") or "127.0.0.1"
638 local MYSQL_PORT = os.getenv("MYSQL_PORT") or "3306"
639 local MYSQL_DB = os.getenv("MYSQL_DB") or "test"
640-local MYSQL_TEST_BIN = os.getenv("MYSQL_TEST_BIN") or "mysqltest"
641-local MYSQL_CLIENT_BIN = os.getenv("MYSQL_CLIENT_BIN") or "mysql"
642-local TESTS_REGEX = os.getenv("TESTS_REGEX")
643-
644---
645--- Global variables that can be referenced from .options files
646---
647--- TODO : add HOST variables for MASTER, SLAVE, CHAIN
648-function get_port_base()
649- local port_base_start = os.getenv("MYSQL_PROXY_START_PORT") or 32768
650- local port_base_end = os.getenv("MYSQL_PROXY_END_PORT") or 65535
651- local port_interval = 64 -- let's take the base port in steps of ...
652- local range = port_base_end - port_base_start - port_interval
653- math.randomseed(posix.getpid())
654- local rand = math.floor(math.random() * (math.ceil(range / port_interval)))
655- local port_base = port_base_start + (rand * port_interval)
656-
657- print(("... using tcp-port = %d as start port"):format(port_base))
658-
659- return port_base
660-end
661-local port_base = get_port_base()
662-
663-PROXY_HOST = os.getenv("PROXY_HOST") or "127.0.0.1"
664-PROXY_PORT = os.getenv("PROXY_PORT") or tostring(port_base + 0)
665-PROXY_MASTER_PORT = os.getenv("PROXY_MASTER_PORT") or tostring(port_base + 10)
666-PROXY_SLAVE_PORT = os.getenv("PROXY_SLAVE_PORT") or tostring(port_base + 20)
667-PROXY_CHAIN_PORT = os.getenv("PROXY_CHAIN_PORT") or tostring(port_base + 30)
668-ADMIN_PORT = os.getenv("ADMIN_PORT") or tostring(port_base + 15)
669-ADMIN_MASTER_PORT = os.getenv("ADMIN_MASTER_PORT") or tostring(port_base + 25)
670-ADMIN_SLAVE_PORT = os.getenv("ADMIN_SLAVE_PORT") or tostring(port_base + 35)
671-ADMIN_CHAIN_PORT = os.getenv("ADMIN_CHAIN_PORT") or tostring(port_base + 45)
672-ADMIN_USER = os.getenv("ADMIN_USER") or "root"
673-ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD") or ""
674-ADMIN_DEFAULT_SCRIPT_FILENAME = os.getenv("ADMIN_DEFAULT_SCRIPT_FILENAME") or ""
675--- local PROXY_TMP_LUASCRIPT = os.getenv("PROXY_TMP_LUASCRIPT") or "/tmp/proxy.tmp.lua"
676-
677-local srcdir = os.getenv("srcdir") or testdir .. "/"
678-local top_builddir = os.getenv("top_builddir") or testdir .. "/../"
679-local builddir = os.getenv("builddir") or testdir .. "/" -- same as srcdir by default
680-
681-local PROXY_TRACE = os.getenv("PROXY_TRACE") or "" -- use it to inject strace or valgrind
682-local PROXY_PARAMS = os.getenv("PROXY_PARAMS") or "" -- extra params
683-local PROXY_BINPATH = os.getenv("PROXY_BINPATH") or top_builddir .. "/src/mysql-proxy"
684-PROXY_LIBPATH = os.getenv("PROXY_LIBPATH") or top_builddir .. "/plugins/"
685-
686-local COVERAGE_LCOV = os.getenv("COVERAGE_LCOV")
687+
688
689 --
690 -- end of user-vars
691 --
692
693-PROXY_PIDFILE = lfs.currentdir() .. "/mysql-proxy-test.pid"
694-PROXY_MASTER_PIDFILE = lfs.currentdir() .. "/mysql-proxy-test-master.pid"
695-PROXY_SLAVE_PIDFILE = lfs.currentdir() .. "/mysql-proxy-test-slave.pid"
696-PROXY_CHAIN_PIDFILE = lfs.currentdir() .. "/mysql-proxy-test-chain.pid"
697-PROXY_BACKEND_PIDFILE = lfs.currentdir() .. "/mysql-proxy-test-backend.pid"
698-PROXY_TEST_BASEDIR = lfs.currentdir()
699-
700-DEFAULT_SCRIPT_FILENAME = "/tmp/dummy.lua"
701-default_proxy_options = {
702- ["proxy-backend-addresses"] = MYSQL_HOST .. ":" .. MYSQL_PORT,
703- ["proxy-address"] = PROXY_HOST .. ":" .. PROXY_PORT,
704- ["admin-address"] = PROXY_HOST .. ":" .. ADMIN_PORT,
705- ["admin-username"] = ADMIN_USER,
706- ["admin-password"] = ADMIN_PASSWORD,
707- ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
708- ["pid-file"] = PROXY_PIDFILE,
709- ["proxy-lua-script"] = DEFAULT_SCRIPT_FILENAME,
710- ["plugin-dir"] = PROXY_LIBPATH,
711- ["basedir"] = PROXY_TEST_BASEDIR,
712- }
713-
714-default_master_options = {
715- ["proxy-backend-addresses"] = MYSQL_HOST .. ":" .. MYSQL_PORT,
716- ["proxy-address"] = PROXY_HOST .. ":" .. PROXY_MASTER_PORT,
717- ["admin-address"] = PROXY_HOST .. ":" .. ADMIN_MASTER_PORT,
718- ["admin-username"] = ADMIN_USER,
719- ["admin-password"] = ADMIN_PASSWORD,
720- ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
721- ["pid-file"] = PROXY_MASTER_PIDFILE,
722- ["proxy-lua-script"] = DEFAULT_SCRIPT_FILENAME,
723- ["plugin-dir"] = PROXY_LIBPATH,
724- ["basedir"] = PROXY_TEST_BASEDIR,
725- }
726-
727-default_slave_options = {
728- ["proxy-backend-addresses"] = MYSQL_HOST .. ":" .. MYSQL_PORT,
729- ["proxy-address"] = PROXY_HOST .. ":" .. PROXY_SLAVE_PORT,
730- ["admin-address"] = PROXY_HOST .. ":" .. ADMIN_SLAVE_PORT,
731- ["admin-username"] = ADMIN_USER,
732- ["admin-password"] = ADMIN_PASSWORD,
733- ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
734- ["pid-file"] = PROXY_SLAVE_PIDFILE,
735- ["proxy-lua-script"] = DEFAULT_SCRIPT_FILENAME,
736- ["plugin-dir"] = PROXY_LIBPATH,
737- ["basedir"] = PROXY_TEST_BASEDIR,
738- }
739-
740-
741-tests_to_skip = {}
742-local tests_to_skip_filename = 'tests_to_skip.lua'
743-local proxy_list = {}
744-default_proxy_name = 'default'
745-
746-local exitcode=0
747-
748----
749--- print_verbose()
750---
751--- prints a message if either the DEBUG or VERBOSE variables
752--- are set.
753---
754--- @param msg the message being printed
755--- @param min_level the minimum verbosity level for printing the message (default 1)
756-function print_verbose(msg, min_level)
757- min_level = min_level or 1
758- if (VERBOSE >= min_level) then
759- print (msg)
760- end
761-end
762----
763--- check if the file exists and is readable
764-function file_exists(f)
765- return lfs.attributes(f)
766-end
767-
768----
769--- create the default option file
770---
771-function make_default_options_file(fname)
772- if file_exists(fname) then
773- return
774- end
775- local fd = assert(io.open(fname, "w"))
776- fd:write('start_proxy(default_proxy_name, default_proxy_options) \n')
777- fd:close();
778-end
779-
780----
781--- copy a file
782---
783--- @param dst filename of the destination
784--- @param src filename of the source
785-function file_copy(dst, src)
786- -- print_verbose("copying ".. src .. " to " .. dst)
787- local src_fd = assert(io.open(src, "rb"))
788- local content = src_fd:read("*a")
789- src_fd:close();
790-
791- local dst_fd = assert(io.open(dst, "wb+"))
792- dst_fd:write(content);
793- dst_fd:close();
794-end
795-
796----
797--- create a empty file
798---
799--- if the file exists, it will be truncated to 0
800---
801--- @param dst filename to create and truncate
802-function file_empty(dst)
803- -- print_verbose("emptying " .. dst)
804- local dst_fd = assert(io.open(dst, "wb+"))
805- dst_fd:close();
806-end
807-
808----
809--- turn a option-table into a string
810---
811--- the values are encoded and quoted for the shell
812---
813--- @param tbl a option table
814--- @param sep the seperator, defaults to a space
815-function options_tostring(tbl, sep)
816- -- default value for sep
817- sep = sep or " "
818+-- options for the MySQL Proxy
819+MySQLProxy = {
820+}
821+function MySQLProxy:new(o)
822+ -- create a new process object
823
824- assert(type(tbl) == "table")
825- assert(type(sep) == "string")
826-
827- local s = ""
828- for k, v in pairs(tbl) do
829- local values
830- -- if the value is a table, repeat the option
831- if type(v) == "table" then
832- values = v
833- else
834- values = { v }
835- end
836-
837- for tk, tv in pairs(values) do
838- local enc_value = tv:gsub("\\", "\\\\"):gsub("\"", "\\\"")
839- s = s .. "--" .. k .. "=\"" .. enc_value .. "\" "
840- end
841- end
842- -- print_verbose(" option: " .. s)
843-
844- return s
845-end
846-
847---- turns an option table into a string of environment variables
848---
849-function env_options_tostring(tbl)
850- assert(type(tbl) == "table")
851-
852- local s = ""
853- for k, v in pairs(tbl) do
854- local enc_value = v:gsub("\\", "\\\\"):gsub("\"", "\\\"")
855- s = s .. k .. "=\"" .. enc_value .. "\" "
856- end
857-
858- return s
859-end
860-
861-
862-function os_execute(cmdline)
863- print_verbose("$ " .. cmdline)
864- return os.execute(cmdline)
865-end
866-
867----
868--- get the PID from the pid file
869---
870-function get_pid(pid_file_name)
871- -- the file may exist, but the PID may not be written yet
872- local pid
873- local rounds = 0
874-
875- repeat
876- local fh = assert(io.open(pid_file_name, 'r'))
877- pid = fh:read("*n")
878- fh:close()
879- if not pid then
880- glib2.usleep(200 * 1000) -- wait a bit until we get some content
881- rounds = rounds + 1
882-
883- if rounds > 10 then
884- error(("reading PID from existing pid-file '%s' failed after waiting 2 sec"):format(
885- pid_file_name))
886+ o = o or {}
887+ setmetatable(o, self)
888+ self.__index = self
889+
890+ self.backends = { }
891+ return o
892+end
893+
894+function MySQLProxy:add_backend(addr)
895+ self.backends[#self.backends + 1] = addr
896+end
897+
898+function MySQLProxy:get_args(opts)
899+ -- add a default backend
900+ if #self.backends == 0 then
901+ self:add_backend(("%s:%d"):format(MYSQL_HOST, MYSQL_PORT))
902+ end
903+
904+ opts = opts or { }
905+ opts["plugin-dir"] = self.testenv["plugin_dir"]
906+ opts["basedir"] = self.testenv["basedir"]
907+ opts["pid-file"] = opts["pid-file"] or ("%s/mysql-proxy.pid"):format(self.testenv.basedir)
908+ opts["plugins"] = "proxy"
909+ opts["proxy-address"]= opts["proxy-address"] or ("%s:%d"):format(self.testenv.PROXY_HOST, self.testenv.PROXY_PORT)
910+ self.proxy_address = opts["proxy-address"]
911+ opts["daemon"] = true
912+ opts["lua-path"] = self.testenv.lua_path
913+ opts["lua-cpath"] = self.testenv.lua_cpath
914+
915+ opts["proxy-backend-addresses"] = self.backends
916+
917+ return opts
918+end
919+
920+function MySQLProxy:get_env()
921+ return {}
922+end
923+
924+function MySQLProxy:get_command()
925+ return os.getenv("PROXY_BINPATH") or self.testenv.top_builddir .. "/src/mysql-proxy"
926+end
927+
928+---
929+-- the backend
930+MockBackend = MySQLProxy:new()
931+function MockBackend:chain_with_frontend(frontend)
932+ frontend:add_backend(self.proxy_address)
933+end
934+
935+Test = {
936+}
937+
938+function Test:new(o)
939+ -- create a new process object
940+ --
941+ o = o or {}
942+ setmetatable(o, self)
943+ self.__index = self
944+
945+ self.skipped = false
946+ self.testname = nil
947+
948+ return o
949+end
950+
951+---
952+-- @return true[, msg] if test should be skipped, false otherwise
953+function Test:is_skipped()
954+ return self.skipped
955+end
956+
957+function Test:setup()
958+ return true
959+end
960+
961+function Test:teardown()
962+ return true
963+end
964+
965+function Test:run()
966+ local is_skipped, skip_msg = self:is_skipped()
967+
968+ if is_skipped then
969+ print(('skip %s %s'):format(
970+ self.testname,
971+ skip_msg or 'no reason given'))
972+ return 77
973+ end
974+
975+ self.procs = processmanager:new()
976+
977+ local ret, errmsg = self:setup()
978+ if not ret then
979+ print(("err %s # setup failed: %s"):format(
980+ self.testname,
981+ errmsg))
982+ ret = -1
983+ else
984+ ret = self:run_test()
985+ self:teardown()
986+ end
987+
988+ self.procs:shutdown_all() -- shutdown all the processes we started
989+
990+ return ret
991+end
992+
993+---
994+-- a Test baseclass that knows about
995+-- * mysqltest as test-driver
996+-- * starting MySQL Proxies before starting mysqltest
997+--
998+-- inherited from Test
999+MySQLProxyTest = Test:new()
1000+
1001+---
1002+-- 'test_self' is a hack to pass down the object we test right now to chain_proxy().
1003+--
1004+-- It is set in MySQLProxyTest:setup()
1005+test_self = nil
1006+
1007+---
1008+-- setup the backend proxies and wire them together
1009+--
1010+-- depends on 'test_self' being a set to the currently running Test-object
1011+function chain_proxy(backend_filenames, script_filename)
1012+ local self = test_self
1013+
1014+ if type(backend_filenames) ~= "table" then
1015+ backend_filenames = { backend_filenames }
1016+ end
1017+
1018+ local backends = { }
1019+ for backend_ndx, backend_filename in ipairs(backend_filenames) do
1020+ local mock = MockBackend:new()
1021+ mock.testenv = self.testenv
1022+
1023+ local port = tonumber(self.testenv.PROXY_CHAIN_PORT) + backend_ndx - 1 -- we start with 1
1024+
1025+ local mock_args = mock:get_args({
1026+ ["proxy-lua-script"] = ("%s/t/%s"):format(self.suite_srcdir, backend_filename),
1027+ ["proxy-address"] = ("%s:%d"):format(self.testenv.PROXY_HOST, port),
1028+ ["pid-file"] = ("%s/chain-%d.pid"):format(self.testenv.basedir, backend_ndx)
1029+ })
1030+
1031+ local proc = process:new()
1032+ local ret = proc:execute(mock:get_command(),
1033+ mock:get_env(),
1034+ mock_args)
1035+ if ret ~= 0 then
1036+ return false, ""
1037+ end
1038+
1039+ if mock_args["pid-file"] then
1040+ local is_running, errmsg = proc:wait_running(mock_args["pid-file"])
1041+ if not is_running then
1042+ return false, errmsg or "not running"
1043 end
1044 end
1045- until pid
1046-
1047- -- the PID we get here should be a number
1048- assert(type(pid) == "number")
1049-
1050- return pid
1051-end
1052-
1053-function wait_proc_up(pid_file)
1054- local rounds = 0
1055-
1056- while not file_exists(pid_file) do
1057- glib2.usleep(200 * 1000) -- wait until the pid-file is created
1058-
1059- rounds = rounds + 1
1060- print_verbose(("(wait_proc_up) pid-wait: %d rounds, (%s)"):format(rounds, pid_file))
1061-
1062- if rounds > 1000 then error(("proxy failed to start: no pid-file %s"):format(pid_file)) end
1063- end
1064-
1065- local pid = get_pid(pid_file)
1066-
1067- rounds = 0
1068- -- check that the process referenced in the PID-file is still up
1069- while 0 ~= os.execute("kill -0 ".. pid .." 2> /dev/null") do
1070- glib2.usleep(200 * 1000) -- wait until the pid-file is created
1071- rounds = rounds + 1
1072- print_verbose(("(wait_proc_up) kill-wait: %d rounds, pid=%d (%s)"):format(rounds, pid, pid_file))
1073-
1074- if rounds > 5 then error(("proxy seems to have crashed: pid=%d (%s)"):format(pid, pid_file)) end
1075- end
1076-end
1077-
1078-function proc_is_up(pid)
1079- return os.execute("kill -0 ".. pid .." 2> /dev/null")
1080-end
1081-
1082-function proc_stop(pid)
1083- return os.execute("kill -TERM ".. pid)
1084-end
1085-
1086-function wait_proc_down(pid_file)
1087- local rounds = 0
1088- local pid = get_pid(pid_file)
1089-
1090- -- wait until the proc in the pid file is dead
1091- -- the shutdown takes at about 500ms
1092- while 0 == proc_is_up(pid) do
1093- glib2.usleep(200 * 1000) -- wait until process is gone
1094- rounds = rounds + 1
1095- print_verbose(("(wait_proc_down) kill-wait: %d rounds, pid=%d (%s)"):format(rounds, pid, pid_file))
1096- end
1097-end
1098-
1099-function stop_proxy()
1100- -- shut dowm the proxy
1101- --
1102- -- win32 has tasklist and taskkill on the shell
1103-
1104-
1105- -- shuts down every proxy in the proxy list
1106- --
1107- for proxy_name, proxy_options in pairs(proxy_list) do
1108- local pid
1109- pid_file = proxy_options['pid-file']
1110- print_verbose ('stopping proxy ' .. proxy_name)
1111-
1112- pid = get_pid(pid_file)
1113-
1114- if proc_is_up(pid) then
1115- proc_stop(pid)
1116- else
1117- print("-- process "..proxy_name.." is already down")
1118- exitcode = -1
1119- end
1120- end
1121-
1122- -- wait until they are all gone
1123- for proxy_name, proxy_options in pairs(proxy_list) do
1124- pid_file = proxy_options['pid-file']
1125-
1126- wait_proc_down(pid_file)
1127-
1128- os.remove(pid_file)
1129- end
1130-
1131- --
1132- -- empties the proxy list
1133- --
1134- proxy_list = { }
1135-end
1136-
1137-function only_item ( tbl, item)
1138- local exists = false
1139- for i,v in pairs(tbl) do
1140- if i == item then
1141- exists = true
1142- else
1143- return false
1144- end
1145- end
1146- return exists
1147-end
1148-
1149----
1150--- before_test()
1151---
1152--- Executes a script with a base name like test_name and extension ".options"
1153---
1154--- If there is no such file, the default options are used
1155---
1156-function before_test(basedir, test_name)
1157- local script_filename = basedir .. "/t/" .. test_name .. ".lua"
1158- local options_filename = basedir .. "/t/" .. test_name .. ".options"
1159- local has_option_file = file_exists(options_filename)
1160- if file_exists( script_filename) then
1161- if has_option_file then
1162- default_proxy_options['proxy-lua-script'] = script_filename
1163- print_verbose('using lua script directly ' .. script_filename)
1164- file_empty(DEFAULT_SCRIPT_FILENAME)
1165- else
1166- default_proxy_options['proxy-lua-script'] = DEFAULT_SCRIPT_FILENAME
1167- file_copy(DEFAULT_SCRIPT_FILENAME, script_filename)
1168- print_verbose('copying lua script to default ' .. script_filename)
1169- end
1170- else
1171- default_proxy_options['proxy-lua-script'] = DEFAULT_SCRIPT_FILENAME
1172- file_empty(DEFAULT_SCRIPT_FILENAME)
1173- print_verbose('using empty lua script')
1174- end
1175- global_basedir = basedir
1176- print_verbose ('current_dir ' ..
1177- basedir ..
1178- ' - script: ' ..
1179- default_proxy_options['proxy-lua-script'] )
1180- --
1181- -- executes the content of the options file
1182- --
1183- if has_option_file then
1184- print_verbose('# using options file ' .. options_filename)
1185- stop_proxy()
1186- else
1187- --
1188- -- if no option file is found, the default options file is executed
1189- --
1190- options_filename = basedir .. "/t/default.options"
1191- print_verbose('#using default options file' .. options_filename)
1192- if only_item(proxy_list,'default') then
1193- print_verbose('reusing existing proxy')
1194- return
1195- end
1196- make_default_options_file(options_filename)
1197- end
1198- assert(loadfile(options_filename))()
1199-end
1200-
1201-function after_test()
1202- if only_item(proxy_list, 'default') then
1203- return
1204- end
1205- stop_proxy()
1206-end
1207-
1208-function alternative_execute (cmd)
1209- print_verbose(cmd)
1210-
1211- local fh = io.popen(cmd)
1212- assert(fh, 'error executing '.. cmd)
1213- local result = ''
1214- local line = fh:read()
1215- while line do
1216- result = result .. line
1217- line = fh:read()
1218- end
1219- fh:close()
1220- return result
1221-end
1222-
1223-function conditional_execute (cmd)
1224- if USE_POPEN then
1225- return alternative_execute(cmd)
1226- else
1227- return os_execute(cmd)
1228- end
1229-end
1230-
1231----
1232--- run a test
1233---
1234--- @param testname name of the test
1235--- @return exit-code of mysql-test
1236-function run_test(filename, basedir)
1237- if not basedir then basedir = srcdir end
1238-
1239- local testname = assert(filename:match("t/(.+)\.test"))
1240- if tests_to_skip[testname] then
1241- print('skip ' .. testname ..' '.. (tests_to_skip[testname] or 'no reason given') )
1242- return 0, 1
1243- end
1244- before_test(basedir, testname)
1245- if VERBOSE > 1 then
1246- os.execute('echo -n "' .. testname .. ' " ; ' )
1247- end
1248+
1249+ self.procs:add(proc)
1250+ table.insert(backends, mock)
1251+ end
1252+ -- all the backends are up, start the middle proxy
1253+
1254+ script_filename = ("%s/t/%s"):format(self.suite_srcdir, script_filename)
1255+
1256+ local proxy = MySQLProxy:new()
1257+ proxy.testenv = self.testenv
1258+
1259+ for _, backend in ipairs(backends) do
1260+ backend:chain_with_frontend(proxy)
1261+ end
1262+
1263+ local proxy_args = proxy:get_args({
1264+ ["proxy-lua-script"] = script_filename,
1265+ })
1266+
1267+ local proc = process:new()
1268+ local ret = proc:execute(proxy:get_command(),
1269+ proxy:get_env(),
1270+ proxy_args)
1271+ if ret ~= 0 then
1272+ return false, ""
1273+ end
1274+ if proxy_args["pid-file"] then
1275+ local is_running, errmsg = proc:wait_running(proxy_args["pid-file"])
1276+ if not is_running then
1277+ return false, errmsg or "not running"
1278+ end
1279+ end
1280+
1281+ self.procs:add(proc)
1282+end
1283+
1284+function MySQLProxyTest:start_proxy(script_filename)
1285+ local proxy = MySQLProxy:new()
1286+ proxy.testenv = self.testenv
1287+ local proxy_args = proxy:get_args({
1288+ ["proxy-lua-script"] = script_filename,
1289+ })
1290+
1291+ local proc = process:new()
1292+ local ret = proc:execute(proxy:get_command(),
1293+ proxy:get_env(),
1294+ proxy_args)
1295+ if ret ~= 0 then
1296+ return false, ""
1297+ end
1298+ if proxy_args["pid-file"] then
1299+ local is_running, errmsg = proc:wait_running(proxy_args["pid-file"])
1300+ if not is_running then
1301+ return false, errmsg or "not running"
1302+ end
1303+ end
1304+
1305+ self.procs:add(proc)
1306+end
1307+
1308+function MySQLProxyTest:setup()
1309+ local script_filename = ("%s/t/%s.lua"):format(self.suite_srcdir, self.testname)
1310+ local options_filename = ("%s/t/%s.options"):format(self.suite_srcdir, self.testname)
1311+ local has_script_file = fileutils.file_exists(script_filename)
1312+ local has_options_file = fileutils.file_exists(options_filename)
1313+
1314+ -- if we have a options file, run it as part of the setup phases
1315+ -- if not, assume that we want to start a proxy with the script_filename
1316+ if has_options_file then
1317+ -- load the options file
1318+ test_self = self -- expose 'self' so that chain_proxy() can use it
1319+ local setup_func, errmsg = loadfile(options_filename)
1320+ if not setup_func then
1321+ return false, errmsg
1322+ end
1323+
1324+ -- try to call the setup func
1325+ local ret, errmsg = pcall(setup_func)
1326+ if not ret then
1327+ return false, errmsg
1328+ end
1329+ else
1330+ self:start_proxy(script_filename)
1331+ end
1332+ return true
1333+end
1334+
1335+function MySQLProxyTest:run_test()
1336 local result = 0
1337- local ret = conditional_execute(
1338- env_options_tostring({
1339- ['MYSQL_USER'] = MYSQL_USER,
1340- ['MYSQL_PASSWORD'] = MYSQL_PASSWORD,
1341- ['PROXY_HOST'] = PROXY_HOST,
1342- ['PROXY_PORT'] = PROXY_PORT,
1343- ['PROXY_CHAIN_PORT'] = PROXY_CHAIN_PORT,
1344- ['MASTER_PORT'] = PROXY_MASTER_PORT,
1345- ['SLAVE_PORT'] = PROXY_SLAVE_PORT,
1346- }) .. ' ' ..
1347- MYSQL_TEST_BIN .. " " ..
1348- options_tostring({
1349- user = MYSQL_USER,
1350- password = MYSQL_PASSWORD,
1351- database = MYSQL_DB,
1352- host = PROXY_HOST,
1353- port = PROXY_PORT,
1354+
1355+ local proc = process:new()
1356+ local ret = proc:popen(
1357+ self.testenv.MYSQL_TEST_BIN,
1358+ {
1359+ ['MYSQL_USER'] = self.testenv.MYSQL_USER,
1360+ ['MYSQL_PASSWORD'] = self.testenv.MYSQL_PASSWORD,
1361+ ['PROXY_HOST'] = self.testenv.PROXY_HOST,
1362+ ['PROXY_PORT'] = self.testenv.PROXY_PORT,
1363+ ['PROXY_CHAIN_PORT'] = self.testenv.PROXY_CHAIN_PORT,
1364+ ['MASTER_PORT'] = self.testenv.PROXY_MASTER_PORT,
1365+ ['SLAVE_PORT'] = self.testenv.PROXY_SLAVE_PORT,
1366+ },
1367+ {
1368+ user = self.testenv.MYSQL_USER,
1369+ password = self.testenv.MYSQL_PASSWORD,
1370+ database = self.testenv.MYSQL_DB,
1371+ host = self.testenv.PROXY_HOST,
1372+ port = self.testenv.PROXY_PORT,
1373 verbose = (VERBOSE > 0) and "TRUE" or "FALSE", -- pass down the verbose setting
1374- ["test-file"] = basedir .. "/t/" .. testname .. ".test",
1375- ["result-file"] = basedir .. "/r/" .. testname .. ".result",
1376- ["logdir"] = builddir, -- the .result dir might not be writable
1377+ ["test-file"] = self.suite_srcdir .. "/t/" .. self.testname .. ".test",
1378+ ["result-file"] = self.suite_srcdir .. "/r/" .. self.testname .. ".result",
1379+ ["logdir"] = self.suite_builddir, -- the .result dir might not be writable
1380 })
1381- )
1382- if USE_POPEN then
1383- assert(ret == 'ok' or ret =='not ok', 'unexpected result <' .. ret .. '>')
1384- if (ret == 'ok') then
1385- result = 0
1386- elseif ret == 'not ok'then
1387- result = 1
1388- end
1389- print(ret .. ' ' .. testname)
1390- else
1391- result = ret
1392- end
1393- after_test()
1394- return result, 0
1395-end
1396-
1397----
1398---sql_execute()
1399---
1400--- Executes a SQL query in a given Proxy
1401---
1402--- If no Proxy is indicated, the query is passed directly to the backend server
1403---
1404--- @param query A SQL statement to execute, or a table of SQL statements
1405--- @param proxy_name the name of the proxy that executes the query
1406-function sql_execute(queries, proxy_name)
1407- local ret = 0
1408- assert(type(queries) == 'string' or type(queries) == 'table', 'invalid type for query' )
1409- if type(queries) == 'string' then
1410- queries = {queries}
1411- end
1412- local query = ''
1413- for i, q in pairs(queries) do
1414- query = query .. ';' .. q
1415- end
1416-
1417- if proxy_name then
1418- --
1419- -- a Proxy name is passed.
1420- -- The query is executed with the given proxy
1421- local opts = proxy_list[proxy_name]
1422- assert(opts,'proxy '.. proxy_name .. ' not active')
1423- assert(opts['proxy-address'],'address for proxy '.. proxy_name .. ' not found')
1424- local p_host, p_port = opts['proxy-address']:match('(%S+):(%S+)')
1425- ret = os_execute( MYSQL_CLIENT_BIN .. ' ' ..
1426- options_tostring({
1427- user = MYSQL_USER,
1428- password = MYSQL_PASSWORD,
1429- database = MYSQL_DB,
1430- host = p_host,
1431- port = p_port,
1432- execute = query
1433- })
1434- )
1435- assert(ret == 0, 'error using mysql client ')
1436- else
1437- --
1438- -- No proxy name was passed.
1439- -- The query is executed in the backend server
1440- --
1441- ret = os_execute( MYSQL_CLIENT_BIN .. ' ' ..
1442- options_tostring({
1443- user = MYSQL_USER,
1444- password = MYSQL_PASSWORD,
1445- database = MYSQL_DB,
1446- host = PROXY_HOST,
1447- port = MYSQL_PORT,
1448- execute = query
1449- })
1450- )
1451- end
1452- return ret
1453-end
1454-
1455-stop_proxy()
1456-
1457--- the proxy needs the lua-script to exist
1458--- file_empty(PROXY_TMP_LUASCRIPT)
1459-
1460--- if the pid-file is still pointing to a active process, kill it
1461---[[
1462-if file_exists(PROXY_PIDFILE) then
1463- os.execute("kill -TERM `cat ".. PROXY_PIDFILE .." `")
1464- os.remove(PROXY_PIDFILE)
1465-end
1466---]]
1467-
1468-if COVERAGE_LCOV then
1469- -- os_execute(COVERAGE_LCOV ..
1470- -- " --zerocounters --directory ".. srcdir .. "/../src/" )
1471-end
1472-
1473--- setting the include path
1474---
1475-
1476--- this is the path containing the global Lua modules
1477-local GLOBAL_LUA_PATH = os.getenv('LUA_LDIR') or '/usr/share/lua/5.1/?.lua'
1478-
1479--- this is the path containing the Proxy libraries
1480-local PROXY_LUA_PATH = os.getenv('LUA_PATH') or '/usr/local/share/?.lua'
1481-
1482--- This is the path with specific libraries for the test suite
1483-local PRIVATE_LUA_PATH = arg[1] .. '/t/?.lua'
1484-
1485--- This is the path with additional libraries that the user needs
1486-local LUA_USER_PATH = os.getenv('LUA_USER_PATH') or '../lib/?.lua'
1487-
1488--- Building the final include path
1489-local INCLUDE_PATH =
1490- LUA_USER_PATH .. ';' ..
1491- PRIVATE_LUA_PATH .. ';' ..
1492- GLOBAL_LUA_PATH .. ';' ..
1493- PROXY_LUA_PATH
1494-
1495----
1496--- start_proxy()
1497---
1498--- starts an instance of MySQL Proxy
1499---
1500--- @param proxy_name internal name of the proxy instance, for retrieval
1501--- @param proxy_options the options to start the Proxy
1502-function start_proxy(proxy_name, proxy_options)
1503- -- start the proxy
1504- assert(type(proxy_options) == 'table')
1505- if not file_exists(proxy_options['proxy-lua-script']) then
1506- proxy_options['proxy-lua-script'] =
1507- global_basedir ..
1508- '/t/' .. proxy_options['proxy-lua-script']
1509- end
1510- print_verbose("starting " .. proxy_name .. " with " .. options_tostring(proxy_options))
1511-
1512- -- remove the old pid-file if it exists
1513- os.remove(proxy_options['pid-file'])
1514- -- if we are supposed to listen on a UNIX socket, remove it first, because we don't clean it up on exit!
1515- -- TODO: fix the code, instead of hacking around here! Bug#38415
1516- if proxy_options['proxy-address'] == '/tmp/mysql-proxy-test.sock' then
1517- os.remove(proxy_options['proxy-address'])
1518- end
1519- -- os.execute("head " .. proxy_options['proxy-lua-script'])
1520- assert(os.execute( 'LUA_PATH="' .. INCLUDE_PATH .. '" ' ..
1521- PROXY_TRACE .. " " .. PROXY_BINPATH .. " " ..
1522- options_tostring( proxy_options) .. " &"
1523- ))
1524-
1525- -- wait until the proxy is up
1526- wait_proc_up(proxy_options['pid-file'])
1527-
1528- proxy_list[proxy_name] = proxy_options
1529-end
1530-
1531----
1532--- simulate_replication()
1533---
1534--- creates a fake master/slave by having two proxies
1535--- pointing at the same backend
1536---
1537--- you can alter those backends by changing
1538--- the starting parameters
1539---
1540--- @param master_options options for master
1541--- @param slave_options options for slave
1542-function simulate_replication(master_options, slave_options)
1543- if not master_options then
1544- master_options = default_master_options
1545- end
1546- if not master_options['pid-file'] then
1547- master_options['pid-file'] = PROXY_MASTER_PIDFILE
1548- end
1549- if not slave_options then
1550- slave_options = default_slave_options
1551- end
1552- if not slave_options['pid-file'] then
1553- slave_options['pid-file'] = PROXY_SLAVE_PIDFILE
1554- end
1555- start_proxy('master', master_options)
1556- start_proxy('slave', slave_options)
1557-end
1558-
1559----
1560--- chain_proxy()
1561---
1562--- starts two proxy instances, with the first one is pointing to the
1563--- default backend server, and the second one (with default ports)
1564--- is pointing at the first proxy
1565---
1566--- client -> proxy -> backend_proxy -> [ mysql-backend ]
1567---
1568--- usually we use it to mock a server with a lua script (...-mock.lua) and
1569--- the script under test in the proxy (...-test.lua)
1570---
1571--- in case you want to start several backend_proxies, just provide a array
1572--- as first param
1573---
1574--- @param backend_lua_script
1575--- @param second_lua_script
1576--- @param use_replication uses a master proxy as backend
1577-function chain_proxy (backend_lua_scripts, second_lua_script, use_replication)
1578- local backends = { }
1579-
1580- if type(backend_lua_scripts) == "table" then
1581- backends = backend_lua_scripts
1582- else
1583- backends = { backend_lua_scripts }
1584- end
1585-
1586- local backend_addresses = { }
1587-
1588- for i, backend_lua_script in ipairs(backends) do
1589- backend_addresses[i] = PROXY_HOST .. ":" .. (PROXY_CHAIN_PORT + i - 1)
1590-
1591- backend_proxy_options = {
1592- ["proxy-backend-addresses"] = MYSQL_HOST .. ":" .. MYSQL_PORT,
1593- ["proxy-address"] = backend_addresses[i],
1594- ["admin-address"] = PROXY_HOST .. ":" .. (ADMIN_CHAIN_PORT + i - 1),
1595- ["admin-username"] = ADMIN_USER,
1596- ["admin-password"] = ADMIN_PASSWORD,
1597- ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
1598- ["pid-file"] = PROXY_CHAIN_PIDFILE .. i,
1599- ["proxy-lua-script"] = backend_lua_script or DEFAULT_SCRIPT_FILENAME,
1600- ["plugin-dir"] = PROXY_LIBPATH,
1601- ["basedir"] = PROXY_TEST_BASEDIR,
1602- ["log-level"] = (VERBOSE == 4) and "debug" or "critical",
1603- }
1604- --
1605- -- if replication was not started, then it is started here
1606- --
1607- if use_replication and (use_replication == true) then
1608- if (proxy_list['master'] == nil) then
1609- simulate_replication()
1610- end
1611- backend_proxy_options["proxy-backend-addresses"] = PROXY_HOST .. ':' .. PROXY_MASTER_PORT
1612- end
1613- start_proxy('backend_proxy' .. i, backend_proxy_options)
1614- end
1615-
1616- second_proxy_options = {
1617- ["proxy-backend-addresses"] = backend_addresses ,
1618- ["proxy-address"] = PROXY_HOST .. ":" .. PROXY_PORT,
1619- ["admin-address"] = PROXY_HOST .. ":" .. ADMIN_PORT,
1620- ["admin-username"] = ADMIN_USER,
1621- ["admin-password"] = ADMIN_PASSWORD,
1622- ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
1623- ["pid-file"] = PROXY_PIDFILE,
1624- ["proxy-lua-script"] = second_lua_script or DEFAULT_SCRIPT_FILENAME,
1625- ["plugin-dir"] = PROXY_LIBPATH,
1626- ["basedir"] = PROXY_TEST_BASEDIR,
1627- ["log-level"] = (VERBOSE == 3) and "debug" or "critical",
1628- }
1629- start_proxy('second_proxy',second_proxy_options)
1630-end
1631-
1632-local num_tests = 0
1633-local num_passes = 0
1634-local num_skipped = 0
1635-local num_fails = 0
1636-local all_ok = true
1637-local failed_test = {}
1638-
1639-file_empty(DEFAULT_SCRIPT_FILENAME)
1640-
1641---
1642--- if we have a argument, exectute the named test
1643--- otherwise execute all tests we can find
1644-if #arg then
1645- for i, a in ipairs(arg) do
1646- local stat = assert(lfs.attributes(a))
1647-
1648- if file_exists(a .. '/' .. tests_to_skip_filename) then
1649- assert(loadfile(a .. '/' .. tests_to_skip_filename))()
1650- end
1651-
1652- -- if it is a directory, execute all of them
1653- if stat.mode == "directory" then
1654- for file in lfs.dir(a .. "/t/") do
1655- if not TESTS_REGEX or file:match(TESTS_REGEX) then
1656- local testname = file:match("(.+\.test)$")
1657-
1658- if testname then
1659- print_verbose("# >> " .. testname .. " started")
1660-
1661- num_tests = num_tests + 1
1662- local r, skipped = run_test("t/" .. testname, a)
1663- if (r == 0) then
1664- num_passes = num_passes + 1 - skipped
1665- else
1666- num_fails = num_fails + 1
1667- all_ok = false
1668- table.insert(failed_test, testname)
1669- end
1670- num_skipped = num_skipped + skipped
1671-
1672- print_verbose("# << (exitcode = " .. r .. ")" )
1673-
1674- if r ~= 0 and exitcode == 0 then
1675- exitcode = r
1676- end
1677- end
1678- if all_ok == false and (not FORCE_ON_ERROR) then
1679- break
1680- end
1681- end
1682- end
1683- else
1684- -- otherwise just this one test
1685- --
1686- -- FIXME: base/ is hard-coded for now
1687- exitcode, skipped = run_test(a, "base/")
1688- num_skipped = num_skipped + skipped
1689- end
1690- end
1691-else
1692- for file in lfs.dir(srcdir .. "/t/") do
1693- local testname = file:match("(.+\.test)$")
1694-
1695- if testname then
1696- print_verbose("# >> " .. testname .. " started")
1697-
1698- num_tests = num_tests + 1
1699- local r, skipped = run_test("t/" .. testname)
1700- num_skipped = num_skipped + skipped
1701-
1702- print_verbose("# << (exitcode = " .. r .. ")" )
1703- if (r == 0) then
1704- num_passes = num_passes + 1 - skipped
1705- else
1706- all_ok = false
1707- num_fails = num_fails + 1
1708- table.insert(failed_test, testname)
1709- end
1710- if r ~= 0 and exitcode == 0 then
1711- exitcode = r
1712- end
1713- if all_ok == false and (not FORCE_ON_ERROR) then
1714- break
1715- end
1716- end
1717- end
1718-end
1719-
1720-if all_ok ==false then
1721- print ("*** ERRORS OCCURRED - The following tests failed")
1722- for i,v in pairs(failed_test) do
1723- print(v )
1724- end
1725-end
1726-
1727---
1728--- prints test suite statistics
1729-print_verbose (string.format('tests: %d - skipped: %d (%4.1f%%) - passed: %d (%4.1f%%) - failed: %d (%4.1f%%)',
1730- num_tests,
1731- num_skipped,
1732- num_skipped / num_tests * 100,
1733- num_passes,
1734- num_passes / (num_tests - num_skipped) * 100,
1735- num_fails,
1736- num_fails / (num_tests - num_skipped) * 100))
1737-
1738---
1739--- stops any remaining active proxy
1740---
1741-stop_proxy()
1742-
1743-if COVERAGE_LCOV then
1744- os_execute(COVERAGE_LCOV ..
1745- " --quiet " ..
1746- " --capture --directory ".. srcdir .. "/../src/" ..
1747- " > " .. srcdir .. "/../tests.coverage.info" )
1748-
1749- os_execute("genhtml " ..
1750- "--show-details " ..
1751- "--output-directory " .. srcdir .. "/../coverage/ " ..
1752- "--keep-descriptions " ..
1753- "--frames " ..
1754- srcdir .. "/../tests.coverage.info")
1755-end
1756-
1757+
1758+ if (ret == "ok") then
1759+ print(("ok # %s"):format(self.testname))
1760+ return 0
1761+ elseif (ret == "not ok") then
1762+ print(("not ok # %s"):format(self.testname))
1763+ return -1
1764+ else
1765+ print(("not ok # (%s) %s"):format(ret, self.testname))
1766+ return -1
1767+ end
1768+end
1769+
1770+local runner = TestRunner:new()
1771+runner:register_tests({"base"})
1772+local exitcode = runner:run_all()
1773
1774 if exitcode == 0 then
1775 os.exit(0)
1776 else
1777- print_verbose("mysql-test exit-code: " .. exitcode)
1778+ shellutils.print_verbose("mysql-test exit-code: " .. exitcode)
1779 os.exit(-1)
1780 end
1781
1782
1783=== added directory 'tests/suite/testenv'
1784=== added file 'tests/suite/testenv/Makefile.am'
1785--- tests/suite/testenv/Makefile.am 1970-01-01 00:00:00 +0000
1786+++ tests/suite/testenv/Makefile.am 2010-09-08 12:58:43 +0000
1787@@ -0,0 +1,7 @@
1788+EXTRA_DIST=\
1789+ fileutils.lua \
1790+ shellutils.lua \
1791+ testutils.lua \
1792+ process.lua \
1793+ processmanager.lua
1794+
1795
1796=== added file 'tests/suite/testenv/fileutils.lua'
1797--- tests/suite/testenv/fileutils.lua 1970-01-01 00:00:00 +0000
1798+++ tests/suite/testenv/fileutils.lua 2010-09-08 12:58:43 +0000
1799@@ -0,0 +1,84 @@
1800+--[[ $%BEGINLICENSE%$
1801+ Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
1802+
1803+ This program is free software; you can redistribute it and/or
1804+ modify it under the terms of the GNU General Public License as
1805+ published by the Free Software Foundation; version 2 of the
1806+ License.
1807+
1808+ This program is distributed in the hope that it will be useful,
1809+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1810+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1811+ GNU General Public License for more details.
1812+
1813+ You should have received a copy of the GNU General Public License
1814+ along with this program; if not, write to the Free Software
1815+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
1816+ 02110-1301 USA
1817+
1818+ $%ENDLICENSE%$ --]]
1819+
1820+require("lfs")
1821+
1822+module("testenv.fileutils", package.seeall)
1823+
1824+---
1825+-- check if the file exists and is readable
1826+function file_exists(f)
1827+ local r = lfs.attributes(f)
1828+ return (r ~= nil)
1829+end
1830+
1831+---
1832+-- check if a path is absolute
1833+--
1834+-- FIXME: right now it is Unix only
1835+function path_is_absolute(f)
1836+ return f:byte() == "/"
1837+end
1838+
1839+---
1840+-- get the directory-name of a path
1841+--
1842+-- @param filename path to create the directory name from
1843+function dirname(filename)
1844+ local dirname = filename
1845+
1846+ attr = assert(lfs.attributes(dirname))
1847+
1848+ if attr.mode == "directory" then
1849+ return dirname
1850+ end
1851+
1852+ dirname = filename:gsub("/[^/]+$", "")
1853+
1854+ attr = assert(lfs.attributes(dirname))
1855+
1856+ assert(attr.mode == "directory", "dirname("..filename..") failed: is ".. attr.mode)
1857+
1858+ return dirname
1859+end
1860+
1861+---
1862+-- get the file-name of a path
1863+--
1864+-- @param filename path to create the directory name from
1865+function basename(filename)
1866+ name = filename:gsub(".*/", "")
1867+
1868+ return name
1869+end
1870+
1871+---
1872+-- create a empty file
1873+--
1874+-- if the file exists, it will be truncated to 0
1875+--
1876+-- @param dst filename to create and truncate
1877+function create_empty_file(dst)
1878+ -- print_verbose("emptying " .. dst)
1879+ local dst_fd = assert(io.open(dst, "wb+"))
1880+ dst_fd:close();
1881+end
1882+
1883+
1884
1885=== added file 'tests/suite/testenv/process.lua'
1886--- tests/suite/testenv/process.lua 1970-01-01 00:00:00 +0000
1887+++ tests/suite/testenv/process.lua 2010-09-08 12:58:43 +0000
1888@@ -0,0 +1,179 @@
1889+--[[ $%BEGINLICENSE%$
1890+ Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
1891+
1892+ This program is free software; you can redistribute it and/or
1893+ modify it under the terms of the GNU General Public License as
1894+ published by the Free Software Foundation; version 2 of the
1895+ License.
1896+
1897+ This program is distributed in the hope that it will be useful,
1898+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1899+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1900+ GNU General Public License for more details.
1901+
1902+ You should have received a copy of the GNU General Public License
1903+ along with this program; if not, write to the Free Software
1904+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
1905+ 02110-1301 USA
1906+
1907+ $%ENDLICENSE%$ --]]
1908+
1909+local _G = _G
1910+
1911+local shellutils = require("testenv.shellutils")
1912+local glib2 = require("glib2")
1913+
1914+module("testenv.process")
1915+
1916+---
1917+-- life cycle management for processes
1918+--
1919+-- * PID files
1920+-- * wait until they are started
1921+-- * shutting down
1922+--
1923+function new(self, o)
1924+ -- create a new process object
1925+ --
1926+ o = o or {}
1927+ _G.setmetatable(o, self)
1928+ self.__index = self
1929+ return o
1930+end
1931+
1932+function get_pid(self)
1933+ return self.pid
1934+end
1935+
1936+function set_pid(self, pid)
1937+ self.pid = pid
1938+end
1939+
1940+---
1941+-- read a PID from a pidfile
1942+function set_pid_from_pidfile(self, pid_file_name)
1943+ local fh, errmsg = _G.io.open(pid_file_name, 'r')
1944+ if not fh then
1945+ return false, errmsg
1946+ end
1947+ local pid = fh:read("*n")
1948+ fh:close()
1949+
1950+ if _G.type(pid) ~= "number" then
1951+ return false
1952+ end
1953+ if not pid or pid == 0 then
1954+ return false
1955+ end
1956+
1957+ self:set_pid(pid)
1958+
1959+ return true
1960+end
1961+
1962+function is_running(self)
1963+ _G.assert(self.pid)
1964+
1965+ local ret = _G.os.execute("kill -0 ".. self.pid .." 2> /dev/null")
1966+
1967+ return (ret == 0)
1968+end
1969+
1970+---
1971+-- wait until the process is up and running
1972+--
1973+function wait_running(self, pid_file_name)
1974+ -- try to get a PID
1975+ local rounds = 0
1976+ local wait_interval_ms = 100
1977+
1978+ while not self.pid and rounds < 10 do
1979+ self:set_pid_from_pidfile(pid_file_name)
1980+ if self.pid then
1981+ break
1982+ end
1983+
1984+ glib2.usleep(wait_interval_ms * 1000) -- wait until process is gone
1985+ rounds = rounds + 1
1986+ shellutils.print_verbose(("process:wait_running(pid = %s) waited %dms"):format(pid_file_name, wait_interval_ms * rounds))
1987+ end
1988+
1989+ -- check if the process is actually alive
1990+ if not self.pid or not self:is_running() then
1991+ return false
1992+ end
1993+
1994+ return true
1995+end
1996+
1997+function wait_down(self)
1998+ _G.assert(self.pid)
1999+ local rounds = 0
2000+ local wait_interval_ms = 200
2001+
2002+ -- wait until the proc in the pid file is dead
2003+ -- the shutdown takes at about 500ms
2004+ while self:is_running() do
2005+ glib2.usleep(wait_interval_ms * 1000) -- wait until process is gone
2006+ rounds = rounds + 1
2007+ shellutils.print_verbose(("process:wait_down(pid = %d) waited %dms"):format(self.pid, wait_interval_ms * rounds))
2008+ end
2009+
2010+ return true
2011+end
2012+
2013+function shutdown(self)
2014+ -- shut down the proxy
2015+ --
2016+ -- win32 has tasklist and taskkill on the shell
2017+ if self.pid then
2018+ return _G.os.execute("kill -TERM ".. self.pid)
2019+ end
2020+end
2021+
2022+---
2023+-- execute a process
2024+function execute(self, cmd, env, opts)
2025+ local env_str = ""
2026+ local opts_str = ""
2027+
2028+ if env then
2029+ env_str = shellutils.env_tostring(env)
2030+ end
2031+
2032+ if opts then
2033+ opts_str = shellutils.options_tostring(opts)
2034+ end
2035+
2036+ local cmdline = env_str .. " " .. cmd .. " " .. opts_str
2037+ shellutils.print_verbose("$ " .. cmdline)
2038+ return _G.os.execute(cmdline)
2039+end
2040+
2041+function popen(self, cmd, env, opts)
2042+ local env_str = ""
2043+ local opts_str = ""
2044+
2045+ if env then
2046+ env_str = shellutils.env_tostring(env)
2047+ end
2048+
2049+ if opts then
2050+ opts_str = shellutils.options_tostring(opts)
2051+ end
2052+
2053+ local cmdline = env_str .. " " .. cmd .. " " .. opts_str
2054+ shellutils.print_verbose("$ " .. cmdline)
2055+
2056+ local fh = _G.io.popen(cmdline)
2057+ _G.assert(fh, 'error executing '.. cmdline)
2058+ local result = ''
2059+ local line = fh:read()
2060+ while line do
2061+ result = result .. line
2062+ line = fh:read()
2063+ end
2064+ fh:close()
2065+ return result
2066+end
2067+
2068
2069=== added file 'tests/suite/testenv/processmanager.lua'
2070--- tests/suite/testenv/processmanager.lua 1970-01-01 00:00:00 +0000
2071+++ tests/suite/testenv/processmanager.lua 2010-09-08 12:58:43 +0000
2072@@ -0,0 +1,72 @@
2073+--[[ $%BEGINLICENSE%$
2074+ Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2075+
2076+ This program is free software; you can redistribute it and/or
2077+ modify it under the terms of the GNU General Public License as
2078+ published by the Free Software Foundation; version 2 of the
2079+ License.
2080+
2081+ This program is distributed in the hope that it will be useful,
2082+ but WITHOUT ANY WARRANTY; without even the implied warranty of
2083+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2084+ GNU General Public License for more details.
2085+
2086+ You should have received a copy of the GNU General Public License
2087+ along with this program; if not, write to the Free Software
2088+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
2089+ 02110-1301 USA
2090+
2091+ $%ENDLICENSE%$ --]]
2092+
2093+---
2094+-- manage a set of processes
2095+--
2096+-- basicly, all we do is "add a process to the manager" and "shut them all down"
2097+--
2098+
2099+local _G = _G
2100+
2101+module("testenv.processmanager")
2102+
2103+---
2104+-- init a new manager
2105+--
2106+function new(self, o)
2107+ -- create a new processes object
2108+ --
2109+ o = o or {}
2110+ _G.setmetatable(o, self)
2111+ self.__index = self
2112+
2113+ self.processes = { }
2114+
2115+ return o
2116+end
2117+
2118+---
2119+-- add a process
2120+function add(self, proc)
2121+ _G.table.insert(self.processes, proc)
2122+end
2123+
2124+---
2125+-- stop all monitored processes
2126+--
2127+function shutdown_all(self)
2128+ -- shuts down every proxy in the proxy list
2129+ --
2130+ for proc_name, proc in _G.pairs(self.processes) do
2131+ if proc.pid then
2132+ proc:shutdown()
2133+ end
2134+ end
2135+
2136+ for proc_name, proc in _G.pairs(self.processes) do
2137+ if proc.pid then
2138+ proc:wait_down()
2139+ end
2140+ self.processes[proc_name] = nil
2141+ end
2142+end
2143+
2144+
2145
2146=== added file 'tests/suite/testenv/shellutils.lua'
2147--- tests/suite/testenv/shellutils.lua 1970-01-01 00:00:00 +0000
2148+++ tests/suite/testenv/shellutils.lua 2010-09-08 12:58:43 +0000
2149@@ -0,0 +1,92 @@
2150+--[[ $%BEGINLICENSE%$
2151+ Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2152+
2153+ This program is free software; you can redistribute it and/or
2154+ modify it under the terms of the GNU General Public License as
2155+ published by the Free Software Foundation; version 2 of the
2156+ License.
2157+
2158+ This program is distributed in the hope that it will be useful,
2159+ but WITHOUT ANY WARRANTY; without even the implied warranty of
2160+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2161+ GNU General Public License for more details.
2162+
2163+ You should have received a copy of the GNU General Public License
2164+ along with this program; if not, write to the Free Software
2165+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
2166+ 02110-1301 USA
2167+
2168+ $%ENDLICENSE%$ --]]
2169+
2170+module("testenv.shellutils", package.seeall)
2171+
2172+VERBOSE = tonumber(os.getenv("VERBOSE")) or 0
2173+
2174+---
2175+-- turn a option-table into a string
2176+--
2177+-- the values are encoded and quoted for the shell
2178+--
2179+-- @param tbl a option table
2180+-- @param sep the seperator, defaults to a space
2181+function options_tostring(tbl, sep)
2182+ -- default value for sep
2183+ sep = sep or " "
2184+
2185+ assert(type(tbl) == "table")
2186+ assert(type(sep) == "string")
2187+
2188+ local s_tbl = { }
2189+ for k, v in pairs(tbl) do
2190+ local values
2191+ -- if the value is a table, repeat the option
2192+ if type(v) == "table" then
2193+ values = v
2194+ else
2195+ values = { v }
2196+ end
2197+
2198+ for tk, tv in pairs(values) do
2199+ if tv == true then
2200+ table.insert(s_tbl, "--" .. k)
2201+ else
2202+ local enc_value = tv:gsub("\\", "\\\\"):gsub("\"", "\\\"")
2203+ table.insert(s_tbl, "--" .. k .. "=\"" .. enc_value .. "\"")
2204+ end
2205+ end
2206+ end
2207+ local s = table.concat(s_tbl, " ")
2208+ -- print_verbose(" option: " .. s)
2209+
2210+ return s
2211+end
2212+
2213+--- turns an option table into a string of environment variables
2214+--
2215+function env_tostring(tbl)
2216+ assert(type(tbl) == "table")
2217+
2218+ local s = ""
2219+ for k, v in pairs(tbl) do
2220+ local enc_value = v:gsub("\\", "\\\\"):gsub("\"", "\\\"")
2221+ s = s .. k .. "=\"" .. enc_value .. "\" "
2222+ end
2223+
2224+ return s
2225+end
2226+
2227+---
2228+-- print_verbose()
2229+--
2230+-- prints a message if either the DEBUG or VERBOSE variables
2231+-- are set.
2232+--
2233+-- @param msg the message being printed
2234+-- @param min_level the minimum verbosity level for printing the message (default 1)
2235+function print_verbose(msg, min_level)
2236+ min_level = min_level or 1
2237+ if (VERBOSE >= min_level) then
2238+ print (msg)
2239+ end
2240+end
2241+
2242
2243=== added file 'tests/suite/testenv/testutils.lua'
2244--- tests/suite/testenv/testutils.lua 1970-01-01 00:00:00 +0000
2245+++ tests/suite/testenv/testutils.lua 2010-09-08 12:58:43 +0000
2246@@ -0,0 +1,271 @@
2247+--[[ $%BEGINLICENSE%$
2248+ Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2249+
2250+ This program is free software; you can redistribute it and/or
2251+ modify it under the terms of the GNU General Public License as
2252+ published by the Free Software Foundation; version 2 of the
2253+ License.
2254+
2255+ This program is distributed in the hope that it will be useful,
2256+ but WITHOUT ANY WARRANTY; without even the implied warranty of
2257+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2258+ GNU General Public License for more details.
2259+
2260+ You should have received a copy of the GNU General Public License
2261+ along with this program; if not, write to the Free Software
2262+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
2263+ 02110-1301 USA
2264+
2265+ $%ENDLICENSE%$ --]]
2266+
2267+module("testenv.testutils", package.seeall)
2268+
2269+--
2270+-- Global variables that can be referenced from .options files
2271+--
2272+function get_port_base(port_base_start, port_base_end)
2273+ local port_base_start = port_base_start or 32768
2274+ local port_base_end = port_base_end or 65535
2275+ local port_interval = 64 -- let's take the base port in steps of ...
2276+ local range = port_base_end - port_base_start - port_interval
2277+ math.randomseed(posix.getpid())
2278+ local rand = math.floor(math.random() * (math.ceil(range / port_interval)))
2279+ local port_base = port_base_start + (rand * port_interval)
2280+
2281+ print(("... using tcp-port = %d as start port"):format(port_base))
2282+
2283+ return port_base
2284+end
2285+
2286+
2287+-- setting the include path
2288+--
2289+
2290+-- this is the path containing the global Lua modules
2291+local GLOBAL_LUA_PATH = os.getenv('LUA_LDIR') or '/usr/share/lua/5.1/?.lua'
2292+
2293+-- this is the path containing the Proxy libraries
2294+local PROXY_LUA_PATH = os.getenv('LUA_PATH') or '/usr/local/share/?.lua'
2295+
2296+-- This is the path with specific libraries for the test suite
2297+local PRIVATE_LUA_PATH = arg[1] .. '/t/?.lua'
2298+
2299+-- This is the path with additional libraries that the user needs
2300+local LUA_USER_PATH = os.getenv('LUA_USER_PATH') or '../lib/?.lua'
2301+
2302+-- Building the final include path
2303+local INCLUDE_PATH =
2304+ LUA_USER_PATH .. ';' ..
2305+ PRIVATE_LUA_PATH .. ';' ..
2306+ GLOBAL_LUA_PATH .. ';' ..
2307+ PROXY_LUA_PATH
2308+
2309+---
2310+-- start_proxy()
2311+--
2312+-- starts an instance of MySQL Proxy
2313+--
2314+-- @param proxy_name internal name of the proxy instance, for retrieval
2315+-- @param proxy_options the options to start the Proxy
2316+function start_proxy(proxy_name, proxy_options)
2317+ -- start the proxy
2318+ assert(type(proxy_options) == 'table')
2319+ if not file_exists(proxy_options['proxy-lua-script']) then
2320+ proxy_options['proxy-lua-script'] =
2321+ global_basedir ..
2322+ '/t/' .. proxy_options['proxy-lua-script']
2323+ end
2324+ print_verbose("starting " .. proxy_name .. " with " .. options_tostring(proxy_options))
2325+
2326+ -- remove the old pid-file if it exists
2327+ os.remove(proxy_options['pid-file'])
2328+ -- if we are supposed to listen on a UNIX socket, remove it first, because we don't clean it up on exit!
2329+ -- TODO: fix the code, instead of hacking around here! Bug#38415
2330+ if proxy_options['proxy-address'] == '/tmp/mysql-proxy-test.sock' then
2331+ os.remove(proxy_options['proxy-address'])
2332+ end
2333+ -- os.execute("head " .. proxy_options['proxy-lua-script'])
2334+ assert(os.execute( 'LUA_PATH="' .. INCLUDE_PATH .. '" ' ..
2335+ PROXY_TRACE .. " " .. PROXY_BINPATH .. " " ..
2336+ options_tostring( proxy_options) .. " &"
2337+ ))
2338+
2339+ -- wait until the proxy is up
2340+ proc = Process.new()
2341+ proc.set_pid_from_pidfile(proxy_options['pid-file'])
2342+
2343+ procs[proxy_name] = proc
2344+end
2345+
2346+---
2347+-- simulate_replication()
2348+--
2349+-- creates a fake master/slave by having two proxies
2350+-- pointing at the same backend
2351+--
2352+-- you can alter those backends by changing
2353+-- the starting parameters
2354+--
2355+-- @param master_options options for master
2356+-- @param slave_options options for slave
2357+function simulate_replication(master_options, slave_options)
2358+ if not master_options then
2359+ master_options = default_master_options
2360+ end
2361+ if not master_options['pid-file'] then
2362+ master_options['pid-file'] = PROXY_MASTER_PIDFILE
2363+ end
2364+ if not slave_options then
2365+ slave_options = default_slave_options
2366+ end
2367+ if not slave_options['pid-file'] then
2368+ slave_options['pid-file'] = PROXY_SLAVE_PIDFILE
2369+ end
2370+ start_proxy('master', master_options)
2371+ start_proxy('slave', slave_options)
2372+end
2373+
2374+---
2375+-- chain_proxy()
2376+--
2377+-- starts two proxy instances, with the first one is pointing to the
2378+-- default backend server, and the second one (with default ports)
2379+-- is pointing at the first proxy
2380+--
2381+-- client -> proxy -> backend_proxy -> [ mysql-backend ]
2382+--
2383+-- usually we use it to mock a server with a lua script (...-mock.lua) and
2384+-- the script under test in the proxy (...-test.lua)
2385+--
2386+-- in case you want to start several backend_proxies, just provide a array
2387+-- as first param
2388+--
2389+-- @param backend_lua_script
2390+-- @param second_lua_script
2391+-- @param use_replication uses a master proxy as backend
2392+function chain_proxy (backend_lua_scripts, second_lua_script, use_replication)
2393+ local backends = { }
2394+
2395+ if type(backend_lua_scripts) == "table" then
2396+ backends = backend_lua_scripts
2397+ else
2398+ backends = { backend_lua_scripts }
2399+ end
2400+
2401+ local backend_addresses = { }
2402+
2403+ for i, backend_lua_script in ipairs(backends) do
2404+ backend_addresses[i] = PROXY_HOST .. ":" .. (PROXY_CHAIN_PORT + i - 1)
2405+
2406+ backend_proxy_options = {
2407+ ["proxy-backend-addresses"] = MYSQL_HOST .. ":" .. MYSQL_PORT,
2408+ ["proxy-address"] = backend_addresses[i],
2409+ ["admin-address"] = PROXY_HOST .. ":" .. (ADMIN_CHAIN_PORT + i - 1),
2410+ ["admin-username"] = ADMIN_USER,
2411+ ["admin-password"] = ADMIN_PASSWORD,
2412+ ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
2413+ ["pid-file"] = PROXY_CHAIN_PIDFILE .. i,
2414+ ["proxy-lua-script"] = backend_lua_script or DEFAULT_SCRIPT_FILENAME,
2415+ ["plugin-dir"] = PROXY_LIBPATH,
2416+ ["basedir"] = PROXY_TEST_BASEDIR,
2417+ ["log-level"] = (VERBOSE == 4) and "debug" or "critical",
2418+ }
2419+ --
2420+ -- if replication was not started, then it is started here
2421+ --
2422+ if use_replication and (use_replication == true) then
2423+ if (proxy_list['master'] == nil) then
2424+ simulate_replication()
2425+ end
2426+ backend_proxy_options["proxy-backend-addresses"] = PROXY_HOST .. ':' .. PROXY_MASTER_PORT
2427+ end
2428+ start_proxy('backend_proxy' .. i, backend_proxy_options)
2429+ end
2430+
2431+ second_proxy_options = {
2432+ ["proxy-backend-addresses"] = backend_addresses ,
2433+ ["proxy-address"] = PROXY_HOST .. ":" .. PROXY_PORT,
2434+ ["admin-address"] = PROXY_HOST .. ":" .. ADMIN_PORT,
2435+ ["admin-username"] = ADMIN_USER,
2436+ ["admin-password"] = ADMIN_PASSWORD,
2437+ ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
2438+ ["pid-file"] = PROXY_PIDFILE,
2439+ ["proxy-lua-script"] = second_lua_script or DEFAULT_SCRIPT_FILENAME,
2440+ ["plugin-dir"] = PROXY_LIBPATH,
2441+ ["basedir"] = PROXY_TEST_BASEDIR,
2442+ ["log-level"] = (VERBOSE == 3) and "debug" or "critical",
2443+ }
2444+ start_proxy('second_proxy',second_proxy_options)
2445+end
2446+
2447+
2448+---
2449+--sql_execute()
2450+--
2451+-- Executes a SQL query in a given Proxy
2452+--
2453+-- If no Proxy is indicated, the query is passed directly to the backend server
2454+--
2455+-- @param query A SQL statement to execute, or a table of SQL statements
2456+-- @param proxy_name the name of the proxy that executes the query
2457+function sql_execute(queries, proxy_name)
2458+ local ret = 0
2459+ assert(type(queries) == 'string' or type(queries) == 'table', 'invalid type for query' )
2460+ if type(queries) == 'string' then
2461+ queries = {queries}
2462+ end
2463+ local query = ''
2464+ for i, q in pairs(queries) do
2465+ query = query .. ';' .. q
2466+ end
2467+
2468+ if proxy_name then
2469+ --
2470+ -- a Proxy name is passed.
2471+ -- The query is executed with the given proxy
2472+ local opts = proxy_list[proxy_name]
2473+ assert(opts,'proxy '.. proxy_name .. ' not active')
2474+ assert(opts['proxy-address'],'address for proxy '.. proxy_name .. ' not found')
2475+ local p_host, p_port = opts['proxy-address']:match('(%S+):(%S+)')
2476+ ret = os_execute( MYSQL_CLIENT_BIN .. ' ' ..
2477+ options_tostring({
2478+ user = MYSQL_USER,
2479+ password = MYSQL_PASSWORD,
2480+ database = MYSQL_DB,
2481+ host = p_host,
2482+ port = p_port,
2483+ execute = query
2484+ })
2485+ )
2486+ assert(ret == 0, 'error using mysql client ')
2487+ else
2488+ --
2489+ -- No proxy name was passed.
2490+ -- The query is executed in the backend server
2491+ --
2492+ ret = os_execute( MYSQL_CLIENT_BIN .. ' ' ..
2493+ options_tostring({
2494+ user = MYSQL_USER,
2495+ password = MYSQL_PASSWORD,
2496+ database = MYSQL_DB,
2497+ host = PROXY_HOST,
2498+ port = MYSQL_PORT,
2499+ execute = query
2500+ })
2501+ )
2502+ end
2503+ return ret
2504+end
2505+
2506+function only_item ( tbl, item)
2507+ local exists = false
2508+ for i,v in pairs(tbl) do
2509+ if i == item then
2510+ exists = true
2511+ else
2512+ return false
2513+ end
2514+ end
2515+ return exists
2516+end
2517+

Subscribers

People subscribed via source and target branches