mirror of
https://github.com/MariaDB/server.git
synced 2026-01-06 05:22:24 +03:00
WL#3234 Maria control file manager.
Fitting ma_control_file_test into the mytap unittest framework: new directories: - unittest/storage/ for unit tests of any storage engine - unittest/storage/maria for ... Maria, containing ma_control_file-t. Later, older tests like ma_test*, ma_test_all (but which is Unix dependent in its current form) could move here too. The plugins macro enable building of unittest/storage/X for any enabled engine X which has such a directory. If Falcon wants to have unit tests there too, I may have to merge this patch into 5.x one day. config/ac-macros/plugins.m4: If a storage engine has a directory in unittest/storage, build this directory. configure.in: build storage engines' unit tests. storage/maria/Makefile.am: ma_control_file_test moves to unittest/storage/maria storage/maria/ma_control_file.c: more error codes when opening the control file fails. ma_control_file_end() may now return an error if my_close() failed. storage/maria/ma_control_file.h: more error codes when opening the control file fails. unittest/Makefile.am: adding unit tests for storage engines. Note that unit.pl simply recurses into "storage", so if a unit test for storage engine X has been built previously, and now you re-configure (without making clean) to disable this engine, then the unit test of X will not be rebuilt but will still be present in storage/X, so will be run. unittest/storage/maria/ma_control_file-t.c: Making the test fit the mytap framework (return all the way up the stack instead of assert(); use the mytap functions plan(), ok() etc). Adding test of file too short/long. unittest/storage/maria/Makefile.am: a_control_file-t is added to the Maria unit tests. Later, older tests (ma_test1 etc) could also move here. unittest/storage/Makefile.am: New BitKeeper file ``unittest/storage/Makefile.am''
This commit is contained in:
@@ -280,6 +280,7 @@ AC_DEFUN([MYSQL_CONFIGURE_PLUGINS],[
|
||||
_MYSQL_EMIT_PLUGIN_ACTIONS(m4_bpatsubst(__mysql_plugin_list__, :, [,]))
|
||||
AC_SUBST([mysql_se_dirs])
|
||||
AC_SUBST([mysql_pg_dirs])
|
||||
AC_SUBST([mysql_se_unittest_dirs])
|
||||
])
|
||||
])
|
||||
])
|
||||
@@ -315,6 +316,7 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[
|
||||
])
|
||||
AC_MSG_CHECKING([whether to use ]$3)
|
||||
mysql_use_plugin_dir=""
|
||||
mysql_use_plugin_unittest_dir=""
|
||||
m4_ifdef([$10],[
|
||||
if test "X[$mysql_plugin_]$2" = Xyes -a \
|
||||
"X[$with_plugin_]$2" != Xno -o \
|
||||
@@ -407,10 +409,24 @@ dnl Although this is "pretty", it breaks libmysqld build
|
||||
m4_syscmd(test -f "$6/configure")
|
||||
ifelse(m4_sysval, 0,
|
||||
[AC_CONFIG_SUBDIRS($6)],
|
||||
[AC_CONFIG_FILES($6/Makefile)]
|
||||
[
|
||||
AC_CONFIG_FILES($6/Makefile)
|
||||
m4_syscmd(test -d "unittest/$6")
|
||||
ifelse(m4_sysval, 0,
|
||||
[
|
||||
mysql_use_plugin_unittest_dir="$6"
|
||||
AC_CONFIG_FILES(unittest/$6/Makefile)
|
||||
], [])
|
||||
]
|
||||
)
|
||||
ifelse(m4_substr($6, 0, 8), [storage/],
|
||||
[mysql_se_dirs="$mysql_se_dirs ]m4_substr($6, 8)",
|
||||
[
|
||||
[mysql_se_name="]m4_substr($6, 8)"
|
||||
mysql_se_dirs="$mysql_se_dirs $mysql_se_name"
|
||||
if test -n "$mysql_use_plugin_unittest_dir" ; then
|
||||
mysql_se_unittest_dirs="$mysql_se_unitest_dirs $mysql_se_name"
|
||||
fi
|
||||
],
|
||||
m4_substr($6, 0, 7), [plugin/],
|
||||
[mysql_pg_dirs="$mysql_pg_dirs ]m4_substr($6, 7)",
|
||||
[AC_FATAL([don't know how to handle plugin dir ]$6)])
|
||||
|
||||
@@ -2482,6 +2482,7 @@ AC_SUBST(MAKE_BINARY_DISTRIBUTION_OPTIONS)
|
||||
AC_CONFIG_FILES(Makefile extra/Makefile mysys/Makefile dnl
|
||||
unittest/Makefile unittest/mytap/Makefile unittest/mytap/t/Makefile dnl
|
||||
unittest/mysys/Makefile unittest/examples/Makefile dnl
|
||||
unittest/storage/Makefile dnl
|
||||
strings/Makefile regex/Makefile storage/Makefile dnl
|
||||
man/Makefile BUILD/Makefile vio/Makefile dnl
|
||||
libmysql/Makefile client/Makefile dnl
|
||||
|
||||
@@ -47,7 +47,7 @@ maria_pack_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
noinst_PROGRAMS = ma_test1 ma_test2 ma_test3 ma_rt_test ma_sp_test ma_control_file_test
|
||||
noinst_PROGRAMS = ma_test1 ma_test2 ma_test3 ma_rt_test ma_sp_test
|
||||
noinst_HEADERS = maria_def.h ma_rt_index.h ma_rt_key.h ma_rt_mbr.h \
|
||||
ma_sp_defs.h ma_fulltext.h ma_ftdefs.h ma_ft_test1.h ma_ft_eval.h \
|
||||
ma_control_file.h ha_maria.h
|
||||
@@ -89,12 +89,6 @@ ma_sp_test_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
ma_control_file_test_DEPENDENCIES= $(LIBRARIES)
|
||||
ma_control_file_test_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/storage/myisam/libmyisam.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
libmaria_a_SOURCES = ma_init.c ma_open.c ma_extra.c ma_info.c ma_rkey.c \
|
||||
ma_rnext.c ma_rnext_same.c \
|
||||
ma_search.c ma_page.c ma_key.c ma_locking.c \
|
||||
@@ -113,7 +107,7 @@ libmaria_a_SOURCES = ma_init.c ma_open.c ma_extra.c ma_info.c ma_rkey.c \
|
||||
ha_maria.cc \
|
||||
ma_rt_index.c ma_rt_key.c ma_rt_mbr.c ma_rt_split.c \
|
||||
ma_sp_key.c ma_control_file.c
|
||||
CLEANFILES = test?.MA? FT?.MA? isam.log ma_test_all ma_rt_test.MA? sp_test.MA? maria_control
|
||||
CLEANFILES = test?.MA? FT?.MA? isam.log ma_test_all ma_rt_test.MA? sp_test.MA?
|
||||
|
||||
SUFFIXES = .sh
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ uint32 last_logno;
|
||||
Control file is less then 512 bytes (a disk sector),
|
||||
to be as atomic as possible
|
||||
*/
|
||||
static int control_file_fd;
|
||||
static int control_file_fd= -1;
|
||||
|
||||
static void lsn8store(char *buffer, const LSN *lsn)
|
||||
{
|
||||
@@ -87,15 +87,16 @@ static char simple_checksum(char *buffer, uint size)
|
||||
|
||||
RETURN
|
||||
0 - OK
|
||||
1 - Error
|
||||
1 - Error (in which case the file is left closed)
|
||||
*/
|
||||
int ma_control_file_create_or_open()
|
||||
CONTROL_FILE_ERROR ma_control_file_create_or_open()
|
||||
{
|
||||
char buffer[CONTROL_FILE_SIZE];
|
||||
char name[FN_REFLEN];
|
||||
MY_STAT stat_buff;
|
||||
my_bool create_file;
|
||||
int open_flags= O_BINARY | /*O_DIRECT |*/ O_RDWR;
|
||||
int error= CONTROL_FILE_UNKNOWN_ERROR;
|
||||
DBUG_ENTER("ma_control_file_create_or_open");
|
||||
|
||||
/*
|
||||
@@ -106,16 +107,19 @@ int ma_control_file_create_or_open()
|
||||
DBUG_ASSERT(CONTROL_FILE_LSN_SIZE == (4+4));
|
||||
DBUG_ASSERT(CONTROL_FILE_FILENO_SIZE == 4);
|
||||
|
||||
/* name is concatenation of Maria's home dir and "control" */
|
||||
if (fn_format(name, "control", maria_data_root, "", MYF(MY_WME)) == NullS)
|
||||
DBUG_RETURN(1);
|
||||
if (control_file_fd >= 0) /* already open */
|
||||
DBUG_RETURN(0);
|
||||
|
||||
if (fn_format(name, CONTROL_FILE_BASE_NAME,
|
||||
maria_data_root, "", MYF(MY_WME)) == NullS)
|
||||
DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
|
||||
|
||||
create_file= test(my_access(name,F_OK));
|
||||
|
||||
if (create_file)
|
||||
{
|
||||
if ((control_file_fd= my_create(name, 0, open_flags, MYF(0))) < 0)
|
||||
DBUG_RETURN(1);
|
||||
DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
|
||||
/*
|
||||
TODO: from "man fsync" on Linux:
|
||||
"fsync does not necessarily ensure that the entry in the directory
|
||||
@@ -127,10 +131,10 @@ int ma_control_file_create_or_open()
|
||||
To be safer we should make sure that there are no logs or data/index
|
||||
files around (indeed it could be that the control file alone was deleted
|
||||
or not restored, and we should not go on with life at this point).
|
||||
|
||||
|
||||
TODO: For now we trust (this is alpha version), but for beta if would
|
||||
be great to verify.
|
||||
|
||||
|
||||
We could have a tool which can rebuild the control file, by reading the
|
||||
directory of logs, finding the newest log, reading it to find last
|
||||
checkpoint... Slow but can save your db.
|
||||
@@ -138,7 +142,7 @@ int ma_control_file_create_or_open()
|
||||
|
||||
LSN imposs_lsn= CONTROL_FILE_IMPOSSIBLE_LSN;
|
||||
uint32 imposs_logno= CONTROL_FILE_IMPOSSIBLE_FILENO;
|
||||
|
||||
|
||||
/* init the file with these "undefined" values */
|
||||
DBUG_RETURN(ma_control_file_write_and_force(&imposs_lsn, imposs_logno,
|
||||
CONTROL_FILE_UPDATE_ALL));
|
||||
@@ -147,12 +151,12 @@ int ma_control_file_create_or_open()
|
||||
/* Otherwise, file exists */
|
||||
|
||||
if ((control_file_fd= my_open(name, open_flags, MYF(MY_WME))) < 0)
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (my_stat(name, &stat_buff, MYF(MY_WME)) == NULL)
|
||||
DBUG_RETURN(1);
|
||||
goto err;
|
||||
|
||||
if ((uint)stat_buff.st_size != CONTROL_FILE_SIZE)
|
||||
if (my_stat(name, &stat_buff, MYF(MY_WME)) == NULL)
|
||||
goto err;
|
||||
|
||||
if ((uint)stat_buff.st_size < CONTROL_FILE_SIZE)
|
||||
{
|
||||
/*
|
||||
Given that normally we write only a sector and it's atomic, the only
|
||||
@@ -165,31 +169,43 @@ int ma_control_file_create_or_open()
|
||||
disk/filesystem has a problem.
|
||||
So let's be rigid.
|
||||
*/
|
||||
my_message(0, "wrong file size", MYF(0)); /* TODO: improve errors */
|
||||
my_error(HA_ERR_CRASHED, MYF(0), name);
|
||||
DBUG_RETURN(1);
|
||||
my_message(0, "too small file", MYF(0)); /* TODO: improve errors */
|
||||
error= CONTROL_FILE_TOO_SMALL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ((uint)stat_buff.st_size > CONTROL_FILE_SIZE)
|
||||
{
|
||||
my_message(0, "too big file", MYF(0)); /* TODO: improve errors */
|
||||
error= CONTROL_FILE_TOO_BIG;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (my_read(control_file_fd, buffer, CONTROL_FILE_SIZE,
|
||||
MYF(MY_FNABP | MY_WME)))
|
||||
DBUG_RETURN(1);
|
||||
goto err;
|
||||
if (memcmp(buffer + CONTROL_FILE_MAGIC_STRING_OFFSET,
|
||||
CONTROL_FILE_MAGIC_STRING, CONTROL_FILE_MAGIC_STRING_SIZE))
|
||||
{
|
||||
my_message(0, "bad magic string", MYF(0));
|
||||
DBUG_RETURN(1);
|
||||
error= CONTROL_FILE_BAD_MAGIC_STRING;
|
||||
goto err;
|
||||
}
|
||||
if (simple_checksum(buffer + CONTROL_FILE_LSN_OFFSET,
|
||||
CONTROL_FILE_SIZE - CONTROL_FILE_LSN_OFFSET) !=
|
||||
buffer[CONTROL_FILE_CHECKSUM_OFFSET])
|
||||
{
|
||||
my_message(0, "checksum mismatch", MYF(0));
|
||||
DBUG_RETURN(1);
|
||||
error= CONTROL_FILE_BAD_CHECKSUM;
|
||||
goto err;
|
||||
}
|
||||
last_checkpoint_lsn= lsn8korr(buffer + CONTROL_FILE_LSN_OFFSET);
|
||||
last_logno= uint4korr(buffer + CONTROL_FILE_FILENO_OFFSET);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
err:
|
||||
ma_control_file_end();
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
@@ -227,6 +243,8 @@ int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno,
|
||||
my_bool update_checkpoint_lsn= FALSE, update_logno= FALSE;
|
||||
DBUG_ENTER("ma_control_file_write_and_force");
|
||||
|
||||
DBUG_ASSERT(control_file_fd >= 0); /* must be open */
|
||||
|
||||
memcpy(buffer + CONTROL_FILE_MAGIC_STRING_OFFSET,
|
||||
CONTROL_FILE_MAGIC_STRING, CONTROL_FILE_MAGIC_STRING_SIZE);
|
||||
|
||||
@@ -259,7 +277,7 @@ int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno,
|
||||
0, MYF(MY_FNABP | MY_WME)) ||
|
||||
my_sync(control_file_fd, MYF(MY_WME)))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
|
||||
/* TODO: you need some protection to be able to write last_* global vars */
|
||||
if (update_checkpoint_lsn)
|
||||
last_checkpoint_lsn= *checkpoint_lsn;
|
||||
@@ -277,15 +295,26 @@ int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno,
|
||||
ma_control_file_end()
|
||||
*/
|
||||
|
||||
void ma_control_file_end()
|
||||
int ma_control_file_end()
|
||||
{
|
||||
int close_error;
|
||||
DBUG_ENTER("ma_control_file_end");
|
||||
my_close(control_file_fd, MYF(MY_WME));
|
||||
|
||||
if (control_file_fd < 0) /* already closed */
|
||||
DBUG_RETURN(0);
|
||||
|
||||
close_error= my_close(control_file_fd, MYF(MY_WME));
|
||||
/*
|
||||
As my_close() frees structures even if close() fails, we do the same,
|
||||
i.e. we mark the file as closed in all cases.
|
||||
*/
|
||||
control_file_fd= -1;
|
||||
/*
|
||||
As this module owns these variables, closing the module forbids access to
|
||||
them (just a safety):
|
||||
*/
|
||||
last_checkpoint_lsn= CONTROL_FILE_IMPOSSIBLE_LSN;
|
||||
last_logno= CONTROL_FILE_IMPOSSIBLE_FILENO;
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
DBUG_RETURN(close_error);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ typedef struct st_lsn {
|
||||
#define maria_data_root "."
|
||||
#endif
|
||||
|
||||
#define CONTROL_FILE_BASE_NAME "maria_control"
|
||||
/*
|
||||
indicate absence of the log file number; first log is always number 1, 0 is
|
||||
impossible.
|
||||
@@ -55,7 +56,15 @@ extern uint32 last_logno;
|
||||
If present, read it to find out last checkpoint's LSN and last log.
|
||||
Called at engine's start.
|
||||
*/
|
||||
int ma_control_file_create_or_open();
|
||||
typedef enum enum_control_file_error {
|
||||
CONTROL_FILE_OK= 0,
|
||||
CONTROL_FILE_TOO_SMALL,
|
||||
CONTROL_FILE_TOO_BIG,
|
||||
CONTROL_FILE_BAD_MAGIC_STRING,
|
||||
CONTROL_FILE_BAD_CHECKSUM,
|
||||
CONTROL_FILE_UNKNOWN_ERROR /* any other error */
|
||||
} CONTROL_FILE_ERROR;
|
||||
CONTROL_FILE_ERROR ma_control_file_create_or_open();
|
||||
|
||||
/*
|
||||
Write information durably to the control file.
|
||||
@@ -66,10 +75,10 @@ int ma_control_file_create_or_open();
|
||||
#define CONTROL_FILE_UPDATE_ONLY_LSN 1
|
||||
#define CONTROL_FILE_UPDATE_ONLY_LOGNO 2
|
||||
int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno,
|
||||
uint objs_to_write);
|
||||
uint objs_to_write);
|
||||
|
||||
|
||||
/* Free resources taken by control file subsystem */
|
||||
void ma_control_file_end();
|
||||
int ma_control_file_end();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,312 +0,0 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Unit test of the control file module of the Maria engine */
|
||||
|
||||
/* TODO: make it fit the mytap framework */
|
||||
|
||||
/*
|
||||
Note that it is not possible to test the durability of the write (can't
|
||||
pull the plug programmatically :)
|
||||
*/
|
||||
|
||||
#include "maria.h"
|
||||
#include "ma_control_file.h"
|
||||
#include <my_getopt.h>
|
||||
|
||||
char file_name[FN_REFLEN];
|
||||
int fd= -1;
|
||||
|
||||
static void clean_files();
|
||||
static void run_test_normal();
|
||||
static void run_test_abnormal();
|
||||
static void usage();
|
||||
static void get_options(int argc, char *argv[]);
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
MY_INIT(argv[0]);
|
||||
|
||||
get_options(argc,argv);
|
||||
|
||||
clean_files();
|
||||
run_test_normal();
|
||||
run_test_abnormal();
|
||||
|
||||
fprintf(stderr, "All tests succeeded\n");
|
||||
exit(0); /* all ok, if some test failed, we will have aborted */
|
||||
}
|
||||
|
||||
/*
|
||||
Abort unless given expression is non-zero.
|
||||
|
||||
SYNOPSIS
|
||||
DIE_UNLESS(expr)
|
||||
|
||||
DESCRIPTION
|
||||
We can't use any kind of system assert as we need to
|
||||
preserve tested invariants in release builds as well.
|
||||
|
||||
NOTE
|
||||
This is infamous copy-paste from mysql_client_test.c;
|
||||
we should instead put it in some include in one single place.
|
||||
*/
|
||||
|
||||
#define DIE_UNLESS(expr) \
|
||||
((void) ((expr) ? 0 : (die(__FILE__, __LINE__, #expr), 0)))
|
||||
#define DIE_IF(expr) \
|
||||
((void) (!(expr) ? 0 : (die(__FILE__, __LINE__, #expr), 0)))
|
||||
#define DIE(expr) \
|
||||
die(__FILE__, __LINE__, #expr)
|
||||
|
||||
void die(const char *file, int line, const char *expr)
|
||||
{
|
||||
fprintf(stderr, "%s:%d: check failed: '%s'\n", file, line, expr);
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static void clean_files()
|
||||
{
|
||||
DIE_IF(fn_format(file_name, "control", maria_data_root, "", MYF(MY_WME)) ==
|
||||
NullS);
|
||||
my_delete(file_name, MYF(0)); /* maybe file does not exist, ignore error */
|
||||
}
|
||||
|
||||
|
||||
static void run_test_normal()
|
||||
{
|
||||
LSN checkpoint_lsn;
|
||||
uint32 logno;
|
||||
uint objs_to_write;
|
||||
uint i;
|
||||
char buffer[17];
|
||||
|
||||
/* TEST0: Instance starts from scratch (control file does not exist) */
|
||||
DIE_UNLESS(ma_control_file_create_or_open() == 0);
|
||||
/* Check that the module reports no information */
|
||||
DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
DIE_UNLESS(last_checkpoint_lsn.file_no == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
DIE_UNLESS(last_checkpoint_lsn.rec_offset == CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET);
|
||||
|
||||
/* TEST1: Simulate creation of one log */
|
||||
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
|
||||
logno= 123;
|
||||
DIE_UNLESS(ma_control_file_write_and_force(NULL, logno,
|
||||
objs_to_write) == 0);
|
||||
/* Check that last_logno was updated */
|
||||
DIE_UNLESS(last_logno == logno);
|
||||
/* Simulate shutdown */
|
||||
ma_control_file_end();
|
||||
/* Verify amnesia */
|
||||
DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
DIE_UNLESS(last_checkpoint_lsn.file_no == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
DIE_UNLESS(last_checkpoint_lsn.rec_offset == CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET);
|
||||
/* And restart */
|
||||
DIE_UNLESS(ma_control_file_create_or_open() == 0);
|
||||
DIE_UNLESS(last_logno == logno);
|
||||
|
||||
/* TEST2: Simulate creation of 5 logs */
|
||||
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
|
||||
logno= 100;
|
||||
for (i= 0; i<5; i++)
|
||||
{
|
||||
logno*= 3;
|
||||
DIE_UNLESS(ma_control_file_write_and_force(NULL, logno,
|
||||
objs_to_write) == 0);
|
||||
}
|
||||
ma_control_file_end();
|
||||
DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
DIE_UNLESS(last_checkpoint_lsn.file_no == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
DIE_UNLESS(last_checkpoint_lsn.rec_offset == CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET);
|
||||
DIE_UNLESS(ma_control_file_create_or_open() == 0);
|
||||
DIE_UNLESS(last_logno == logno);
|
||||
|
||||
/*
|
||||
TEST3: Simulate one checkpoint, one log creation, two checkpoints, one
|
||||
log creation.
|
||||
*/
|
||||
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LSN;
|
||||
checkpoint_lsn= (LSN){5, 10000};
|
||||
logno= 10;
|
||||
DIE_UNLESS(ma_control_file_write_and_force(&checkpoint_lsn, logno,
|
||||
objs_to_write) == 0);
|
||||
/* check that last_logno was not updated */
|
||||
DIE_UNLESS(last_logno != logno);
|
||||
/* Check that last_checkpoint_lsn was updated */
|
||||
DIE_UNLESS(last_checkpoint_lsn.file_no == checkpoint_lsn.file_no);
|
||||
DIE_UNLESS(last_checkpoint_lsn.rec_offset == checkpoint_lsn.rec_offset);
|
||||
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
|
||||
checkpoint_lsn= (LSN){5, 20000};
|
||||
logno= 17;
|
||||
DIE_UNLESS(ma_control_file_write_and_force(&checkpoint_lsn, logno,
|
||||
objs_to_write) == 0);
|
||||
/* Check that checkpoint LSN was not updated */
|
||||
DIE_UNLESS(last_checkpoint_lsn.rec_offset != checkpoint_lsn.rec_offset);
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LSN;
|
||||
checkpoint_lsn= (LSN){17, 20000};
|
||||
DIE_UNLESS(ma_control_file_write_and_force(&checkpoint_lsn, logno,
|
||||
objs_to_write) == 0);
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LSN;
|
||||
checkpoint_lsn= (LSN){17, 45000};
|
||||
DIE_UNLESS(ma_control_file_write_and_force(&checkpoint_lsn, logno,
|
||||
objs_to_write) == 0);
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
|
||||
logno= 19;
|
||||
DIE_UNLESS(ma_control_file_write_and_force(&checkpoint_lsn, logno,
|
||||
objs_to_write) == 0);
|
||||
|
||||
ma_control_file_end();
|
||||
DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
DIE_UNLESS(last_checkpoint_lsn.file_no == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
DIE_UNLESS(last_checkpoint_lsn.rec_offset == CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET);
|
||||
DIE_UNLESS(ma_control_file_create_or_open() == 0);
|
||||
DIE_UNLESS(last_logno == logno);
|
||||
DIE_UNLESS(last_checkpoint_lsn.file_no == checkpoint_lsn.file_no);
|
||||
DIE_UNLESS(last_checkpoint_lsn.rec_offset == checkpoint_lsn.rec_offset);
|
||||
|
||||
/*
|
||||
TEST4: actually check by ourselves the content of the file.
|
||||
Note that constants (offsets) are hard-coded here, precisely to prevent
|
||||
someone from changing them in the control file module and breaking
|
||||
backward-compatibility.
|
||||
TODO: when we reach the format-freeze state, we may even just do a
|
||||
comparison with a raw binary string, to not depend on any uint4korr
|
||||
future change/breakage.
|
||||
*/
|
||||
|
||||
DIE_IF((fd= my_open(file_name,
|
||||
O_BINARY | O_RDWR,
|
||||
MYF(MY_WME))) < 0);
|
||||
DIE_IF(my_read(fd, buffer, 17, MYF(MY_FNABP | MY_WME)) != 0);
|
||||
DIE_IF(my_close(fd, MYF(MY_WME)) != 0);
|
||||
i= uint4korr(buffer+5);
|
||||
DIE_UNLESS(i == last_checkpoint_lsn.file_no);
|
||||
i= uint4korr(buffer+9);
|
||||
DIE_UNLESS(i == last_checkpoint_lsn.rec_offset);
|
||||
i= uint4korr(buffer+13);
|
||||
DIE_UNLESS(i == last_logno);
|
||||
|
||||
|
||||
/* TEST5: Simulate stop/start/nothing/stop/start */
|
||||
|
||||
ma_control_file_end();
|
||||
DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
DIE_UNLESS(ma_control_file_create_or_open() == 0);
|
||||
ma_control_file_end();
|
||||
DIE_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
DIE_UNLESS(ma_control_file_create_or_open() == 0);
|
||||
DIE_UNLESS(last_logno == logno);
|
||||
DIE_UNLESS(last_checkpoint_lsn.file_no == checkpoint_lsn.file_no);
|
||||
DIE_UNLESS(last_checkpoint_lsn.rec_offset == checkpoint_lsn.rec_offset);
|
||||
|
||||
}
|
||||
|
||||
static void run_test_abnormal()
|
||||
{
|
||||
char buffer[4];
|
||||
/* Corrupt the control file */
|
||||
DIE_IF((fd= my_open(file_name,
|
||||
O_BINARY | O_RDWR,
|
||||
MYF(MY_WME))) < 0);
|
||||
DIE_IF(my_pread(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) != 0);
|
||||
DIE_IF(my_pwrite(fd, "papa", 4, 0, MYF(MY_FNABP | MY_WME)) != 0);
|
||||
DIE_IF(my_close(fd, MYF(MY_WME)) != 0);
|
||||
|
||||
/* Check that control file module sees the problem */
|
||||
DIE_IF(ma_control_file_create_or_open() == 0);
|
||||
|
||||
/* Restore it and corrupt it differently */
|
||||
DIE_IF((fd= my_open(file_name,
|
||||
O_BINARY | O_RDWR,
|
||||
MYF(MY_WME))) < 0);
|
||||
/* Restore magic string */
|
||||
DIE_IF(my_pwrite(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) != 0);
|
||||
DIE_IF(my_pread(fd, buffer, 1, 4, MYF(MY_FNABP | MY_WME)) != 0);
|
||||
buffer[1]= buffer[0]+3; /* mangle checksum */
|
||||
DIE_IF(my_pwrite(fd, buffer+1, 1, 4, MYF(MY_FNABP | MY_WME)) != 0);
|
||||
DIE_IF(my_close(fd, MYF(MY_WME)) != 0);
|
||||
|
||||
/* Check that control file module sees the problem */
|
||||
DIE_IF(ma_control_file_create_or_open() == 0);
|
||||
|
||||
/* Note that control file is left corrupted at this point */
|
||||
}
|
||||
|
||||
|
||||
static struct my_option my_long_options[] =
|
||||
{
|
||||
#ifndef DBUG_OFF
|
||||
{"debug", '#', "Debug log.",
|
||||
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||
#endif
|
||||
{"help", '?', "Display help and exit",
|
||||
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"version", 'V', "Print version number and exit",
|
||||
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
static void version()
|
||||
{
|
||||
printf("ma_control_file_test: unit test for the control file "
|
||||
"module of the Maria storage engine. Ver 1.0 \n");
|
||||
}
|
||||
|
||||
static my_bool
|
||||
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
|
||||
char *argument)
|
||||
{
|
||||
switch(optid) {
|
||||
case 'V':
|
||||
version();
|
||||
exit(0);
|
||||
case '#':
|
||||
DBUG_PUSH (argument);
|
||||
break;
|
||||
case '?':
|
||||
version();
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read options */
|
||||
|
||||
static void get_options(int argc, char *argv[])
|
||||
{
|
||||
int ho_error;
|
||||
|
||||
if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
|
||||
exit(ho_error);
|
||||
|
||||
return;
|
||||
} /* get options */
|
||||
|
||||
|
||||
static void usage()
|
||||
{
|
||||
printf("Usage: %s [options]\n\n", my_progname);
|
||||
my_print_help(my_long_options);
|
||||
my_print_variables(my_long_options);
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
SUBDIRS = mytap . mysys examples
|
||||
SUBDIRS = mytap . mysys storage examples
|
||||
|
||||
noinst_SCRIPTS = unit
|
||||
EXTRA_DIST = unit.pl
|
||||
CLEANFILES = unit
|
||||
|
||||
unittests = mytap mysys
|
||||
unittests = mytap mysys storage
|
||||
|
||||
test: unit
|
||||
./unit run $(unittests)
|
||||
|
||||
27
unittest/storage/Makefile.am
Normal file
27
unittest/storage/Makefile.am
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
# Process this file with automake to create Makefile.in
|
||||
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
# These are built from source in the Docs directory
|
||||
EXTRA_DIST =
|
||||
# Cannot use @mysql_se_dirs@ as not all engines have unit tests here
|
||||
SUBDIRS = @mysql_se_unittest_dirs@
|
||||
|
||||
# Don't update the files from bitkeeper
|
||||
%::SCCS/s.%
|
||||
29
unittest/storage/maria/Makefile.am
Normal file
29
unittest/storage/maria/Makefile.am
Normal file
@@ -0,0 +1,29 @@
|
||||
# Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
AM_CPPFLAGS = @ZLIB_INCLUDES@ -I$(top_builddir)/include
|
||||
AM_CPPFLAGS += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap
|
||||
|
||||
# Only reason to link with libmyisam.a here is that it's where some fulltext
|
||||
# pieces are (but soon we'll remove fulltext dependencies from Maria).
|
||||
LDADD= $(top_builddir)/unittest/mytap/libmytap.a \
|
||||
$(top_builddir)/storage/maria/libmaria.a \
|
||||
$(top_builddir)/storage/myisam/libmyisam.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
noinst_PROGRAMS = ma_control_file-t
|
||||
CLEANFILES = maria_control
|
||||
448
unittest/storage/maria/ma_control_file-t.c
Normal file
448
unittest/storage/maria/ma_control_file-t.c
Normal file
@@ -0,0 +1,448 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Unit test of the control file module of the Maria engine WL#3234 */
|
||||
|
||||
/*
|
||||
Note that it is not possible to test the durability of the write (can't
|
||||
pull the plug programmatically :)
|
||||
*/
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <tap.h>
|
||||
|
||||
#ifndef WITH_MARIA_STORAGE_ENGINE
|
||||
/*
|
||||
If Maria is not compiled in, normally we don't come to building this test.
|
||||
*/
|
||||
#error "Maria engine is not compiled in, test cannot be built"
|
||||
#endif
|
||||
|
||||
#include "maria.h"
|
||||
#include "../../../storage/maria/ma_control_file.h"
|
||||
#include <my_getopt.h>
|
||||
|
||||
char file_name[FN_REFLEN];
|
||||
|
||||
/* The values we'll set and expect the control file module to return */
|
||||
LSN expect_checkpoint_lsn;
|
||||
uint32 expect_logno;
|
||||
|
||||
static int delete_file();
|
||||
/*
|
||||
Those are test-specific wrappers around the module's API functions: after
|
||||
calling the module's API functions they perform checks on the result.
|
||||
*/
|
||||
static int close_file(); /* wraps ma_control_file_end */
|
||||
static int create_or_open_file(); /* wraps ma_control_file_open_or_create */
|
||||
static int write_file(); /* wraps ma_control_file_write_and_force */
|
||||
|
||||
/* Tests */
|
||||
static int test_one_log();
|
||||
static int test_five_logs();
|
||||
static int test_3_checkpoints_and_2_logs();
|
||||
static int test_binary_content();
|
||||
static int test_start_stop();
|
||||
static int test_2_open_and_2_close();
|
||||
static int test_bad_magic_string();
|
||||
static int test_bad_checksum();
|
||||
static int test_bad_size();
|
||||
|
||||
/* Utility */
|
||||
static int verify_module_values_match_expected();
|
||||
static int verify_module_values_are_impossible();
|
||||
static void usage();
|
||||
static void get_options(int argc, char *argv[]);
|
||||
|
||||
/*
|
||||
If "expr" is FALSE, this macro will make the function print a diagnostic
|
||||
message and immediately return 1.
|
||||
This is inspired from assert() but does not crash the binary (sometimes we
|
||||
may want to see how other tests go even if one fails).
|
||||
RET_ERR means "return error".
|
||||
*/
|
||||
|
||||
#define RET_ERR_UNLESS(expr) \
|
||||
{if (!(expr)) {diag("line %d: failure: '%s'", __LINE__, #expr); return 1;}}
|
||||
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
MY_INIT(argv[0]);
|
||||
|
||||
plan(9);
|
||||
|
||||
diag("Unit tests for control file");
|
||||
|
||||
get_options(argc,argv);
|
||||
|
||||
diag("Deleting control file at startup, if there is an old one");
|
||||
RET_ERR_UNLESS(0 == delete_file()); /* if fails, can't continue */
|
||||
|
||||
diag("Tests of normal conditions");
|
||||
ok(0 == test_one_log(), "test of creating one log");
|
||||
ok(0 == test_five_logs(), "test of creating five logs");
|
||||
ok(0 == test_3_checkpoints_and_2_logs(),
|
||||
"test of creating three checkpoints and two logs");
|
||||
ok(0 == test_binary_content(), "test of the binary content of the file");
|
||||
ok(0 == test_start_stop(), "test of multiple starts and stops");
|
||||
diag("Tests of abnormal conditions");
|
||||
ok(0 == test_2_open_and_2_close(),
|
||||
"test of two open and two close (strange call sequence)");
|
||||
ok(0 == test_bad_magic_string(), "test of bad magic string");
|
||||
ok(0 == test_bad_checksum(), "test of bad checksum");
|
||||
ok(0 == test_bad_size(), "test of too small/big file");
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
|
||||
|
||||
static int delete_file()
|
||||
{
|
||||
RET_ERR_UNLESS(fn_format(file_name, CONTROL_FILE_BASE_NAME,
|
||||
maria_data_root, "", MYF(MY_WME)) != NullS);
|
||||
/*
|
||||
Maybe file does not exist, ignore error.
|
||||
The error will however be printed on stderr.
|
||||
*/
|
||||
my_delete(file_name, MYF(MY_WME));
|
||||
expect_checkpoint_lsn= CONTROL_FILE_IMPOSSIBLE_LSN;
|
||||
expect_logno= CONTROL_FILE_IMPOSSIBLE_FILENO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Verifies that global values last_checkpoint_lsn and last_logno (belonging
|
||||
to the module) match what we expect.
|
||||
*/
|
||||
static int verify_module_values_match_expected()
|
||||
{
|
||||
RET_ERR_UNLESS(last_logno == expect_logno);
|
||||
RET_ERR_UNLESS(last_checkpoint_lsn.file_no ==
|
||||
expect_checkpoint_lsn.file_no);
|
||||
RET_ERR_UNLESS(last_checkpoint_lsn.rec_offset ==
|
||||
expect_checkpoint_lsn.rec_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Verifies that global values last_checkpoint_lsn and last_logno (belonging
|
||||
to the module) are impossible (this is used when the file has been closed).
|
||||
*/
|
||||
static int verify_module_values_are_impossible()
|
||||
{
|
||||
RET_ERR_UNLESS(last_logno == CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
RET_ERR_UNLESS(last_checkpoint_lsn.file_no ==
|
||||
CONTROL_FILE_IMPOSSIBLE_FILENO);
|
||||
RET_ERR_UNLESS(last_checkpoint_lsn.rec_offset ==
|
||||
CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int close_file()
|
||||
{
|
||||
/* Simulate shutdown */
|
||||
ma_control_file_end();
|
||||
/* Verify amnesia */
|
||||
RET_ERR_UNLESS(verify_module_values_are_impossible() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_or_open_file()
|
||||
{
|
||||
RET_ERR_UNLESS(ma_control_file_create_or_open() == CONTROL_FILE_OK);
|
||||
/* Check that the module reports expected information */
|
||||
RET_ERR_UNLESS(verify_module_values_match_expected() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_file(const LSN *checkpoint_lsn,
|
||||
uint32 logno,
|
||||
uint objs_to_write)
|
||||
{
|
||||
RET_ERR_UNLESS(ma_control_file_write_and_force(checkpoint_lsn, logno,
|
||||
objs_to_write) == 0);
|
||||
/* Check that the module reports expected information */
|
||||
RET_ERR_UNLESS(verify_module_values_match_expected() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_one_log()
|
||||
{
|
||||
uint objs_to_write;
|
||||
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
|
||||
expect_logno= 123;
|
||||
RET_ERR_UNLESS(write_file(NULL, expect_logno,
|
||||
objs_to_write) == 0);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_five_logs()
|
||||
{
|
||||
uint objs_to_write;
|
||||
uint i;
|
||||
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
|
||||
expect_logno= 100;
|
||||
for (i= 0; i<5; i++)
|
||||
{
|
||||
expect_logno*= 3;
|
||||
RET_ERR_UNLESS(write_file(NULL, expect_logno,
|
||||
objs_to_write) == 0);
|
||||
}
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_3_checkpoints_and_2_logs()
|
||||
{
|
||||
uint objs_to_write;
|
||||
/*
|
||||
Simulate one checkpoint, one log creation, two checkpoints, one
|
||||
log creation.
|
||||
*/
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LSN;
|
||||
expect_checkpoint_lsn= (LSN){5, 10000};
|
||||
RET_ERR_UNLESS(write_file(&expect_checkpoint_lsn,
|
||||
expect_logno, objs_to_write) == 0);
|
||||
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
|
||||
expect_logno= 17;
|
||||
RET_ERR_UNLESS(write_file(&expect_checkpoint_lsn,
|
||||
expect_logno, objs_to_write) == 0);
|
||||
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LSN;
|
||||
expect_checkpoint_lsn= (LSN){17, 20000};
|
||||
RET_ERR_UNLESS(write_file(&expect_checkpoint_lsn,
|
||||
expect_logno, objs_to_write) == 0);
|
||||
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LSN;
|
||||
expect_checkpoint_lsn= (LSN){17, 45000};
|
||||
RET_ERR_UNLESS(write_file(&expect_checkpoint_lsn,
|
||||
expect_logno, objs_to_write) == 0);
|
||||
|
||||
objs_to_write= CONTROL_FILE_UPDATE_ONLY_LOGNO;
|
||||
expect_logno= 19;
|
||||
RET_ERR_UNLESS(write_file(&expect_checkpoint_lsn,
|
||||
expect_logno, objs_to_write) == 0);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_binary_content()
|
||||
{
|
||||
uint i;
|
||||
int fd;
|
||||
|
||||
/*
|
||||
TEST4: actually check by ourselves the content of the file.
|
||||
Note that constants (offsets) are hard-coded here, precisely to prevent
|
||||
someone from changing them in the control file module and breaking
|
||||
backward-compatibility.
|
||||
TODO: when we reach the format-freeze state, we may even just do a
|
||||
comparison with a raw binary string, to not depend on any uint4korr
|
||||
future change/breakage.
|
||||
*/
|
||||
|
||||
char buffer[17];
|
||||
RET_ERR_UNLESS((fd= my_open(file_name,
|
||||
O_BINARY | O_RDWR,
|
||||
MYF(MY_WME))) >= 0);
|
||||
RET_ERR_UNLESS(my_read(fd, buffer, 17, MYF(MY_FNABP | MY_WME)) == 0);
|
||||
RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
i= uint4korr(buffer+5);
|
||||
RET_ERR_UNLESS(i == last_checkpoint_lsn.file_no);
|
||||
i= uint4korr(buffer+9);
|
||||
RET_ERR_UNLESS(i == last_checkpoint_lsn.rec_offset);
|
||||
i= uint4korr(buffer+13);
|
||||
RET_ERR_UNLESS(i == last_logno);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_start_stop()
|
||||
{
|
||||
/* TEST5: Simulate start/nothing/stop/start/nothing/stop/start */
|
||||
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_2_open_and_2_close()
|
||||
{
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int test_bad_magic_string()
|
||||
{
|
||||
char buffer[4];
|
||||
int fd;
|
||||
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
|
||||
/* Corrupt magic string */
|
||||
RET_ERR_UNLESS((fd= my_open(file_name,
|
||||
O_BINARY | O_RDWR,
|
||||
MYF(MY_WME))) >= 0);
|
||||
RET_ERR_UNLESS(my_pread(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) == 0);
|
||||
RET_ERR_UNLESS(my_pwrite(fd, "papa", 4, 0, MYF(MY_FNABP | MY_WME)) == 0);
|
||||
|
||||
/* Check that control file module sees the problem */
|
||||
RET_ERR_UNLESS(ma_control_file_create_or_open() ==
|
||||
CONTROL_FILE_BAD_MAGIC_STRING);
|
||||
/* Restore magic string */
|
||||
RET_ERR_UNLESS(my_pwrite(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) == 0);
|
||||
RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_bad_checksum()
|
||||
{
|
||||
char buffer[4];
|
||||
int fd;
|
||||
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
|
||||
/* Corrupt checksum */
|
||||
RET_ERR_UNLESS((fd= my_open(file_name,
|
||||
O_BINARY | O_RDWR,
|
||||
MYF(MY_WME))) >= 0);
|
||||
RET_ERR_UNLESS(my_pread(fd, buffer, 1, 4, MYF(MY_FNABP | MY_WME)) == 0);
|
||||
buffer[0]+= 3; /* mangle checksum */
|
||||
RET_ERR_UNLESS(my_pwrite(fd, buffer+1, 1, 4, MYF(MY_FNABP | MY_WME)) == 0);
|
||||
/* Check that control file module sees the problem */
|
||||
RET_ERR_UNLESS(ma_control_file_create_or_open() ==
|
||||
CONTROL_FILE_BAD_CHECKSUM);
|
||||
/* Restore checksum */
|
||||
buffer[0]-= 3;
|
||||
RET_ERR_UNLESS(my_pwrite(fd, buffer+1, 1, 4, MYF(MY_FNABP | MY_WME)) == 0);
|
||||
RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int test_bad_size()
|
||||
{
|
||||
char buffer[]="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
int fd;
|
||||
|
||||
/* A too short file */
|
||||
RET_ERR_UNLESS(delete_file() == 0);
|
||||
RET_ERR_UNLESS((fd= my_open(file_name,
|
||||
O_BINARY | O_RDWR | O_CREAT,
|
||||
MYF(MY_WME))) >= 0);
|
||||
RET_ERR_UNLESS(my_write(fd, buffer, 10, MYF(MY_FNABP | MY_WME)) == 0);
|
||||
/* Check that control file module sees the problem */
|
||||
RET_ERR_UNLESS(ma_control_file_create_or_open() == CONTROL_FILE_TOO_SMALL);
|
||||
RET_ERR_UNLESS(my_write(fd, buffer, 30, MYF(MY_FNABP | MY_WME)) == 0);
|
||||
/* Check that control file module sees the problem */
|
||||
RET_ERR_UNLESS(ma_control_file_create_or_open() == CONTROL_FILE_TOO_BIG);
|
||||
RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0);
|
||||
|
||||
/* Leave a correct control file */
|
||||
RET_ERR_UNLESS(delete_file() == 0);
|
||||
RET_ERR_UNLESS(create_or_open_file() == CONTROL_FILE_OK);
|
||||
RET_ERR_UNLESS(close_file() == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct my_option my_long_options[] =
|
||||
{
|
||||
#ifndef DBUG_OFF
|
||||
{"debug", '#', "Debug log.",
|
||||
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||
#endif
|
||||
{"help", '?', "Display help and exit",
|
||||
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"version", 'V', "Print version number and exit",
|
||||
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
static void version()
|
||||
{
|
||||
printf("ma_control_file_test: unit test for the control file "
|
||||
"module of the Maria storage engine. Ver 1.0 \n");
|
||||
}
|
||||
|
||||
static my_bool
|
||||
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
|
||||
char *argument)
|
||||
{
|
||||
switch(optid) {
|
||||
case 'V':
|
||||
version();
|
||||
exit(0);
|
||||
case '#':
|
||||
DBUG_PUSH (argument);
|
||||
break;
|
||||
case '?':
|
||||
version();
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read options */
|
||||
|
||||
static void get_options(int argc, char *argv[])
|
||||
{
|
||||
int ho_error;
|
||||
|
||||
if ((ho_error=handle_options(&argc, &argv, my_long_options,
|
||||
get_one_option)))
|
||||
exit(ho_error);
|
||||
|
||||
return;
|
||||
} /* get options */
|
||||
|
||||
|
||||
static void usage()
|
||||
{
|
||||
printf("Usage: %s [options]\n\n", my_progname);
|
||||
my_print_help(my_long_options);
|
||||
my_print_variables(my_long_options);
|
||||
}
|
||||
Reference in New Issue
Block a user