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

Proposed by Jan Kneschke
Status: Superseded
Proposed branch: lp:~jan-kneschke/mysql-proxy/test-suite-refactoring
Merge into: lp:mysql-proxy
Diff against target: 2453 lines (+1242/-922)
19 files modified
configure.in (+1/-0)
lib/glib2.c (+11/-0)
lib/posix.c (+10/-0)
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 (+13/-0)
tests/suite/base/t/lineno.test (+4/-0)
tests/suite/base/t/load_multi.test (+8/-8)
tests/suite/base/t/pooling-mock.lua (+7/-23)
tests/suite/base/t/pooling.test (+18/-10)
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) Needs Fixing
Registry Administrators Pending
Review via email: mp+34720@code.launchpad.net

This proposal has been superseded by a proposal from 2010-09-07.

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 :

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 :

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

1133. By <email address hidden>

added license headers and fixed typos

1134. By <email address hidden>

removed some dead code and documented the 'test_self' hack

1135. By <email address hidden>

comment cosmetics

1136. By <email address hidden>

updated year of copyright for the files changed

1137. By <email address hidden>

stripped the intermediate years as the form is $start, $end

Unmerged revisions

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

Subscribers

People subscribed via source and target branches