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
=== modified file 'configure.in'
--- configure.in 2010-05-25 13:44:43 +0000
+++ configure.in 2010-09-08 12:58:43 +0000
@@ -407,6 +407,7 @@
407 lib/proxy/Makefile \407 lib/proxy/Makefile \
408 tests/Makefile \408 tests/Makefile \
409 tests/suite/Makefile \409 tests/suite/Makefile \
410 tests/suite/testenv/Makefile \
410 tests/suite/base/Makefile \411 tests/suite/base/Makefile \
411 tests/suite/base/t/Makefile \412 tests/suite/base/t/Makefile \
412 tests/suite/base/r/Makefile \413 tests/suite/base/r/Makefile \
413414
=== modified file 'lib/glib2.c'
--- lib/glib2.c 2010-08-20 15:02:44 +0000
+++ lib/glib2.c 2010-09-08 12:58:43 +0000
@@ -1,5 +1,5 @@
1/* $%BEGINLICENSE%$1/* $%BEGINLICENSE%$
2 Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.2 Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
33
4 This program is free software; you can redistribute it and/or4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as5 modify it under the terms of the GNU General Public License as
@@ -33,6 +33,16 @@
33 return 0;33 return 0;
34}34}
3535
36static int lua_g_setenv (lua_State *L) {
37 const char * key = luaL_checkstring (L, 1);
38 const char * value = luaL_checkstring (L, 2);
39 int overwrite = luaL_optint(L, 3, 1);
40
41 lua_pushboolean(L, g_setenv(key, value, overwrite));
42
43 return 1;
44}
45
36static int lua_g_get_current_time (lua_State *L) {46static int lua_g_get_current_time (lua_State *L) {
37 GTimeVal t;47 GTimeVal t;
3848
@@ -83,6 +93,7 @@
83 {"usleep", lua_g_usleep},93 {"usleep", lua_g_usleep},
84 {"md5", lua_g_checksum_md5},94 {"md5", lua_g_checksum_md5},
85 {"get_current_time", lua_g_get_current_time},95 {"get_current_time", lua_g_get_current_time},
96 {"setenv", lua_g_setenv},
86 {NULL, NULL},97 {NULL, NULL},
87};98};
8899
89100
=== modified file 'lib/posix.c'
--- lib/posix.c 2008-11-11 17:09:04 +0000
+++ lib/posix.c 2010-09-08 12:58:43 +0000
@@ -1,9 +1,28 @@
1/* Copyright (C) 2008 MySQL AB */1/* $%BEGINLICENSE%$
2 Copyright (c) 2008, 2010 Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; version 2 of the
7 License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 02110-1301 USA
18
19 $%ENDLICENSE%$ */
220
3#ifdef HAVE_CONFIG_H21#ifdef HAVE_CONFIG_H
4#include "config.h"22#include "config.h"
5#endif23#endif
624
25#include <sys/param.h>
7#include <sys/types.h>26#include <sys/types.h>
8#include <unistd.h>27#include <unistd.h>
928
@@ -27,6 +46,14 @@
27 return 1;46 return 1;
28}47}
2948
49static int lua_getcwd (lua_State *L) {
50 char cwd[MAXPATHLEN];
51
52 lua_pushstring (L, getcwd(cwd, sizeof(cwd)));
53
54 return 1;
55}
56
30static int lua_getpwuid(lua_State *L) {57static int lua_getpwuid(lua_State *L) {
31 struct passwd *p;58 struct passwd *p;
32 int uid = luaL_checkinteger (L, 1);59 int uid = luaL_checkinteger (L, 1);
@@ -76,6 +103,7 @@
76 {"getpid", lua_getpid},103 {"getpid", lua_getpid},
77 {"getuid", lua_getuid},104 {"getuid", lua_getuid},
78 {"getpwuid", lua_getpwuid},105 {"getpwuid", lua_getpwuid},
106 {"getcwd", lua_getcwd},
79 {NULL, NULL},107 {NULL, NULL},
80};108};
81109
82110
=== modified file 'src/mysql-proxy-cli.c'
--- src/mysql-proxy-cli.c 2010-09-02 12:05:35 +0000
+++ src/mysql-proxy-cli.c 2010-09-08 12:58:43 +0000
@@ -56,6 +56,7 @@
56#endif56#endif
5757
58#include <glib.h>58#include <glib.h>
59#include <glib/gstdio.h> /* g_unlink() */
59#include <gmodule.h>60#include <gmodule.h>
6061
61#ifdef HAVE_LUA_H62#ifdef HAVE_LUA_H
@@ -115,6 +116,7 @@
115 GOptionEntry *config_entries;116 GOptionEntry *config_entries;
116117
117 gchar *pid_file;118 gchar *pid_file;
119 gboolean pid_file_is_created;
118120
119 gchar *plugin_dir;121 gchar *plugin_dir;
120 gchar **plugin_names;122 gchar **plugin_names;
@@ -166,7 +168,13 @@
166 if (frontend->user) g_free(frontend->user);168 if (frontend->user) g_free(frontend->user);
167 if (frontend->log_filename) g_free(frontend->log_filename);169 if (frontend->log_filename) g_free(frontend->log_filename);
168 if (frontend->log_config_filename) g_free(frontend->log_config_filename);170 if (frontend->log_config_filename) g_free(frontend->log_config_filename);
169 if (frontend->pid_file) g_free(frontend->pid_file);171 if (frontend->pid_file) {
172 /* only try to delete the PID if we created it */
173 if (frontend->pid_file_is_created) {
174 g_unlink(frontend->pid_file);
175 }
176 g_free(frontend->pid_file);
177 }
170 if (frontend->log_level) g_free(frontend->log_level);178 if (frontend->log_level) g_free(frontend->log_level);
171 if (frontend->plugin_dir) g_free(frontend->plugin_dir);179 if (frontend->plugin_dir) g_free(frontend->plugin_dir);
172180
@@ -591,6 +599,7 @@
591599
592 GOTO_EXIT(EXIT_FAILURE);600 GOTO_EXIT(EXIT_FAILURE);
593 }601 }
602 frontend->pid_file_is_created = TRUE; /* track that we created the PID file successfully and can delete it */
594 }603 }
595604
596 /* the message has to be _after_ the g_option_content_parse() to 605 /* the message has to be _after_ the g_option_content_parse() to
597606
=== modified file 'tests/suite/Makefile.am'
--- tests/suite/Makefile.am 2009-09-22 15:13:32 +0000
+++ tests/suite/Makefile.am 2010-09-08 12:58:43 +0000
@@ -1,8 +1,9 @@
1SUBDIRS=base bugs1SUBDIRS=testenv base bugs
22
3TESTS_ENVIRONMENT = \3TESTS_ENVIRONMENT = \
4 MYSQL_TEST_BIN="${MYSQL_TEST_BIN}" \4 MYSQL_TEST_BIN="${MYSQL_TEST_BIN}" \
5 LUA_USER_PATH="${top_srcdir}/lib/?.lua" \5 LUA_USER_PATH="${top_srcdir}/lib/?.lua" \
6 LUA_PATH="${top_srcdir}/tests/suite/?.lua" \
6 LUA_CPATH=".libs/?.@DYNLIB_LUA_SUFFIX@;${top_builddir}/lib/.libs/?.@DYNLIB_LUA_SUFFIX@" \7 LUA_CPATH=".libs/?.@DYNLIB_LUA_SUFFIX@;${top_builddir}/lib/.libs/?.@DYNLIB_LUA_SUFFIX@" \
7 @DYNLIB_PATH_VAR@="${top_builddir}/src/.libs/:${@DYNLIB_PATH_VAR@}" \8 @DYNLIB_PATH_VAR@="${top_builddir}/src/.libs/:${@DYNLIB_PATH_VAR@}" \
8 MYSQL_PROXY_VERSION="${PACKAGE_VERSION}" \9 MYSQL_PROXY_VERSION="${PACKAGE_VERSION}" \
@@ -24,6 +25,9 @@
2425
25EXTRA_DIST=\26EXTRA_DIST=\
26 run-tests.lua \27 run-tests.lua \
28 fileutils.lua \
29 shellutils.lua \
30 testutils.lua \
27 lua-tests-wrapper.sh.in \31 lua-tests-wrapper.sh.in \
28 CMakeLists.txt32 CMakeLists.txt
2933
3034
=== modified file 'tests/suite/base/r/lineno.result'
--- tests/suite/base/r/lineno.result 2009-09-21 14:34:47 +0000
+++ tests/suite/base/r/lineno.result 2010-09-08 12:58:43 +0000
@@ -1,4 +1,4 @@
1SELECT backtrace;1SELECT backtrace;
2backtrace2backtrace
3stack traceback:3stack traceback:
4 [string "/tmp/dummy.lua"]:48: in function <[string "/tmp/dummy.lua"]:32>4 [string "lineno.lua"]:48: in function <[string "lineno.lua"]:32>
55
=== modified file 'tests/suite/base/r/pooling.result'
--- tests/suite/base/r/pooling.result 2008-07-03 15:20:35 +0000
+++ tests/suite/base/r/pooling.result 2010-09-08 12:58:43 +0000
@@ -1,15 +1,18 @@
1SELECT counter;1SELECT conn_id, stmt_id;
2conn_id stmt_id2conn_id stmt_id
32 132 1
4SELECT counter;4SELECT conn_id, stmt_id;
5conn_id stmt_id
61 1
7SELECT counter;
8conn_id stmt_id
91 2
10SELECT counter;
11conn_id stmt_id5conn_id stmt_id
122 262 2
13SELECT counter;7SELECT conn_id, stmt_id;
8conn_id stmt_id
93 1
10SELECT conn_id, stmt_id;
11conn_id stmt_id
122 3
13SELECT conn_id, stmt_id;
14conn_id stmt_id
154 1
16SELECT conn_id, stmt_id;
14conn_id stmt_id17conn_id stmt_id
155 1185 1
1619
=== modified file 'tests/suite/base/t/lineno.lua'
--- tests/suite/base/t/lineno.lua 2010-04-06 15:25:06 +0000
+++ tests/suite/base/t/lineno.lua 2010-09-08 12:58:43 +0000
@@ -1,5 +1,5 @@
1--[[ 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.1--[[ 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.
2 Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.2 Copyright (c) 2009, 2010 Oracle and/or its affiliates. All rights reserved.
33
4 This program is free software; you can redistribute it and/or modify4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by5 it under the terms of the GNU General Public License as published by
@@ -50,6 +50,19 @@
5050
51 }51 }
52 }52 }
53 elseif packet:sub(2) == "SELECT current_backtrace_filename" then
54 proxy.response = {
55 type = proxy.MYSQLD_PACKET_OK,
56 resultset = {
57 fields = {
58 { name = "filename", type = proxy.MYSQL_TYPE_STRING }
59 },
60 rows = {
61 { debug.getinfo(1, "S").short_src }
62 }
63
64 }
65 }
53 else66 else
54 proxy.response = {67 proxy.response = {
55 type = proxy.MYSQLD_PACKET_ERR,68 type = proxy.MYSQLD_PACKET_ERR,
5669
=== modified file 'tests/suite/base/t/lineno.test'
--- tests/suite/base/t/lineno.test 2010-04-06 14:26:51 +0000
+++ tests/suite/base/t/lineno.test 2010-09-08 12:58:43 +0000
@@ -1,5 +1,5 @@
1# $%BEGINLICENSE%$1# $%BEGINLICENSE%$
2# Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.2# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
3# 3#
4# This program is free software; you can redistribute it and/or4# This program is free software; you can redistribute it and/or
5# modify it under the terms of the GNU General Public License as5# modify it under the terms of the GNU General Public License as
@@ -17,4 +17,8 @@
17# 02110-1301 USA17# 02110-1301 USA
18# 18#
19# $%ENDLICENSE%$19# $%ENDLICENSE%$
20
21# fetch the Lua version of the current file-name
22--let current_backtrace_filename = query_get_value("SELECT current_backtrace_filename", "filename", 1)
23--replace_result $current_backtrace_filename "[string \"lineno.lua\"]"
20SELECT backtrace;24SELECT backtrace;
2125
=== modified file 'tests/suite/base/t/load_multi.test'
--- tests/suite/base/t/load_multi.test 2010-04-06 14:26:51 +0000
+++ tests/suite/base/t/load_multi.test 2010-09-08 12:58:43 +0000
@@ -1,5 +1,5 @@
1# $%BEGINLICENSE%$1# $%BEGINLICENSE%$
2# Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.2# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3# 3#
4# This program is free software; you can redistribute it and/or4# This program is free software; you can redistribute it and/or
5# modify it under the terms of the GNU General Public License as5# modify it under the terms of the GNU General Public License as
@@ -18,14 +18,14 @@
18# 18#
19# $%ENDLICENSE%$19# $%ENDLICENSE%$
20--disable_query_log20--disable_query_log
21--replace_result $srcdir <srcdir>21--replace_result $abs_srcdir <srcdir>
22--eval pload $srcdir/base/t/lm2.lua22--eval pload $abs_srcdir/base/t/lm2.lua
23--enable_query_log23--enable_query_log
24connect (conn1,127.0.0.1,$MYSQL_USER,$MYSQL_PASSWORD,test,$PROXY_PORT,);24connect (conn1,127.0.0.1,$MYSQL_USER,$MYSQL_PASSWORD,test,$PROXY_PORT,);
25select pload status;25select pload status;
26--disable_query_log26--disable_query_log
27--replace_result $srcdir <srcdir>27--replace_result $abs_srcdir <srcdir>
28--eval pload $srcdir/base/t/lm1.lua28--eval pload $abs_srcdir/base/t/lm1.lua
29--enable_query_log29--enable_query_log
30connection conn1;30connection conn1;
31select 'first' as info;31select 'first' as info;
@@ -44,13 +44,13 @@
44disconnect conn1;44disconnect conn1;
45disconnect conn2;45disconnect conn2;
46--disable_query_log46--disable_query_log
47--replace_result $srcdir <srcdir>47--replace_result $abs_srcdir <srcdir>
48--eval pload $srcdir/base/t/lm3.lua48--eval pload $abs_srcdir/base/t/lm3.lua
49--enable_query_log49--enable_query_log
50select 1000;50select 1000;
51--disable_query_log51--disable_query_log
52--replace_result $srcdir <srcdir>52--replace_result $abs_srcdir <srcdir>
53--eval punload $srcdir/base/t/lm3.lua53--eval punload $abs_srcdir/base/t/lm3.lua
54--enable_query_log54--enable_query_log
55select 1000;55select 1000;
5656
5757
=== modified file 'tests/suite/base/t/pooling-mock.lua'
--- tests/suite/base/t/pooling-mock.lua 2010-04-06 14:26:51 +0000
+++ tests/suite/base/t/pooling-mock.lua 2010-09-08 12:58:43 +0000
@@ -1,5 +1,5 @@
1--[[ $%BEGINLICENSE%$1--[[ $%BEGINLICENSE%$
2 Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.2 Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
33
4 This program is free software; you can redistribute it and/or4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as5 modify it under the terms of the GNU General Public License as
@@ -23,24 +23,8 @@
23-- by comparing the statement-ids and the connection ids we can 23-- by comparing the statement-ids and the connection ids we can
24-- track if the ro-pooling script was reusing a connection24-- track if the ro-pooling script was reusing a connection
25--25--
26function packet_auth(fields)26
27 fields = fields or { }27local proto = require("mysql.proto")
28 return "\010" .. -- proto version
29 (fields.version or "5.0.45-proxy") .. -- version
30 "\000" .. -- term-null
31 "\001\000\000\000" .. -- thread-id
32 "\065\065\065\065" ..
33 "\065\065\065\065" .. -- challenge - part I
34 "\000" .. -- filler
35 "\001\130" .. -- server cap (long pass, 4.1 proto)
36 "\008" .. -- charset
37 "\002\000" .. -- status
38 ("\000"):rep(13) .. -- filler
39 "\065\065\065\065"..
40 "\065\065\065\065"..
41 "\065\065\065\065"..
42 "\000" -- challenge - part II
43end
4428
45-- will be called once after connect29-- will be called once after connect
46stmt_id = 030stmt_id = 0
@@ -60,7 +44,7 @@
60 proxy.response = {44 proxy.response = {
61 type = proxy.MYSQLD_PACKET_RAW,45 type = proxy.MYSQLD_PACKET_RAW,
62 packets = {46 packets = {
63 packet_auth()47 proto.to_challenge_packet({})
64 }48 }
65 }49 }
66 return proxy.PROXY_SEND_RESULT50 return proxy.PROXY_SEND_RESULT
@@ -78,15 +62,15 @@
78 stmt_id = stmt_id + 162 stmt_id = stmt_id + 1
7963
80 local query = packet:sub(2) 64 local query = packet:sub(2)
81 if query == 'SELECT counter' then65 if query == 'SELECT conn_id, stmt_id' then
82 proxy.response = {66 proxy.response = {
83 type = proxy.MYSQLD_PACKET_OK,67 type = proxy.MYSQLD_PACKET_OK,
84 resultset = {68 resultset = {
85 fields = {69 fields = {
86 { name = 'conn_id' },70 { name = 'conn_id', type = proxy.MYSQLD_TYPE_INT },
87 { name = 'stmt_id' },71 { name = 'stmt_id', type = proxy.MYSQLD_TYPE_INT },
88 },72 },
89 rows = { { tostring(conn_id), tostring(stmt_id) } }73 rows = { { conn_id, stmt_id } }
90 }74 }
91 }75 }
92 else76 else
9377
=== modified file 'tests/suite/base/t/pooling.test'
--- tests/suite/base/t/pooling.test 2010-04-06 14:26:51 +0000
+++ tests/suite/base/t/pooling.test 2010-09-08 12:58:43 +0000
@@ -1,5 +1,5 @@
1# $%BEGINLICENSE%$1# $%BEGINLICENSE%$
2# Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.2# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
3# 3#
4# This program is free software; you can redistribute it and/or4# This program is free software; you can redistribute it and/or
5# modify it under the terms of the GNU General Public License as5# modify it under the terms of the GNU General Public License as
@@ -17,22 +17,30 @@
17# 02110-1301 USA17# 02110-1301 USA
18# 18#
19# $%ENDLICENSE%$19# $%ENDLICENSE%$
20connect (conn1,127.0.0.1,root,,,$PROXY_PORT); 20
21SELECT counter;21# open a few connections and test if they reuse connections that
22# end up in the pool
23
24connect (conn1,$PROXY_HOST,root,,,$PROXY_PORT);
25SELECT conn_id, stmt_id;
22disconnect conn1;26disconnect conn1;
2327
24connect (conn2,127.0.0.1,root,,,$PROXY_PORT); 28connect (conn2,$PROXY_HOST,root,,,$PROXY_PORT);
25SELECT counter;29SELECT conn_id, stmt_id;
26disconnect conn2;30disconnect conn2;
2731
28connect (conn3,127.0.0.1,root,,,$PROXY_PORT); 32connect (conn3,$PROXY_HOST,root,,,$PROXY_PORT);
29SELECT counter;33SELECT conn_id, stmt_id;
30disconnect conn3;34disconnect conn3;
3135
32connect (conn4,127.0.0.1,root,,,$PROXY_PORT); 36connect (conn4,$PROXY_HOST,root,,,$PROXY_PORT);
33SELECT counter;37SELECT conn_id, stmt_id;
34disconnect conn4;38disconnect conn4;
3539
36connect (conn5,127.0.0.1,root,,,$PROXY_PORT); 40connect (conn5,$PROXY_HOST,root,,,$PROXY_PORT);
37SELECT counter;41SELECT conn_id, stmt_id;
38disconnect conn5;42disconnect conn5;
43
44connect (conn6,$PROXY_HOST,root,,,$PROXY_PORT);
45SELECT conn_id, stmt_id;
46disconnect conn6;
3947
=== modified file 'tests/suite/run-tests.lua'
--- tests/suite/run-tests.lua 2010-05-05 10:46:53 +0000
+++ tests/suite/run-tests.lua 2010-09-08 12:58:43 +0000
@@ -30,46 +30,156 @@
30require("lfs")30require("lfs")
31require("glib2")31require("glib2")
32require("posix")32require("posix")
3333local fileutils = require("testenv.fileutils")
34---34local shellutils = require("testenv.shellutils")
35-- get the directory-name of a path35local testutils = require("testenv.testutils")
36--36local process = require("testenv.process")
37-- @param filename path to create the directory name from37local processmanager = require("testenv.processmanager")
38function dirname(filename)
39 local dirname = filename
40
41 attr = assert(lfs.attributes(dirname))
42
43 if attr.mode == "directory" then
44 return dirname
45 end
46
47 dirname = filename:gsub("/[^/]+$", "")
48
49 attr = assert(lfs.attributes(dirname))
50
51 assert(attr.mode == "directory", "dirname("..filename..") failed: is ".. attr.mode)
52
53 return dirname
54end
55
56---
57-- get the file-name of a path
58--
59-- @param filename path to create the directory name from
60function basename(filename)
61 name = filename:gsub(".*/", "")
62
63 return name
64end
6538
66-- 39--
67-- a set of user variables which can be overwritten from the environment40-- a set of user variables which can be overwritten from the environment
68--41--
6942
70local testdir = dirname(arg[0])43TestRunner = {
7144 num_tests = 0,
72local USE_POPEN = os.getenv('USE_POPEN') or 145 num_passes = 0,
46 num_skipped = 0,
47 num_fails = 0,
48 failed_test = {},
49 tests = {},
50
51 testenv = {
52 },
53
54 force_on_error = true,
55 tests_to_skip_filename = "tests_to_skip.lua",
56 tests_regex = os.getenv("TESTS_REGEX")
57}
58
59function TestRunner:new(o)
60 -- create a new process object
61 --
62 o = o or {}
63 setmetatable(o, self)
64 self.__index = self
65
66 local port_base = testutils.get_port_base(os.getenv("MYSQL_PROXY_START_PORT"), os.getenv("MYSQL_PROXY_END_PORT"))
67
68 self.testenv.testdir = fileutils.dirname(arg[0])
69 self.testenv.srcdir = os.getenv("srcdir") or self.testenv.testdir .. "/"
70 self.testenv.top_builddir = os.getenv("top_builddir") or self.testenv.testdir .. "/../"
71 self.testenv.builddir = os.getenv("builddir") or self.testenv.testdir .. "/" -- same as srcdir by default
72 self.testenv.plugin_dir = self.testenv.top_builddir .. "/plugins/"
73 self.testenv.basedir = lfs.currentdir()
74 self.testenv.lua_path = self.testenv.basedir .. "/" .. self.testenv.srcdir .. "/../../lib/?.lua"
75 self.testenv.lua_cpath = self.testenv.basedir .. "/../../lib/.libs/?.so"
76
77 self.testenv.PROXY_HOST = os.getenv("PROXY_HOST") or "127.0.0.1"
78 self.testenv.PROXY_PORT = os.getenv("PROXY_PORT") or tostring(port_base + 0)
79 self.testenv.MYSQL_TEST_BIN = os.getenv("MYSQL_TEST_BIN") or "mysqltest"
80 self.testenv.MYSQL_CLIENT_BIN = os.getenv("MYSQL_CLIENT_BIN") or "mysql"
81 self.testenv.PROXY_CHAIN_PORT = os.getenv("PROXY_CHAIN_PORT") or tostring(port_base + 30)
82
83 self.testenv.abs_srcdir = os.getenv("abs_srcdir")
84
85 if not self.testenv.abs_srcdir then
86 if not fileutils.path_is_absolute(self.testenv.srcdir) then
87 local abs_srcdir = posix.getcwd() .. "/" .. self.testenv.srcdir
88 self.testenv.abs_srcdir = abs_srcdir
89 else
90 self.testenv.abs_srcdir = self.testenv.srcdir
91 end
92 glib2.setenv("abs_srcdir", self.testenv.abs_srcdir) -- expose the abs_srcdir again as we run the proxy as --daemon which chdir()s to /
93 end
94
95 return o
96end
97
98function TestRunner:register_tests(suites)
99 for i, suite in ipairs(suites) do
100 local suite_srcdir = self.testenv.srcdir .. '/' .. suite
101 local suite_skipfile = suite_srcdir .. '/' .. self.tests_to_skip_filename
102 local stat = assert(lfs.attributes(suite_srcdir))
103
104 -- if it is a directory, execute all of them
105 if stat.mode == "directory" then
106 if fileutils.file_exists(suite_skipfile) then
107 assert(loadfile(suite_skipfile))()
108 end
109
110 for file in lfs.dir(suite_srcdir .. "/t/") do
111 local testname = file:match("(.+)\.test$")
112
113 if testname then
114 local is_skipped = false
115
116 if (self.tests_regex and not testname:match(self.tests_regex)) or tests_to_skip[testname] then
117 is_skipped = true
118 end
119
120 local test = MySQLProxyTest:new()
121 test.testname = testname
122 test.suite_srcdir = suite_srcdir
123 test.testenv = self.testenv
124 test.skipped = is_skipped
125
126 self.tests[#self.tests + 1] = test
127 end
128 end
129 end
130 end
131end
132
133function TestRunner:run_all()
134 local exitcode = 0
135
136 for i, test in ipairs(self.tests) do
137 shellutils.print_verbose("# >> " .. test.testname .. " started")
138
139 self.num_tests = self.num_tests + 1
140 local r = test:run()
141 if (r == 0) then
142 self.num_passes = self.num_passes + 1
143 elseif (r == 77) then
144 self.num_skipped = self.num_skipped + 1
145 else
146 self.num_fails = self.num_fails + 1
147 table.insert(self.failed_test, test)
148 end
149
150 shellutils.print_verbose("# << (exitcode = " .. r .. ")" )
151
152 if r ~= 0 and r ~= 77 and exitcode == 0 then
153 exitcode = r
154 end
155
156 if self.num_fails > 0 and (not self.force_on_error) then
157 break
158 end
159 end
160
161 if self.num_fails > 0 then
162 print ("*** ERRORS OCCURRED - The following tests failed")
163 for i, test in pairs(self.failed_test) do
164 print(test.testname)
165 end
166 end
167
168 --
169 -- prints test suite statistics
170 shellutils.print_verbose (string.format('tests: %d - skipped: %d (%4.1f%%) - passed: %d (%4.1f%%) - failed: %d (%4.1f%%)',
171 self.num_tests,
172 self.num_skipped,
173 self.num_skipped / self.num_tests * 100,
174 self.num_passes,
175 self.num_passes / (self.num_tests - self.num_skipped) * 100,
176 self.num_fails,
177 self.num_fails / (self.num_tests - self.num_skipped) * 100))
178
179 return exitcode
180end
181
182
73183
74local VERBOSE = os.getenv('TEST_VERBOSE') or 184local VERBOSE = os.getenv('TEST_VERBOSE') or
75 os.getenv('VERBOSE') or 185 os.getenv('VERBOSE') or
@@ -77,860 +187,318 @@
77VERBOSE = VERBOSE + 0187VERBOSE = VERBOSE + 0
78188
79local FORCE_ON_ERROR = os.getenv('FORCE_ON_ERROR') or os.getenv('FORCE')189local FORCE_ON_ERROR = os.getenv('FORCE_ON_ERROR') or os.getenv('FORCE')
190
191
80local MYSQL_USER = os.getenv("MYSQL_USER") or "root"192local MYSQL_USER = os.getenv("MYSQL_USER") or "root"
81local MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD") or ""193local MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD") or ""
82local MYSQL_HOST = os.getenv("MYSQL_HOST") or "127.0.0.1"194local MYSQL_HOST = os.getenv("MYSQL_HOST") or "127.0.0.1"
83local MYSQL_PORT = os.getenv("MYSQL_PORT") or "3306"195local MYSQL_PORT = os.getenv("MYSQL_PORT") or "3306"
84local MYSQL_DB = os.getenv("MYSQL_DB") or "test"196local MYSQL_DB = os.getenv("MYSQL_DB") or "test"
85local MYSQL_TEST_BIN = os.getenv("MYSQL_TEST_BIN") or "mysqltest"197
86local MYSQL_CLIENT_BIN = os.getenv("MYSQL_CLIENT_BIN") or "mysql"
87local TESTS_REGEX = os.getenv("TESTS_REGEX")
88
89--
90-- Global variables that can be referenced from .options files
91--
92-- TODO : add HOST variables for MASTER, SLAVE, CHAIN
93function get_port_base()
94 local port_base_start = os.getenv("MYSQL_PROXY_START_PORT") or 32768
95 local port_base_end = os.getenv("MYSQL_PROXY_END_PORT") or 65535
96 local port_interval = 64 -- let's take the base port in steps of ...
97 local range = port_base_end - port_base_start - port_interval
98 math.randomseed(posix.getpid())
99 local rand = math.floor(math.random() * (math.ceil(range / port_interval)))
100 local port_base = port_base_start + (rand * port_interval)
101
102 print(("... using tcp-port = %d as start port"):format(port_base))
103
104 return port_base
105end
106local port_base = get_port_base()
107
108PROXY_HOST = os.getenv("PROXY_HOST") or "127.0.0.1"
109PROXY_PORT = os.getenv("PROXY_PORT") or tostring(port_base + 0)
110PROXY_MASTER_PORT = os.getenv("PROXY_MASTER_PORT") or tostring(port_base + 10)
111PROXY_SLAVE_PORT = os.getenv("PROXY_SLAVE_PORT") or tostring(port_base + 20)
112PROXY_CHAIN_PORT = os.getenv("PROXY_CHAIN_PORT") or tostring(port_base + 30)
113ADMIN_PORT = os.getenv("ADMIN_PORT") or tostring(port_base + 15)
114ADMIN_MASTER_PORT = os.getenv("ADMIN_MASTER_PORT") or tostring(port_base + 25)
115ADMIN_SLAVE_PORT = os.getenv("ADMIN_SLAVE_PORT") or tostring(port_base + 35)
116ADMIN_CHAIN_PORT = os.getenv("ADMIN_CHAIN_PORT") or tostring(port_base + 45)
117ADMIN_USER = os.getenv("ADMIN_USER") or "root"
118ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD") or ""
119ADMIN_DEFAULT_SCRIPT_FILENAME = os.getenv("ADMIN_DEFAULT_SCRIPT_FILENAME") or ""
120-- local PROXY_TMP_LUASCRIPT = os.getenv("PROXY_TMP_LUASCRIPT") or "/tmp/proxy.tmp.lua"
121
122local srcdir = os.getenv("srcdir") or testdir .. "/"
123local top_builddir = os.getenv("top_builddir") or testdir .. "/../"
124local builddir = os.getenv("builddir") or testdir .. "/" -- same as srcdir by default
125
126local PROXY_TRACE = os.getenv("PROXY_TRACE") or "" -- use it to inject strace or valgrind
127local PROXY_PARAMS = os.getenv("PROXY_PARAMS") or "" -- extra params
128local PROXY_BINPATH = os.getenv("PROXY_BINPATH") or top_builddir .. "/src/mysql-proxy"
129PROXY_LIBPATH = os.getenv("PROXY_LIBPATH") or top_builddir .. "/plugins/"
130
131local COVERAGE_LCOV = os.getenv("COVERAGE_LCOV")
132198
133--199--
134-- end of user-vars200-- end of user-vars
135--201--
136202
137PROXY_PIDFILE = lfs.currentdir() .. "/mysql-proxy-test.pid"203-- options for the MySQL Proxy
138PROXY_MASTER_PIDFILE = lfs.currentdir() .. "/mysql-proxy-test-master.pid"204MySQLProxy = {
139PROXY_SLAVE_PIDFILE = lfs.currentdir() .. "/mysql-proxy-test-slave.pid"205}
140PROXY_CHAIN_PIDFILE = lfs.currentdir() .. "/mysql-proxy-test-chain.pid"206function MySQLProxy:new(o)
141PROXY_BACKEND_PIDFILE = lfs.currentdir() .. "/mysql-proxy-test-backend.pid"207 -- create a new process object
142PROXY_TEST_BASEDIR = lfs.currentdir()
143
144DEFAULT_SCRIPT_FILENAME = "/tmp/dummy.lua"
145default_proxy_options = {
146 ["proxy-backend-addresses"] = MYSQL_HOST .. ":" .. MYSQL_PORT,
147 ["proxy-address"] = PROXY_HOST .. ":" .. PROXY_PORT,
148 ["admin-address"] = PROXY_HOST .. ":" .. ADMIN_PORT,
149 ["admin-username"] = ADMIN_USER,
150 ["admin-password"] = ADMIN_PASSWORD,
151 ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
152 ["pid-file"] = PROXY_PIDFILE,
153 ["proxy-lua-script"] = DEFAULT_SCRIPT_FILENAME,
154 ["plugin-dir"] = PROXY_LIBPATH,
155 ["basedir"] = PROXY_TEST_BASEDIR,
156 }
157
158default_master_options = {
159 ["proxy-backend-addresses"] = MYSQL_HOST .. ":" .. MYSQL_PORT,
160 ["proxy-address"] = PROXY_HOST .. ":" .. PROXY_MASTER_PORT,
161 ["admin-address"] = PROXY_HOST .. ":" .. ADMIN_MASTER_PORT,
162 ["admin-username"] = ADMIN_USER,
163 ["admin-password"] = ADMIN_PASSWORD,
164 ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
165 ["pid-file"] = PROXY_MASTER_PIDFILE,
166 ["proxy-lua-script"] = DEFAULT_SCRIPT_FILENAME,
167 ["plugin-dir"] = PROXY_LIBPATH,
168 ["basedir"] = PROXY_TEST_BASEDIR,
169 }
170
171default_slave_options = {
172 ["proxy-backend-addresses"] = MYSQL_HOST .. ":" .. MYSQL_PORT,
173 ["proxy-address"] = PROXY_HOST .. ":" .. PROXY_SLAVE_PORT,
174 ["admin-address"] = PROXY_HOST .. ":" .. ADMIN_SLAVE_PORT,
175 ["admin-username"] = ADMIN_USER,
176 ["admin-password"] = ADMIN_PASSWORD,
177 ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
178 ["pid-file"] = PROXY_SLAVE_PIDFILE,
179 ["proxy-lua-script"] = DEFAULT_SCRIPT_FILENAME,
180 ["plugin-dir"] = PROXY_LIBPATH,
181 ["basedir"] = PROXY_TEST_BASEDIR,
182 }
183
184
185tests_to_skip = {}
186local tests_to_skip_filename = 'tests_to_skip.lua'
187local proxy_list = {}
188default_proxy_name = 'default'
189
190local exitcode=0
191
192---
193-- print_verbose()
194--
195-- prints a message if either the DEBUG or VERBOSE variables
196-- are set.
197--
198-- @param msg the message being printed
199-- @param min_level the minimum verbosity level for printing the message (default 1)
200function print_verbose(msg, min_level)
201 min_level = min_level or 1
202 if (VERBOSE >= min_level) then
203 print (msg)
204 end
205end
206---
207-- check if the file exists and is readable
208function file_exists(f)
209 return lfs.attributes(f)
210end
211
212---
213-- create the default option file
214--
215function make_default_options_file(fname)
216 if file_exists(fname) then
217 return
218 end
219 local fd = assert(io.open(fname, "w"))
220 fd:write('start_proxy(default_proxy_name, default_proxy_options) \n')
221 fd:close();
222end
223
224---
225-- copy a file
226--
227-- @param dst filename of the destination
228-- @param src filename of the source
229function file_copy(dst, src)
230 -- print_verbose("copying ".. src .. " to " .. dst)
231 local src_fd = assert(io.open(src, "rb"))
232 local content = src_fd:read("*a")
233 src_fd:close();
234
235 local dst_fd = assert(io.open(dst, "wb+"))
236 dst_fd:write(content);
237 dst_fd:close();
238end
239
240---
241-- create a empty file
242--
243-- if the file exists, it will be truncated to 0
244--
245-- @param dst filename to create and truncate
246function file_empty(dst)
247 -- print_verbose("emptying " .. dst)
248 local dst_fd = assert(io.open(dst, "wb+"))
249 dst_fd:close();
250end
251
252---
253-- turn a option-table into a string
254--
255-- the values are encoded and quoted for the shell
256--
257-- @param tbl a option table
258-- @param sep the seperator, defaults to a space
259function options_tostring(tbl, sep)
260 -- default value for sep
261 sep = sep or " "
262 208
263 assert(type(tbl) == "table")209 o = o or {}
264 assert(type(sep) == "string")210 setmetatable(o, self)
265211 self.__index = self
266 local s = ""212
267 for k, v in pairs(tbl) do213 self.backends = { }
268 local values214 return o
269 -- if the value is a table, repeat the option215end
270 if type(v) == "table" then216
271 values = v217function MySQLProxy:add_backend(addr)
272 else218 self.backends[#self.backends + 1] = addr
273 values = { v }219end
274 end220
275221function MySQLProxy:get_args(opts)
276 for tk, tv in pairs(values) do222 -- add a default backend
277 local enc_value = tv:gsub("\\", "\\\\"):gsub("\"", "\\\"")223 if #self.backends == 0 then
278 s = s .. "--" .. k .. "=\"" .. enc_value .. "\" "224 self:add_backend(("%s:%d"):format(MYSQL_HOST, MYSQL_PORT))
279 end225 end
280 end226
281 -- print_verbose(" option: " .. s)227 opts = opts or { }
282228 opts["plugin-dir"] = self.testenv["plugin_dir"]
283 return s229 opts["basedir"] = self.testenv["basedir"]
284end230 opts["pid-file"] = opts["pid-file"] or ("%s/mysql-proxy.pid"):format(self.testenv.basedir)
285231 opts["plugins"] = "proxy"
286--- turns an option table into a string of environment variables232 opts["proxy-address"]= opts["proxy-address"] or ("%s:%d"):format(self.testenv.PROXY_HOST, self.testenv.PROXY_PORT)
287--233 self.proxy_address = opts["proxy-address"]
288function env_options_tostring(tbl)234 opts["daemon"] = true
289 assert(type(tbl) == "table")235 opts["lua-path"] = self.testenv.lua_path
290236 opts["lua-cpath"] = self.testenv.lua_cpath
291 local s = ""237
292 for k, v in pairs(tbl) do238 opts["proxy-backend-addresses"] = self.backends
293 local enc_value = v:gsub("\\", "\\\\"):gsub("\"", "\\\"")239
294 s = s .. k .. "=\"" .. enc_value .. "\" "240 return opts
295 end241end
296242
297 return s243function MySQLProxy:get_env()
298end244 return {}
299245end
300246
301function os_execute(cmdline)247function MySQLProxy:get_command()
302 print_verbose("$ " .. cmdline)248 return os.getenv("PROXY_BINPATH") or self.testenv.top_builddir .. "/src/mysql-proxy"
303 return os.execute(cmdline)249end
304end250
305251---
306---252-- the backend
307-- get the PID from the pid file253MockBackend = MySQLProxy:new()
308--254function MockBackend:chain_with_frontend(frontend)
309function get_pid(pid_file_name)255 frontend:add_backend(self.proxy_address)
310 -- the file may exist, but the PID may not be written yet256end
311 local pid257
312 local rounds = 0258Test = {
313259}
314 repeat 260
315 local fh = assert(io.open(pid_file_name, 'r'))261function Test:new(o)
316 pid = fh:read("*n")262 -- create a new process object
317 fh:close()263 --
318 if not pid then264 o = o or {}
319 glib2.usleep(200 * 1000) -- wait a bit until we get some content265 setmetatable(o, self)
320 rounds = rounds + 1266 self.__index = self
321267
322 if rounds > 10 then268 self.skipped = false
323 error(("reading PID from existing pid-file '%s' failed after waiting 2 sec"):format(269 self.testname = nil
324 pid_file_name))270
271 return o
272end
273
274---
275-- @return true[, msg] if test should be skipped, false otherwise
276function Test:is_skipped()
277 return self.skipped
278end
279
280function Test:setup()
281 return true
282end
283
284function Test:teardown()
285 return true
286end
287
288function Test:run()
289 local is_skipped, skip_msg = self:is_skipped()
290
291 if is_skipped then
292 print(('skip %s %s'):format(
293 self.testname,
294 skip_msg or 'no reason given'))
295 return 77
296 end
297
298 self.procs = processmanager:new()
299
300 local ret, errmsg = self:setup()
301 if not ret then
302 print(("err %s # setup failed: %s"):format(
303 self.testname,
304 errmsg))
305 ret = -1
306 else
307 ret = self:run_test()
308 self:teardown()
309 end
310
311 self.procs:shutdown_all() -- shutdown all the processes we started
312
313 return ret
314end
315
316---
317-- a Test baseclass that knows about
318-- * mysqltest as test-driver
319-- * starting MySQL Proxies before starting mysqltest
320--
321-- inherited from Test
322MySQLProxyTest = Test:new()
323
324---
325-- 'test_self' is a hack to pass down the object we test right now to chain_proxy().
326--
327-- It is set in MySQLProxyTest:setup()
328test_self = nil
329
330---
331-- setup the backend proxies and wire them together
332--
333-- depends on 'test_self' being a set to the currently running Test-object
334function chain_proxy(backend_filenames, script_filename)
335 local self = test_self
336
337 if type(backend_filenames) ~= "table" then
338 backend_filenames = { backend_filenames }
339 end
340
341 local backends = { }
342 for backend_ndx, backend_filename in ipairs(backend_filenames) do
343 local mock = MockBackend:new()
344 mock.testenv = self.testenv
345
346 local port = tonumber(self.testenv.PROXY_CHAIN_PORT) + backend_ndx - 1 -- we start with 1
347
348 local mock_args = mock:get_args({
349 ["proxy-lua-script"] = ("%s/t/%s"):format(self.suite_srcdir, backend_filename),
350 ["proxy-address"] = ("%s:%d"):format(self.testenv.PROXY_HOST, port),
351 ["pid-file"] = ("%s/chain-%d.pid"):format(self.testenv.basedir, backend_ndx)
352 })
353
354 local proc = process:new()
355 local ret = proc:execute(mock:get_command(),
356 mock:get_env(),
357 mock_args)
358 if ret ~= 0 then
359 return false, ""
360 end
361
362 if mock_args["pid-file"] then
363 local is_running, errmsg = proc:wait_running(mock_args["pid-file"])
364 if not is_running then
365 return false, errmsg or "not running"
325 end366 end
326 end367 end
327 until pid368
328369 self.procs:add(proc)
329 -- the PID we get here should be a number370 table.insert(backends, mock)
330 assert(type(pid) == "number")371 end
331372 -- all the backends are up, start the middle proxy
332 return pid373
333end374 script_filename = ("%s/t/%s"):format(self.suite_srcdir, script_filename)
334375
335function wait_proc_up(pid_file) 376 local proxy = MySQLProxy:new()
336 local rounds = 0377 proxy.testenv = self.testenv
337378
338 while not file_exists(pid_file) do379 for _, backend in ipairs(backends) do
339 glib2.usleep(200 * 1000) -- wait until the pid-file is created380 backend:chain_with_frontend(proxy)
340381 end
341 rounds = rounds + 1382
342 print_verbose(("(wait_proc_up) pid-wait: %d rounds, (%s)"):format(rounds, pid_file))383 local proxy_args = proxy:get_args({
343384 ["proxy-lua-script"] = script_filename,
344 if rounds > 1000 then error(("proxy failed to start: no pid-file %s"):format(pid_file)) end385 })
345 end386
346387 local proc = process:new()
347 local pid = get_pid(pid_file)388 local ret = proc:execute(proxy:get_command(),
348389 proxy:get_env(),
349 rounds = 0390 proxy_args)
350 -- check that the process referenced in the PID-file is still up391 if ret ~= 0 then
351 while 0 ~= os.execute("kill -0 ".. pid .." 2> /dev/null") do392 return false, ""
352 glib2.usleep(200 * 1000) -- wait until the pid-file is created393 end
353 rounds = rounds + 1394 if proxy_args["pid-file"] then
354 print_verbose(("(wait_proc_up) kill-wait: %d rounds, pid=%d (%s)"):format(rounds, pid, pid_file))395 local is_running, errmsg = proc:wait_running(proxy_args["pid-file"])
355396 if not is_running then
356 if rounds > 5 then error(("proxy seems to have crashed: pid=%d (%s)"):format(pid, pid_file)) end397 return false, errmsg or "not running"
357 end398 end
358end399 end
359400
360function proc_is_up(pid)401 self.procs:add(proc)
361 return os.execute("kill -0 ".. pid .." 2> /dev/null")402end
362end403
363404function MySQLProxyTest:start_proxy(script_filename)
364function proc_stop(pid)405 local proxy = MySQLProxy:new()
365 return os.execute("kill -TERM ".. pid)406 proxy.testenv = self.testenv
366end407 local proxy_args = proxy:get_args({
367408 ["proxy-lua-script"] = script_filename,
368function wait_proc_down(pid_file) 409 })
369 local rounds = 0410
370 local pid = get_pid(pid_file)411 local proc = process:new()
371412 local ret = proc:execute(proxy:get_command(),
372 -- wait until the proc in the pid file is dead413 proxy:get_env(),
373 -- the shutdown takes at about 500ms414 proxy_args)
374 while 0 == proc_is_up(pid) do415 if ret ~= 0 then
375 glib2.usleep(200 * 1000) -- wait until process is gone416 return false, ""
376 rounds = rounds + 1417 end
377 print_verbose(("(wait_proc_down) kill-wait: %d rounds, pid=%d (%s)"):format(rounds, pid, pid_file))418 if proxy_args["pid-file"] then
378 end419 local is_running, errmsg = proc:wait_running(proxy_args["pid-file"])
379end420 if not is_running then
380421 return false, errmsg or "not running"
381function stop_proxy()422 end
382 -- shut dowm the proxy423 end
383 --424
384 -- win32 has tasklist and taskkill on the shell425 self.procs:add(proc)
385 426end
386 427
387 -- shuts down every proxy in the proxy list428function MySQLProxyTest:setup()
388 --429 local script_filename = ("%s/t/%s.lua"):format(self.suite_srcdir, self.testname)
389 for proxy_name, proxy_options in pairs(proxy_list) do430 local options_filename = ("%s/t/%s.options"):format(self.suite_srcdir, self.testname)
390 local pid431 local has_script_file = fileutils.file_exists(script_filename)
391 pid_file = proxy_options['pid-file']432 local has_options_file = fileutils.file_exists(options_filename)
392 print_verbose ('stopping proxy ' .. proxy_name)433
393 434 -- if we have a options file, run it as part of the setup phases
394 pid = get_pid(pid_file)435 -- if not, assume that we want to start a proxy with the script_filename
395436 if has_options_file then
396 if proc_is_up(pid) then437 -- load the options file
397 proc_stop(pid)438 test_self = self -- expose 'self' so that chain_proxy() can use it
398 else439 local setup_func, errmsg = loadfile(options_filename)
399 print("-- process "..proxy_name.." is already down")440 if not setup_func then
400 exitcode = -1441 return false, errmsg
401 end442 end
402 end443
403444 -- try to call the setup func
404 -- wait until they are all gone445 local ret, errmsg = pcall(setup_func)
405 for proxy_name, proxy_options in pairs(proxy_list) do446 if not ret then
406 pid_file = proxy_options['pid-file']447 return false, errmsg
407448 end
408 wait_proc_down(pid_file)449 else
409450 self:start_proxy(script_filename)
410 os.remove(pid_file)451 end
411 end452 return true
412453end
413 --454
414 -- empties the proxy list455function MySQLProxyTest:run_test()
415 --
416 proxy_list = { }
417end
418
419function only_item ( tbl, item)
420 local exists = false
421 for i,v in pairs(tbl) do
422 if i == item then
423 exists = true
424 else
425 return false
426 end
427 end
428 return exists
429end
430
431---
432-- before_test()
433--
434-- Executes a script with a base name like test_name and extension ".options"
435--
436-- If there is no such file, the default options are used
437--
438function before_test(basedir, test_name)
439 local script_filename = basedir .. "/t/" .. test_name .. ".lua"
440 local options_filename = basedir .. "/t/" .. test_name .. ".options"
441 local has_option_file = file_exists(options_filename)
442 if file_exists( script_filename) then
443 if has_option_file then
444 default_proxy_options['proxy-lua-script'] = script_filename
445 print_verbose('using lua script directly ' .. script_filename)
446 file_empty(DEFAULT_SCRIPT_FILENAME)
447 else
448 default_proxy_options['proxy-lua-script'] = DEFAULT_SCRIPT_FILENAME
449 file_copy(DEFAULT_SCRIPT_FILENAME, script_filename)
450 print_verbose('copying lua script to default ' .. script_filename)
451 end
452 else
453 default_proxy_options['proxy-lua-script'] = DEFAULT_SCRIPT_FILENAME
454 file_empty(DEFAULT_SCRIPT_FILENAME)
455 print_verbose('using empty lua script')
456 end
457 global_basedir = basedir
458 print_verbose ('current_dir ' ..
459 basedir ..
460 ' - script: ' ..
461 default_proxy_options['proxy-lua-script'] )
462 --
463 -- executes the content of the options file
464 --
465 if has_option_file then
466 print_verbose('# using options file ' .. options_filename)
467 stop_proxy()
468 else
469 --
470 -- if no option file is found, the default options file is executed
471 --
472 options_filename = basedir .. "/t/default.options"
473 print_verbose('#using default options file' .. options_filename)
474 if only_item(proxy_list,'default') then
475 print_verbose('reusing existing proxy')
476 return
477 end
478 make_default_options_file(options_filename)
479 end
480 assert(loadfile(options_filename))()
481end
482
483function after_test()
484 if only_item(proxy_list, 'default') then
485 return
486 end
487 stop_proxy()
488end
489
490function alternative_execute (cmd)
491 print_verbose(cmd)
492
493 local fh = io.popen(cmd)
494 assert(fh, 'error executing '.. cmd)
495 local result = ''
496 local line = fh:read()
497 while line do
498 result = result .. line
499 line = fh:read()
500 end
501 fh:close()
502 return result
503end
504
505function conditional_execute (cmd)
506 if USE_POPEN then
507 return alternative_execute(cmd)
508 else
509 return os_execute(cmd)
510 end
511end
512
513---
514-- run a test
515--
516-- @param testname name of the test
517-- @return exit-code of mysql-test
518function run_test(filename, basedir)
519 if not basedir then basedir = srcdir end
520
521 local testname = assert(filename:match("t/(.+)\.test"))
522 if tests_to_skip[testname] then
523 print('skip ' .. testname ..' '.. (tests_to_skip[testname] or 'no reason given') )
524 return 0, 1
525 end
526 before_test(basedir, testname)
527 if VERBOSE > 1 then
528 os.execute('echo -n "' .. testname .. ' " ; ' )
529 end
530 local result = 0456 local result = 0
531 local ret = conditional_execute(457
532 env_options_tostring({458 local proc = process:new()
533 ['MYSQL_USER'] = MYSQL_USER,459 local ret = proc:popen(
534 ['MYSQL_PASSWORD'] = MYSQL_PASSWORD,460 self.testenv.MYSQL_TEST_BIN,
535 ['PROXY_HOST'] = PROXY_HOST,461 {
536 ['PROXY_PORT'] = PROXY_PORT,462 ['MYSQL_USER'] = self.testenv.MYSQL_USER,
537 ['PROXY_CHAIN_PORT'] = PROXY_CHAIN_PORT,463 ['MYSQL_PASSWORD'] = self.testenv.MYSQL_PASSWORD,
538 ['MASTER_PORT'] = PROXY_MASTER_PORT,464 ['PROXY_HOST'] = self.testenv.PROXY_HOST,
539 ['SLAVE_PORT'] = PROXY_SLAVE_PORT,465 ['PROXY_PORT'] = self.testenv.PROXY_PORT,
540 }) .. ' ' ..466 ['PROXY_CHAIN_PORT'] = self.testenv.PROXY_CHAIN_PORT,
541 MYSQL_TEST_BIN .. " " ..467 ['MASTER_PORT'] = self.testenv.PROXY_MASTER_PORT,
542 options_tostring({468 ['SLAVE_PORT'] = self.testenv.PROXY_SLAVE_PORT,
543 user = MYSQL_USER,469 },
544 password = MYSQL_PASSWORD,470 {
545 database = MYSQL_DB,471 user = self.testenv.MYSQL_USER,
546 host = PROXY_HOST,472 password = self.testenv.MYSQL_PASSWORD,
547 port = PROXY_PORT,473 database = self.testenv.MYSQL_DB,
474 host = self.testenv.PROXY_HOST,
475 port = self.testenv.PROXY_PORT,
548 verbose = (VERBOSE > 0) and "TRUE" or "FALSE", -- pass down the verbose setting 476 verbose = (VERBOSE > 0) and "TRUE" or "FALSE", -- pass down the verbose setting
549 ["test-file"] = basedir .. "/t/" .. testname .. ".test",477 ["test-file"] = self.suite_srcdir .. "/t/" .. self.testname .. ".test",
550 ["result-file"] = basedir .. "/r/" .. testname .. ".result",478 ["result-file"] = self.suite_srcdir .. "/r/" .. self.testname .. ".result",
551 ["logdir"] = builddir, -- the .result dir might not be writable479 ["logdir"] = self.suite_builddir, -- the .result dir might not be writable
552 })480 })
553 )481
554 if USE_POPEN then482 if (ret == "ok") then
555 assert(ret == 'ok' or ret =='not ok', 'unexpected result <' .. ret .. '>')483 print(("ok # %s"):format(self.testname))
556 if (ret == 'ok') then484 return 0
557 result = 0485 elseif (ret == "not ok") then
558 elseif ret == 'not ok'then486 print(("not ok # %s"):format(self.testname))
559 result = 1487 return -1
560 end488 else
561 print(ret .. ' ' .. testname) 489 print(("not ok # (%s) %s"):format(ret, self.testname))
562 else490 return -1
563 result = ret491 end
564 end492end
565 after_test() 493
566 return result, 0494local runner = TestRunner:new()
567end495runner:register_tests({"base"})
568496local exitcode = runner:run_all()
569---
570--sql_execute()
571--
572-- Executes a SQL query in a given Proxy
573--
574-- If no Proxy is indicated, the query is passed directly to the backend server
575--
576-- @param query A SQL statement to execute, or a table of SQL statements
577-- @param proxy_name the name of the proxy that executes the query
578function sql_execute(queries, proxy_name)
579 local ret = 0
580 assert(type(queries) == 'string' or type(queries) == 'table', 'invalid type for query' )
581 if type(queries) == 'string' then
582 queries = {queries}
583 end
584 local query = ''
585 for i, q in pairs(queries) do
586 query = query .. ';' .. q
587 end
588
589 if proxy_name then
590 --
591 -- a Proxy name is passed.
592 -- The query is executed with the given proxy
593 local opts = proxy_list[proxy_name]
594 assert(opts,'proxy '.. proxy_name .. ' not active')
595 assert(opts['proxy-address'],'address for proxy '.. proxy_name .. ' not found')
596 local p_host, p_port = opts['proxy-address']:match('(%S+):(%S+)')
597 ret = os_execute( MYSQL_CLIENT_BIN .. ' ' ..
598 options_tostring({
599 user = MYSQL_USER,
600 password = MYSQL_PASSWORD,
601 database = MYSQL_DB,
602 host = p_host,
603 port = p_port,
604 execute = query
605 })
606 )
607 assert(ret == 0, 'error using mysql client ')
608 else
609 --
610 -- No proxy name was passed.
611 -- The query is executed in the backend server
612 --
613 ret = os_execute( MYSQL_CLIENT_BIN .. ' ' ..
614 options_tostring({
615 user = MYSQL_USER,
616 password = MYSQL_PASSWORD,
617 database = MYSQL_DB,
618 host = PROXY_HOST,
619 port = MYSQL_PORT,
620 execute = query
621 })
622 )
623 end
624 return ret
625end
626
627stop_proxy()
628
629-- the proxy needs the lua-script to exist
630-- file_empty(PROXY_TMP_LUASCRIPT)
631
632-- if the pid-file is still pointing to a active process, kill it
633--[[
634if file_exists(PROXY_PIDFILE) then
635 os.execute("kill -TERM `cat ".. PROXY_PIDFILE .." `")
636 os.remove(PROXY_PIDFILE)
637end
638--]]
639
640if COVERAGE_LCOV then
641 -- os_execute(COVERAGE_LCOV ..
642 -- " --zerocounters --directory ".. srcdir .. "/../src/" )
643end
644
645-- setting the include path
646--
647
648-- this is the path containing the global Lua modules
649local GLOBAL_LUA_PATH = os.getenv('LUA_LDIR') or '/usr/share/lua/5.1/?.lua'
650
651-- this is the path containing the Proxy libraries
652local PROXY_LUA_PATH = os.getenv('LUA_PATH') or '/usr/local/share/?.lua'
653
654-- This is the path with specific libraries for the test suite
655local PRIVATE_LUA_PATH = arg[1] .. '/t/?.lua'
656
657-- This is the path with additional libraries that the user needs
658local LUA_USER_PATH = os.getenv('LUA_USER_PATH') or '../lib/?.lua'
659
660-- Building the final include path
661local INCLUDE_PATH =
662 LUA_USER_PATH .. ';' ..
663 PRIVATE_LUA_PATH .. ';' ..
664 GLOBAL_LUA_PATH .. ';' ..
665 PROXY_LUA_PATH
666
667---
668-- start_proxy()
669--
670-- starts an instance of MySQL Proxy
671--
672-- @param proxy_name internal name of the proxy instance, for retrieval
673-- @param proxy_options the options to start the Proxy
674function start_proxy(proxy_name, proxy_options)
675 -- start the proxy
676 assert(type(proxy_options) == 'table')
677 if not file_exists(proxy_options['proxy-lua-script']) then
678 proxy_options['proxy-lua-script'] =
679 global_basedir ..
680 '/t/' .. proxy_options['proxy-lua-script']
681 end
682 print_verbose("starting " .. proxy_name .. " with " .. options_tostring(proxy_options))
683
684 -- remove the old pid-file if it exists
685 os.remove(proxy_options['pid-file'])
686 -- if we are supposed to listen on a UNIX socket, remove it first, because we don't clean it up on exit!
687 -- TODO: fix the code, instead of hacking around here! Bug#38415
688 if proxy_options['proxy-address'] == '/tmp/mysql-proxy-test.sock' then
689 os.remove(proxy_options['proxy-address'])
690 end
691 -- os.execute("head " .. proxy_options['proxy-lua-script'])
692 assert(os.execute( 'LUA_PATH="' .. INCLUDE_PATH .. '" ' ..
693 PROXY_TRACE .. " " .. PROXY_BINPATH .. " " ..
694 options_tostring( proxy_options) .. " &"
695 ))
696
697 -- wait until the proxy is up
698 wait_proc_up(proxy_options['pid-file'])
699
700 proxy_list[proxy_name] = proxy_options
701end
702
703---
704-- simulate_replication()
705--
706-- creates a fake master/slave by having two proxies
707-- pointing at the same backend
708--
709-- you can alter those backends by changing
710-- the starting parameters
711--
712-- @param master_options options for master
713-- @param slave_options options for slave
714function simulate_replication(master_options, slave_options)
715 if not master_options then
716 master_options = default_master_options
717 end
718 if not master_options['pid-file'] then
719 master_options['pid-file'] = PROXY_MASTER_PIDFILE
720 end
721 if not slave_options then
722 slave_options = default_slave_options
723 end
724 if not slave_options['pid-file'] then
725 slave_options['pid-file'] = PROXY_SLAVE_PIDFILE
726 end
727 start_proxy('master', master_options)
728 start_proxy('slave', slave_options)
729end
730
731---
732-- chain_proxy()
733--
734-- starts two proxy instances, with the first one is pointing to the
735-- default backend server, and the second one (with default ports)
736-- is pointing at the first proxy
737--
738-- client -> proxy -> backend_proxy -> [ mysql-backend ]
739--
740-- usually we use it to mock a server with a lua script (...-mock.lua) and
741-- the script under test in the proxy (...-test.lua)
742--
743-- in case you want to start several backend_proxies, just provide a array
744-- as first param
745--
746-- @param backend_lua_script
747-- @param second_lua_script
748-- @param use_replication uses a master proxy as backend
749function chain_proxy (backend_lua_scripts, second_lua_script, use_replication)
750 local backends = { }
751
752 if type(backend_lua_scripts) == "table" then
753 backends = backend_lua_scripts
754 else
755 backends = { backend_lua_scripts }
756 end
757
758 local backend_addresses = { }
759
760 for i, backend_lua_script in ipairs(backends) do
761 backend_addresses[i] = PROXY_HOST .. ":" .. (PROXY_CHAIN_PORT + i - 1)
762
763 backend_proxy_options = {
764 ["proxy-backend-addresses"] = MYSQL_HOST .. ":" .. MYSQL_PORT,
765 ["proxy-address"] = backend_addresses[i],
766 ["admin-address"] = PROXY_HOST .. ":" .. (ADMIN_CHAIN_PORT + i - 1),
767 ["admin-username"] = ADMIN_USER,
768 ["admin-password"] = ADMIN_PASSWORD,
769 ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
770 ["pid-file"] = PROXY_CHAIN_PIDFILE .. i,
771 ["proxy-lua-script"] = backend_lua_script or DEFAULT_SCRIPT_FILENAME,
772 ["plugin-dir"] = PROXY_LIBPATH,
773 ["basedir"] = PROXY_TEST_BASEDIR,
774 ["log-level"] = (VERBOSE == 4) and "debug" or "critical",
775 }
776 --
777 -- if replication was not started, then it is started here
778 --
779 if use_replication and (use_replication == true) then
780 if (proxy_list['master'] == nil) then
781 simulate_replication()
782 end
783 backend_proxy_options["proxy-backend-addresses"] = PROXY_HOST .. ':' .. PROXY_MASTER_PORT
784 end
785 start_proxy('backend_proxy' .. i, backend_proxy_options)
786 end
787
788 second_proxy_options = {
789 ["proxy-backend-addresses"] = backend_addresses ,
790 ["proxy-address"] = PROXY_HOST .. ":" .. PROXY_PORT,
791 ["admin-address"] = PROXY_HOST .. ":" .. ADMIN_PORT,
792 ["admin-username"] = ADMIN_USER,
793 ["admin-password"] = ADMIN_PASSWORD,
794 ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
795 ["pid-file"] = PROXY_PIDFILE,
796 ["proxy-lua-script"] = second_lua_script or DEFAULT_SCRIPT_FILENAME,
797 ["plugin-dir"] = PROXY_LIBPATH,
798 ["basedir"] = PROXY_TEST_BASEDIR,
799 ["log-level"] = (VERBOSE == 3) and "debug" or "critical",
800 }
801 start_proxy('second_proxy',second_proxy_options)
802end
803
804local num_tests = 0
805local num_passes = 0
806local num_skipped = 0
807local num_fails = 0
808local all_ok = true
809local failed_test = {}
810
811file_empty(DEFAULT_SCRIPT_FILENAME)
812
813--
814-- if we have a argument, exectute the named test
815-- otherwise execute all tests we can find
816if #arg then
817 for i, a in ipairs(arg) do
818 local stat = assert(lfs.attributes(a))
819
820 if file_exists(a .. '/' .. tests_to_skip_filename) then
821 assert(loadfile(a .. '/' .. tests_to_skip_filename))()
822 end
823
824 -- if it is a directory, execute all of them
825 if stat.mode == "directory" then
826 for file in lfs.dir(a .. "/t/") do
827 if not TESTS_REGEX or file:match(TESTS_REGEX) then
828 local testname = file:match("(.+\.test)$")
829
830 if testname then
831 print_verbose("# >> " .. testname .. " started")
832
833 num_tests = num_tests + 1
834 local r, skipped = run_test("t/" .. testname, a)
835 if (r == 0) then
836 num_passes = num_passes + 1 - skipped
837 else
838 num_fails = num_fails + 1
839 all_ok = false
840 table.insert(failed_test, testname)
841 end
842 num_skipped = num_skipped + skipped
843
844 print_verbose("# << (exitcode = " .. r .. ")" )
845
846 if r ~= 0 and exitcode == 0 then
847 exitcode = r
848 end
849 end
850 if all_ok == false and (not FORCE_ON_ERROR) then
851 break
852 end
853 end
854 end
855 else
856 -- otherwise just this one test
857 --
858 -- FIXME: base/ is hard-coded for now
859 exitcode, skipped = run_test(a, "base/")
860 num_skipped = num_skipped + skipped
861 end
862 end
863else
864 for file in lfs.dir(srcdir .. "/t/") do
865 local testname = file:match("(.+\.test)$")
866
867 if testname then
868 print_verbose("# >> " .. testname .. " started")
869
870 num_tests = num_tests + 1
871 local r, skipped = run_test("t/" .. testname)
872 num_skipped = num_skipped + skipped
873
874 print_verbose("# << (exitcode = " .. r .. ")" )
875 if (r == 0) then
876 num_passes = num_passes + 1 - skipped
877 else
878 all_ok = false
879 num_fails = num_fails + 1
880 table.insert(failed_test, testname)
881 end
882 if r ~= 0 and exitcode == 0 then
883 exitcode = r
884 end
885 if all_ok == false and (not FORCE_ON_ERROR) then
886 break
887 end
888 end
889 end
890end
891
892if all_ok ==false then
893 print ("*** ERRORS OCCURRED - The following tests failed")
894 for i,v in pairs(failed_test) do
895 print(v )
896 end
897end
898
899--
900-- prints test suite statistics
901print_verbose (string.format('tests: %d - skipped: %d (%4.1f%%) - passed: %d (%4.1f%%) - failed: %d (%4.1f%%)',
902 num_tests,
903 num_skipped,
904 num_skipped / num_tests * 100,
905 num_passes,
906 num_passes / (num_tests - num_skipped) * 100,
907 num_fails,
908 num_fails / (num_tests - num_skipped) * 100))
909
910--
911-- stops any remaining active proxy
912--
913stop_proxy()
914
915if COVERAGE_LCOV then
916 os_execute(COVERAGE_LCOV ..
917 " --quiet " ..
918 " --capture --directory ".. srcdir .. "/../src/" ..
919 " > " .. srcdir .. "/../tests.coverage.info" )
920
921 os_execute("genhtml " ..
922 "--show-details " ..
923 "--output-directory " .. srcdir .. "/../coverage/ " ..
924 "--keep-descriptions " ..
925 "--frames " ..
926 srcdir .. "/../tests.coverage.info")
927end
928
929497
930if exitcode == 0 then498if exitcode == 0 then
931 os.exit(0)499 os.exit(0)
932else500else
933 print_verbose("mysql-test exit-code: " .. exitcode)501 shellutils.print_verbose("mysql-test exit-code: " .. exitcode)
934 os.exit(-1)502 os.exit(-1)
935end503end
936504
937505
=== added directory 'tests/suite/testenv'
=== added file 'tests/suite/testenv/Makefile.am'
--- tests/suite/testenv/Makefile.am 1970-01-01 00:00:00 +0000
+++ tests/suite/testenv/Makefile.am 2010-09-08 12:58:43 +0000
@@ -0,0 +1,7 @@
1EXTRA_DIST=\
2 fileutils.lua \
3 shellutils.lua \
4 testutils.lua \
5 process.lua \
6 processmanager.lua
7
08
=== added file 'tests/suite/testenv/fileutils.lua'
--- tests/suite/testenv/fileutils.lua 1970-01-01 00:00:00 +0000
+++ tests/suite/testenv/fileutils.lua 2010-09-08 12:58:43 +0000
@@ -0,0 +1,84 @@
1--[[ $%BEGINLICENSE%$
2 Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; version 2 of the
7 License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 02110-1301 USA
18
19 $%ENDLICENSE%$ --]]
20
21require("lfs")
22
23module("testenv.fileutils", package.seeall)
24
25---
26-- check if the file exists and is readable
27function file_exists(f)
28 local r = lfs.attributes(f)
29 return (r ~= nil)
30end
31
32---
33-- check if a path is absolute
34--
35-- FIXME: right now it is Unix only
36function path_is_absolute(f)
37 return f:byte() == "/"
38end
39
40---
41-- get the directory-name of a path
42--
43-- @param filename path to create the directory name from
44function dirname(filename)
45 local dirname = filename
46
47 attr = assert(lfs.attributes(dirname))
48
49 if attr.mode == "directory" then
50 return dirname
51 end
52
53 dirname = filename:gsub("/[^/]+$", "")
54
55 attr = assert(lfs.attributes(dirname))
56
57 assert(attr.mode == "directory", "dirname("..filename..") failed: is ".. attr.mode)
58
59 return dirname
60end
61
62---
63-- get the file-name of a path
64--
65-- @param filename path to create the directory name from
66function basename(filename)
67 name = filename:gsub(".*/", "")
68
69 return name
70end
71
72---
73-- create a empty file
74--
75-- if the file exists, it will be truncated to 0
76--
77-- @param dst filename to create and truncate
78function create_empty_file(dst)
79 -- print_verbose("emptying " .. dst)
80 local dst_fd = assert(io.open(dst, "wb+"))
81 dst_fd:close();
82end
83
84
085
=== added file 'tests/suite/testenv/process.lua'
--- tests/suite/testenv/process.lua 1970-01-01 00:00:00 +0000
+++ tests/suite/testenv/process.lua 2010-09-08 12:58:43 +0000
@@ -0,0 +1,179 @@
1--[[ $%BEGINLICENSE%$
2 Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; version 2 of the
7 License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 02110-1301 USA
18
19 $%ENDLICENSE%$ --]]
20
21local _G = _G
22
23local shellutils = require("testenv.shellutils")
24local glib2 = require("glib2")
25
26module("testenv.process")
27
28---
29-- life cycle management for processes
30--
31-- * PID files
32-- * wait until they are started
33-- * shutting down
34--
35function new(self, o)
36 -- create a new process object
37 --
38 o = o or {}
39 _G.setmetatable(o, self)
40 self.__index = self
41 return o
42end
43
44function get_pid(self)
45 return self.pid
46end
47
48function set_pid(self, pid)
49 self.pid = pid
50end
51
52---
53-- read a PID from a pidfile
54function set_pid_from_pidfile(self, pid_file_name)
55 local fh, errmsg = _G.io.open(pid_file_name, 'r')
56 if not fh then
57 return false, errmsg
58 end
59 local pid = fh:read("*n")
60 fh:close()
61
62 if _G.type(pid) ~= "number" then
63 return false
64 end
65 if not pid or pid == 0 then
66 return false
67 end
68
69 self:set_pid(pid)
70
71 return true
72end
73
74function is_running(self)
75 _G.assert(self.pid)
76
77 local ret = _G.os.execute("kill -0 ".. self.pid .." 2> /dev/null")
78
79 return (ret == 0)
80end
81
82---
83-- wait until the process is up and running
84--
85function wait_running(self, pid_file_name)
86 -- try to get a PID
87 local rounds = 0
88 local wait_interval_ms = 100
89
90 while not self.pid and rounds < 10 do
91 self:set_pid_from_pidfile(pid_file_name)
92 if self.pid then
93 break
94 end
95
96 glib2.usleep(wait_interval_ms * 1000) -- wait until process is gone
97 rounds = rounds + 1
98 shellutils.print_verbose(("process:wait_running(pid = %s) waited %dms"):format(pid_file_name, wait_interval_ms * rounds))
99 end
100
101 -- check if the process is actually alive
102 if not self.pid or not self:is_running() then
103 return false
104 end
105
106 return true
107end
108
109function wait_down(self)
110 _G.assert(self.pid)
111 local rounds = 0
112 local wait_interval_ms = 200
113
114 -- wait until the proc in the pid file is dead
115 -- the shutdown takes at about 500ms
116 while self:is_running() do
117 glib2.usleep(wait_interval_ms * 1000) -- wait until process is gone
118 rounds = rounds + 1
119 shellutils.print_verbose(("process:wait_down(pid = %d) waited %dms"):format(self.pid, wait_interval_ms * rounds))
120 end
121
122 return true
123end
124
125function shutdown(self)
126 -- shut down the proxy
127 --
128 -- win32 has tasklist and taskkill on the shell
129 if self.pid then
130 return _G.os.execute("kill -TERM ".. self.pid)
131 end
132end
133
134---
135-- execute a process
136function execute(self, cmd, env, opts)
137 local env_str = ""
138 local opts_str = ""
139
140 if env then
141 env_str = shellutils.env_tostring(env)
142 end
143
144 if opts then
145 opts_str = shellutils.options_tostring(opts)
146 end
147
148 local cmdline = env_str .. " " .. cmd .. " " .. opts_str
149 shellutils.print_verbose("$ " .. cmdline)
150 return _G.os.execute(cmdline)
151end
152
153function popen(self, cmd, env, opts)
154 local env_str = ""
155 local opts_str = ""
156
157 if env then
158 env_str = shellutils.env_tostring(env)
159 end
160
161 if opts then
162 opts_str = shellutils.options_tostring(opts)
163 end
164
165 local cmdline = env_str .. " " .. cmd .. " " .. opts_str
166 shellutils.print_verbose("$ " .. cmdline)
167
168 local fh = _G.io.popen(cmdline)
169 _G.assert(fh, 'error executing '.. cmdline)
170 local result = ''
171 local line = fh:read()
172 while line do
173 result = result .. line
174 line = fh:read()
175 end
176 fh:close()
177 return result
178end
179
0180
=== added file 'tests/suite/testenv/processmanager.lua'
--- tests/suite/testenv/processmanager.lua 1970-01-01 00:00:00 +0000
+++ tests/suite/testenv/processmanager.lua 2010-09-08 12:58:43 +0000
@@ -0,0 +1,72 @@
1--[[ $%BEGINLICENSE%$
2 Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; version 2 of the
7 License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 02110-1301 USA
18
19 $%ENDLICENSE%$ --]]
20
21---
22-- manage a set of processes
23--
24-- basicly, all we do is "add a process to the manager" and "shut them all down"
25--
26
27local _G = _G
28
29module("testenv.processmanager")
30
31---
32-- init a new manager
33--
34function new(self, o)
35 -- create a new processes object
36 --
37 o = o or {}
38 _G.setmetatable(o, self)
39 self.__index = self
40
41 self.processes = { }
42
43 return o
44end
45
46---
47-- add a process
48function add(self, proc)
49 _G.table.insert(self.processes, proc)
50end
51
52---
53-- stop all monitored processes
54--
55function shutdown_all(self)
56 -- shuts down every proxy in the proxy list
57 --
58 for proc_name, proc in _G.pairs(self.processes) do
59 if proc.pid then
60 proc:shutdown()
61 end
62 end
63
64 for proc_name, proc in _G.pairs(self.processes) do
65 if proc.pid then
66 proc:wait_down()
67 end
68 self.processes[proc_name] = nil
69 end
70end
71
72
073
=== added file 'tests/suite/testenv/shellutils.lua'
--- tests/suite/testenv/shellutils.lua 1970-01-01 00:00:00 +0000
+++ tests/suite/testenv/shellutils.lua 2010-09-08 12:58:43 +0000
@@ -0,0 +1,92 @@
1--[[ $%BEGINLICENSE%$
2 Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; version 2 of the
7 License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 02110-1301 USA
18
19 $%ENDLICENSE%$ --]]
20
21module("testenv.shellutils", package.seeall)
22
23VERBOSE = tonumber(os.getenv("VERBOSE")) or 0
24
25---
26-- turn a option-table into a string
27--
28-- the values are encoded and quoted for the shell
29--
30-- @param tbl a option table
31-- @param sep the seperator, defaults to a space
32function options_tostring(tbl, sep)
33 -- default value for sep
34 sep = sep or " "
35
36 assert(type(tbl) == "table")
37 assert(type(sep) == "string")
38
39 local s_tbl = { }
40 for k, v in pairs(tbl) do
41 local values
42 -- if the value is a table, repeat the option
43 if type(v) == "table" then
44 values = v
45 else
46 values = { v }
47 end
48
49 for tk, tv in pairs(values) do
50 if tv == true then
51 table.insert(s_tbl, "--" .. k)
52 else
53 local enc_value = tv:gsub("\\", "\\\\"):gsub("\"", "\\\"")
54 table.insert(s_tbl, "--" .. k .. "=\"" .. enc_value .. "\"")
55 end
56 end
57 end
58 local s = table.concat(s_tbl, " ")
59 -- print_verbose(" option: " .. s)
60
61 return s
62end
63
64--- turns an option table into a string of environment variables
65--
66function env_tostring(tbl)
67 assert(type(tbl) == "table")
68
69 local s = ""
70 for k, v in pairs(tbl) do
71 local enc_value = v:gsub("\\", "\\\\"):gsub("\"", "\\\"")
72 s = s .. k .. "=\"" .. enc_value .. "\" "
73 end
74
75 return s
76end
77
78---
79-- print_verbose()
80--
81-- prints a message if either the DEBUG or VERBOSE variables
82-- are set.
83--
84-- @param msg the message being printed
85-- @param min_level the minimum verbosity level for printing the message (default 1)
86function print_verbose(msg, min_level)
87 min_level = min_level or 1
88 if (VERBOSE >= min_level) then
89 print (msg)
90 end
91end
92
093
=== added file 'tests/suite/testenv/testutils.lua'
--- tests/suite/testenv/testutils.lua 1970-01-01 00:00:00 +0000
+++ tests/suite/testenv/testutils.lua 2010-09-08 12:58:43 +0000
@@ -0,0 +1,271 @@
1--[[ $%BEGINLICENSE%$
2 Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; version 2 of the
7 License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 02110-1301 USA
18
19 $%ENDLICENSE%$ --]]
20
21module("testenv.testutils", package.seeall)
22
23--
24-- Global variables that can be referenced from .options files
25--
26function get_port_base(port_base_start, port_base_end)
27 local port_base_start = port_base_start or 32768
28 local port_base_end = port_base_end or 65535
29 local port_interval = 64 -- let's take the base port in steps of ...
30 local range = port_base_end - port_base_start - port_interval
31 math.randomseed(posix.getpid())
32 local rand = math.floor(math.random() * (math.ceil(range / port_interval)))
33 local port_base = port_base_start + (rand * port_interval)
34
35 print(("... using tcp-port = %d as start port"):format(port_base))
36
37 return port_base
38end
39
40
41-- setting the include path
42--
43
44-- this is the path containing the global Lua modules
45local GLOBAL_LUA_PATH = os.getenv('LUA_LDIR') or '/usr/share/lua/5.1/?.lua'
46
47-- this is the path containing the Proxy libraries
48local PROXY_LUA_PATH = os.getenv('LUA_PATH') or '/usr/local/share/?.lua'
49
50-- This is the path with specific libraries for the test suite
51local PRIVATE_LUA_PATH = arg[1] .. '/t/?.lua'
52
53-- This is the path with additional libraries that the user needs
54local LUA_USER_PATH = os.getenv('LUA_USER_PATH') or '../lib/?.lua'
55
56-- Building the final include path
57local INCLUDE_PATH =
58 LUA_USER_PATH .. ';' ..
59 PRIVATE_LUA_PATH .. ';' ..
60 GLOBAL_LUA_PATH .. ';' ..
61 PROXY_LUA_PATH
62
63---
64-- start_proxy()
65--
66-- starts an instance of MySQL Proxy
67--
68-- @param proxy_name internal name of the proxy instance, for retrieval
69-- @param proxy_options the options to start the Proxy
70function start_proxy(proxy_name, proxy_options)
71 -- start the proxy
72 assert(type(proxy_options) == 'table')
73 if not file_exists(proxy_options['proxy-lua-script']) then
74 proxy_options['proxy-lua-script'] =
75 global_basedir ..
76 '/t/' .. proxy_options['proxy-lua-script']
77 end
78 print_verbose("starting " .. proxy_name .. " with " .. options_tostring(proxy_options))
79
80 -- remove the old pid-file if it exists
81 os.remove(proxy_options['pid-file'])
82 -- if we are supposed to listen on a UNIX socket, remove it first, because we don't clean it up on exit!
83 -- TODO: fix the code, instead of hacking around here! Bug#38415
84 if proxy_options['proxy-address'] == '/tmp/mysql-proxy-test.sock' then
85 os.remove(proxy_options['proxy-address'])
86 end
87 -- os.execute("head " .. proxy_options['proxy-lua-script'])
88 assert(os.execute( 'LUA_PATH="' .. INCLUDE_PATH .. '" ' ..
89 PROXY_TRACE .. " " .. PROXY_BINPATH .. " " ..
90 options_tostring( proxy_options) .. " &"
91 ))
92
93 -- wait until the proxy is up
94 proc = Process.new()
95 proc.set_pid_from_pidfile(proxy_options['pid-file'])
96
97 procs[proxy_name] = proc
98end
99
100---
101-- simulate_replication()
102--
103-- creates a fake master/slave by having two proxies
104-- pointing at the same backend
105--
106-- you can alter those backends by changing
107-- the starting parameters
108--
109-- @param master_options options for master
110-- @param slave_options options for slave
111function simulate_replication(master_options, slave_options)
112 if not master_options then
113 master_options = default_master_options
114 end
115 if not master_options['pid-file'] then
116 master_options['pid-file'] = PROXY_MASTER_PIDFILE
117 end
118 if not slave_options then
119 slave_options = default_slave_options
120 end
121 if not slave_options['pid-file'] then
122 slave_options['pid-file'] = PROXY_SLAVE_PIDFILE
123 end
124 start_proxy('master', master_options)
125 start_proxy('slave', slave_options)
126end
127
128---
129-- chain_proxy()
130--
131-- starts two proxy instances, with the first one is pointing to the
132-- default backend server, and the second one (with default ports)
133-- is pointing at the first proxy
134--
135-- client -> proxy -> backend_proxy -> [ mysql-backend ]
136--
137-- usually we use it to mock a server with a lua script (...-mock.lua) and
138-- the script under test in the proxy (...-test.lua)
139--
140-- in case you want to start several backend_proxies, just provide a array
141-- as first param
142--
143-- @param backend_lua_script
144-- @param second_lua_script
145-- @param use_replication uses a master proxy as backend
146function chain_proxy (backend_lua_scripts, second_lua_script, use_replication)
147 local backends = { }
148
149 if type(backend_lua_scripts) == "table" then
150 backends = backend_lua_scripts
151 else
152 backends = { backend_lua_scripts }
153 end
154
155 local backend_addresses = { }
156
157 for i, backend_lua_script in ipairs(backends) do
158 backend_addresses[i] = PROXY_HOST .. ":" .. (PROXY_CHAIN_PORT + i - 1)
159
160 backend_proxy_options = {
161 ["proxy-backend-addresses"] = MYSQL_HOST .. ":" .. MYSQL_PORT,
162 ["proxy-address"] = backend_addresses[i],
163 ["admin-address"] = PROXY_HOST .. ":" .. (ADMIN_CHAIN_PORT + i - 1),
164 ["admin-username"] = ADMIN_USER,
165 ["admin-password"] = ADMIN_PASSWORD,
166 ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
167 ["pid-file"] = PROXY_CHAIN_PIDFILE .. i,
168 ["proxy-lua-script"] = backend_lua_script or DEFAULT_SCRIPT_FILENAME,
169 ["plugin-dir"] = PROXY_LIBPATH,
170 ["basedir"] = PROXY_TEST_BASEDIR,
171 ["log-level"] = (VERBOSE == 4) and "debug" or "critical",
172 }
173 --
174 -- if replication was not started, then it is started here
175 --
176 if use_replication and (use_replication == true) then
177 if (proxy_list['master'] == nil) then
178 simulate_replication()
179 end
180 backend_proxy_options["proxy-backend-addresses"] = PROXY_HOST .. ':' .. PROXY_MASTER_PORT
181 end
182 start_proxy('backend_proxy' .. i, backend_proxy_options)
183 end
184
185 second_proxy_options = {
186 ["proxy-backend-addresses"] = backend_addresses ,
187 ["proxy-address"] = PROXY_HOST .. ":" .. PROXY_PORT,
188 ["admin-address"] = PROXY_HOST .. ":" .. ADMIN_PORT,
189 ["admin-username"] = ADMIN_USER,
190 ["admin-password"] = ADMIN_PASSWORD,
191 ["admin-lua-script"] = ADMIN_DEFAULT_SCRIPT_FILENAME,
192 ["pid-file"] = PROXY_PIDFILE,
193 ["proxy-lua-script"] = second_lua_script or DEFAULT_SCRIPT_FILENAME,
194 ["plugin-dir"] = PROXY_LIBPATH,
195 ["basedir"] = PROXY_TEST_BASEDIR,
196 ["log-level"] = (VERBOSE == 3) and "debug" or "critical",
197 }
198 start_proxy('second_proxy',second_proxy_options)
199end
200
201
202---
203--sql_execute()
204--
205-- Executes a SQL query in a given Proxy
206--
207-- If no Proxy is indicated, the query is passed directly to the backend server
208--
209-- @param query A SQL statement to execute, or a table of SQL statements
210-- @param proxy_name the name of the proxy that executes the query
211function sql_execute(queries, proxy_name)
212 local ret = 0
213 assert(type(queries) == 'string' or type(queries) == 'table', 'invalid type for query' )
214 if type(queries) == 'string' then
215 queries = {queries}
216 end
217 local query = ''
218 for i, q in pairs(queries) do
219 query = query .. ';' .. q
220 end
221
222 if proxy_name then
223 --
224 -- a Proxy name is passed.
225 -- The query is executed with the given proxy
226 local opts = proxy_list[proxy_name]
227 assert(opts,'proxy '.. proxy_name .. ' not active')
228 assert(opts['proxy-address'],'address for proxy '.. proxy_name .. ' not found')
229 local p_host, p_port = opts['proxy-address']:match('(%S+):(%S+)')
230 ret = os_execute( MYSQL_CLIENT_BIN .. ' ' ..
231 options_tostring({
232 user = MYSQL_USER,
233 password = MYSQL_PASSWORD,
234 database = MYSQL_DB,
235 host = p_host,
236 port = p_port,
237 execute = query
238 })
239 )
240 assert(ret == 0, 'error using mysql client ')
241 else
242 --
243 -- No proxy name was passed.
244 -- The query is executed in the backend server
245 --
246 ret = os_execute( MYSQL_CLIENT_BIN .. ' ' ..
247 options_tostring({
248 user = MYSQL_USER,
249 password = MYSQL_PASSWORD,
250 database = MYSQL_DB,
251 host = PROXY_HOST,
252 port = MYSQL_PORT,
253 execute = query
254 })
255 )
256 end
257 return ret
258end
259
260function only_item ( tbl, item)
261 local exists = false
262 for i,v in pairs(tbl) do
263 if i == item then
264 exists = true
265 else
266 return false
267 end
268 end
269 return exists
270end
271

Subscribers

People subscribed via source and target branches