Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
e91d9a6f
Commit
e91d9a6f
authored
Jul 24, 2019
by
Oran Agra
Browse files
Extend modules API to allow modules report to redis INFO
this implements #6012
parent
bc5cb168
Changes
9
Hide whitespace changes
Inline
Side-by-side
runtest-moduleapi
View file @
e91d9a6f
...
@@ -13,4 +13,4 @@ then
...
@@ -13,4 +13,4 @@ then
fi
fi
make
-C
tests/modules
&&
\
make
-C
tests/modules
&&
\
$TCLSH
tests/test_helper.tcl
--single
unit/moduleapi/commandfilter
"
${
@
}
"
$TCLSH
tests/test_helper.tcl
--single
unit/moduleapi/commandfilter
--single
unit/moduleapi/infotest
"
${
@
}
"
src/debug.c
View file @
e91d9a6f
...
@@ -1337,6 +1337,12 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
...
@@ -1337,6 +1337,12 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
/* Log dump of processor registers */
/* Log dump of processor registers */
logRegisters
(
uc
);
logRegisters
(
uc
);
/* Log Modules INFO */
serverLogRaw
(
LL_WARNING
|
LL_RAW
,
"
\n
------ MODULES INFO OUTPUT ------
\n
"
);
infostring
=
modulesCollectInfo
(
sdsempty
(),
NULL
,
1
,
0
);
serverLogRaw
(
LL_WARNING
|
LL_RAW
,
infostring
);
sdsfree
(
infostring
);
#if defined(HAVE_PROC_MAPS)
#if defined(HAVE_PROC_MAPS)
/* Test memory */
/* Test memory */
serverLogRaw
(
LL_WARNING
|
LL_RAW
,
"
\n
------ FAST MEMORY TEST ------
\n
"
);
serverLogRaw
(
LL_WARNING
|
LL_RAW
,
"
\n
------ FAST MEMORY TEST ------
\n
"
);
...
...
src/module.c
View file @
e91d9a6f
...
@@ -40,6 +40,16 @@
...
@@ -40,6 +40,16 @@
* pointers that have an API the module can call with them)
* pointers that have an API the module can call with them)
* -------------------------------------------------------------------------- */
* -------------------------------------------------------------------------- */
typedef
struct
RedisModuleInfoCtx
{
struct
RedisModule
*
module
;
sds
requested_section
;
sds
info
;
/* info string we collected so far */
int
sections
;
/* number of sections we collected so far */
int
in_section
;
/* indication if we're in an active section or not */
}
RedisModuleInfoCtx
;
typedef
void
(
*
RedisModuleInfoFunc
)(
RedisModuleInfoCtx
*
ctx
,
int
for_crash_report
);
/* This structure represents a module inside the system. */
/* This structure represents a module inside the system. */
struct
RedisModule
{
struct
RedisModule
{
void
*
handle
;
/* Module dlopen() handle. */
void
*
handle
;
/* Module dlopen() handle. */
...
@@ -51,6 +61,7 @@ struct RedisModule {
...
@@ -51,6 +61,7 @@ struct RedisModule {
list
*
using
;
/* List of modules we use some APIs of. */
list
*
using
;
/* List of modules we use some APIs of. */
list
*
filters
;
/* List of filters the module has registered. */
list
*
filters
;
/* List of filters the module has registered. */
int
in_call
;
/* RM_Call() nesting level */
int
in_call
;
/* RM_Call() nesting level */
RedisModuleInfoFunc
info_cb
;
/* callback for module to add INFO fields. */
};
};
typedef
struct
RedisModule
RedisModule
;
typedef
struct
RedisModule
RedisModule
;
...
@@ -4686,6 +4697,118 @@ int RM_DictCompare(RedisModuleDictIter *di, const char *op, RedisModuleString *k
...
@@ -4686,6 +4697,118 @@ int RM_DictCompare(RedisModuleDictIter *di, const char *op, RedisModuleString *k
return
res
?
REDISMODULE_OK
:
REDISMODULE_ERR
;
return
res
?
REDISMODULE_OK
:
REDISMODULE_ERR
;
}
}
/* --------------------------------------------------------------------------
* Modules Info fields
* -------------------------------------------------------------------------- */
/* Used to start a new section, before adding any fields. the section name will
* be prefixed by "<modulename>_" and must only include A-Z,a-z,0-9.
* When return value is REDISMODULE_ERR, the section should and will be skipped. */
int
RM_AddInfoSection
(
RedisModuleInfoCtx
*
ctx
,
char
*
name
)
{
sds
full_name
=
sdscatprintf
(
sdsdup
(
ctx
->
module
->
name
),
"_%s"
,
name
);
/* proceed only if:
* 1) no section was requested (emit all)
* 2) the module name was requested (emit all)
* 3) this specific section was requested. */
if
(
ctx
->
requested_section
)
{
if
(
strcasecmp
(
ctx
->
requested_section
,
full_name
)
&&
strcasecmp
(
ctx
->
requested_section
,
ctx
->
module
->
name
))
{
sdsfree
(
full_name
);
ctx
->
in_section
=
0
;
return
REDISMODULE_ERR
;
}
}
if
(
ctx
->
sections
++
)
ctx
->
info
=
sdscat
(
ctx
->
info
,
"
\r\n
"
);
ctx
->
info
=
sdscatprintf
(
ctx
->
info
,
"# %s
\r\n
"
,
full_name
);
ctx
->
in_section
=
1
;
sdsfree
(
full_name
);
return
REDISMODULE_OK
;
}
/* Used by RedisModuleInfoFunc to add info fields.
* Each field will be automatically prefixed by "<modulename>_".
* Field names or values must not include \r\n of ":" */
int
RM_AddInfoFieldString
(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
RedisModuleString
*
value
)
{
if
(
!
ctx
->
in_section
)
return
REDISMODULE_ERR
;
ctx
->
info
=
sdscatprintf
(
ctx
->
info
,
"%s_%s:%s
\r\n
"
,
ctx
->
module
->
name
,
field
,
(
sds
)
value
->
ptr
);
return
REDISMODULE_OK
;
}
int
RM_AddInfoFieldCString
(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
char
*
value
)
{
if
(
!
ctx
->
in_section
)
return
REDISMODULE_ERR
;
ctx
->
info
=
sdscatprintf
(
ctx
->
info
,
"%s_%s:%s
\r\n
"
,
ctx
->
module
->
name
,
field
,
value
);
return
REDISMODULE_OK
;
}
int
RM_AddInfoFieldDouble
(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
double
value
)
{
if
(
!
ctx
->
in_section
)
return
REDISMODULE_ERR
;
ctx
->
info
=
sdscatprintf
(
ctx
->
info
,
"%s_%s:%.17g
\r\n
"
,
ctx
->
module
->
name
,
field
,
value
);
return
REDISMODULE_OK
;
}
int
RM_AddInfoFieldLongLong
(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
long
long
value
)
{
if
(
!
ctx
->
in_section
)
return
REDISMODULE_ERR
;
ctx
->
info
=
sdscatprintf
(
ctx
->
info
,
"%s_%s:%lld
\r\n
"
,
ctx
->
module
->
name
,
field
,
value
);
return
REDISMODULE_OK
;
}
int
RM_AddInfoFieldULongLong
(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
unsigned
long
long
value
)
{
if
(
!
ctx
->
in_section
)
return
REDISMODULE_ERR
;
ctx
->
info
=
sdscatprintf
(
ctx
->
info
,
"%s_%s:%llu
\r\n
"
,
ctx
->
module
->
name
,
field
,
value
);
return
REDISMODULE_OK
;
}
int
RM_RegisterInfoFunc
(
RedisModuleCtx
*
ctx
,
RedisModuleInfoFunc
cb
)
{
ctx
->
module
->
info_cb
=
cb
;
return
REDISMODULE_OK
;
}
sds
modulesCollectInfo
(
sds
info
,
sds
section
,
int
for_crash_report
,
int
sections
)
{
dictIterator
*
di
=
dictGetIterator
(
modules
);
dictEntry
*
de
;
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
struct
RedisModule
*
module
=
dictGetVal
(
de
);
if
(
!
module
->
info_cb
)
continue
;
RedisModuleInfoCtx
info_ctx
=
{
module
,
section
,
info
,
sections
,
0
};
module
->
info_cb
(
&
info_ctx
,
for_crash_report
);
info
=
info_ctx
.
info
;
sections
=
info_ctx
.
sections
;
}
dictReleaseIterator
(
di
);
return
info
;
}
/* --------------------------------------------------------------------------
/* --------------------------------------------------------------------------
* Modules utility APIs
* Modules utility APIs
* -------------------------------------------------------------------------- */
* -------------------------------------------------------------------------- */
...
@@ -5490,4 +5613,11 @@ void moduleRegisterCoreAPI(void) {
...
@@ -5490,4 +5613,11 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API
(
CommandFilterArgInsert
);
REGISTER_API
(
CommandFilterArgInsert
);
REGISTER_API
(
CommandFilterArgReplace
);
REGISTER_API
(
CommandFilterArgReplace
);
REGISTER_API
(
CommandFilterArgDelete
);
REGISTER_API
(
CommandFilterArgDelete
);
REGISTER_API
(
RegisterInfoFunc
);
REGISTER_API
(
AddInfoSection
);
REGISTER_API
(
AddInfoFieldString
);
REGISTER_API
(
AddInfoFieldCString
);
REGISTER_API
(
AddInfoFieldDouble
);
REGISTER_API
(
AddInfoFieldLongLong
);
REGISTER_API
(
AddInfoFieldULongLong
);
}
}
src/redismodule.h
View file @
e91d9a6f
...
@@ -160,6 +160,7 @@ typedef struct RedisModuleDict RedisModuleDict;
...
@@ -160,6 +160,7 @@ typedef struct RedisModuleDict RedisModuleDict;
typedef
struct
RedisModuleDictIter
RedisModuleDictIter
;
typedef
struct
RedisModuleDictIter
RedisModuleDictIter
;
typedef
struct
RedisModuleCommandFilterCtx
RedisModuleCommandFilterCtx
;
typedef
struct
RedisModuleCommandFilterCtx
RedisModuleCommandFilterCtx
;
typedef
struct
RedisModuleCommandFilter
RedisModuleCommandFilter
;
typedef
struct
RedisModuleCommandFilter
RedisModuleCommandFilter
;
typedef
struct
RedisModuleInfoCtx
RedisModuleInfoCtx
;
typedef
int
(
*
RedisModuleCmdFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
);
typedef
int
(
*
RedisModuleCmdFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
);
typedef
void
(
*
RedisModuleDisconnectFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleBlockedClient
*
bc
);
typedef
void
(
*
RedisModuleDisconnectFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleBlockedClient
*
bc
);
...
@@ -173,6 +174,7 @@ typedef void (*RedisModuleTypeFreeFunc)(void *value);
...
@@ -173,6 +174,7 @@ typedef void (*RedisModuleTypeFreeFunc)(void *value);
typedef
void
(
*
RedisModuleClusterMessageReceiver
)(
RedisModuleCtx
*
ctx
,
const
char
*
sender_id
,
uint8_t
type
,
const
unsigned
char
*
payload
,
uint32_t
len
);
typedef
void
(
*
RedisModuleClusterMessageReceiver
)(
RedisModuleCtx
*
ctx
,
const
char
*
sender_id
,
uint8_t
type
,
const
unsigned
char
*
payload
,
uint32_t
len
);
typedef
void
(
*
RedisModuleTimerProc
)(
RedisModuleCtx
*
ctx
,
void
*
data
);
typedef
void
(
*
RedisModuleTimerProc
)(
RedisModuleCtx
*
ctx
,
void
*
data
);
typedef
void
(
*
RedisModuleCommandFilterFunc
)
(
RedisModuleCommandFilterCtx
*
filter
);
typedef
void
(
*
RedisModuleCommandFilterFunc
)
(
RedisModuleCommandFilterCtx
*
filter
);
typedef
void
(
*
RedisModuleInfoFunc
)(
RedisModuleInfoCtx
*
ctx
,
int
for_crash_report
);
#define REDISMODULE_TYPE_METHOD_VERSION 1
#define REDISMODULE_TYPE_METHOD_VERSION 1
typedef
struct
RedisModuleTypeMethods
{
typedef
struct
RedisModuleTypeMethods
{
...
@@ -317,6 +319,13 @@ RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictNext)(RedisModuleCtx *ct
...
@@ -317,6 +319,13 @@ RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictNext)(RedisModuleCtx *ct
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_DictPrev
)(
RedisModuleCtx
*
ctx
,
RedisModuleDictIter
*
di
,
void
**
dataptr
);
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_DictPrev
)(
RedisModuleCtx
*
ctx
,
RedisModuleDictIter
*
di
,
void
**
dataptr
);
int
REDISMODULE_API_FUNC
(
RedisModule_DictCompareC
)(
RedisModuleDictIter
*
di
,
const
char
*
op
,
void
*
key
,
size_t
keylen
);
int
REDISMODULE_API_FUNC
(
RedisModule_DictCompareC
)(
RedisModuleDictIter
*
di
,
const
char
*
op
,
void
*
key
,
size_t
keylen
);
int
REDISMODULE_API_FUNC
(
RedisModule_DictCompare
)(
RedisModuleDictIter
*
di
,
const
char
*
op
,
RedisModuleString
*
key
);
int
REDISMODULE_API_FUNC
(
RedisModule_DictCompare
)(
RedisModuleDictIter
*
di
,
const
char
*
op
,
RedisModuleString
*
key
);
int
REDISMODULE_API_FUNC
(
RedisModule_RegisterInfoFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleInfoFunc
cb
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoSection
)(
RedisModuleInfoCtx
*
ctx
,
char
*
name
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoFieldString
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
RedisModuleString
*
value
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoFieldCString
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
char
*
value
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoFieldDouble
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
double
value
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoFieldLongLong
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
long
long
value
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoFieldULongLong
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
unsigned
long
long
value
);
/* Experimental APIs */
/* Experimental APIs */
#ifdef REDISMODULE_EXPERIMENTAL_API
#ifdef REDISMODULE_EXPERIMENTAL_API
...
@@ -490,6 +499,13 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
...
@@ -490,6 +499,13 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
DictPrev
);
REDISMODULE_GET_API
(
DictPrev
);
REDISMODULE_GET_API
(
DictCompare
);
REDISMODULE_GET_API
(
DictCompare
);
REDISMODULE_GET_API
(
DictCompareC
);
REDISMODULE_GET_API
(
DictCompareC
);
REDISMODULE_GET_API
(
RegisterInfoFunc
);
REDISMODULE_GET_API
(
AddInfoSection
);
REDISMODULE_GET_API
(
AddInfoFieldString
);
REDISMODULE_GET_API
(
AddInfoFieldCString
);
REDISMODULE_GET_API
(
AddInfoFieldDouble
);
REDISMODULE_GET_API
(
AddInfoFieldLongLong
);
REDISMODULE_GET_API
(
AddInfoFieldULongLong
);
#ifdef REDISMODULE_EXPERIMENTAL_API
#ifdef REDISMODULE_EXPERIMENTAL_API
REDISMODULE_GET_API
(
GetThreadSafeContext
);
REDISMODULE_GET_API
(
GetThreadSafeContext
);
...
...
src/server.c
View file @
e91d9a6f
...
@@ -3809,12 +3809,15 @@ sds genRedisInfoString(char *section) {
...
@@ -3809,12 +3809,15 @@ sds genRedisInfoString(char *section) {
time_t
uptime
=
server
.
unixtime
-
server
.
stat_starttime
;
time_t
uptime
=
server
.
unixtime
-
server
.
stat_starttime
;
int
j
;
int
j
;
struct
rusage
self_ru
,
c_ru
;
struct
rusage
self_ru
,
c_ru
;
int
allsections
=
0
,
defsections
=
0
;
int
allsections
=
0
,
defsections
=
0
,
everything
=
0
,
modules
=
0
;
int
sections
=
0
;
int
sections
=
0
;
if
(
section
==
NULL
)
section
=
"default"
;
if
(
section
==
NULL
)
section
=
"default"
;
allsections
=
strcasecmp
(
section
,
"all"
)
==
0
;
allsections
=
strcasecmp
(
section
,
"all"
)
==
0
;
defsections
=
strcasecmp
(
section
,
"default"
)
==
0
;
defsections
=
strcasecmp
(
section
,
"default"
)
==
0
;
everything
=
strcasecmp
(
section
,
"everything"
)
==
0
;
modules
=
strcasecmp
(
section
,
"modules"
)
==
0
;
if
(
everything
)
allsections
=
1
;
getrusage
(
RUSAGE_SELF
,
&
self_ru
);
getrusage
(
RUSAGE_SELF
,
&
self_ru
);
getrusage
(
RUSAGE_CHILDREN
,
&
c_ru
);
getrusage
(
RUSAGE_CHILDREN
,
&
c_ru
);
...
@@ -4357,6 +4360,17 @@ sds genRedisInfoString(char *section) {
...
@@ -4357,6 +4360,17 @@ sds genRedisInfoString(char *section) {
}
}
}
}
}
}
/* Get info from modules.
* if user asked for "everything" or "modules", or a specific section
* that's not found yet. */
if
(
everything
||
modules
||
(
!
allsections
&&
!
defsections
&&
sections
==
0
))
{
info
=
modulesCollectInfo
(
info
,
everything
||
modules
?
NULL
:
section
,
0
,
/* not a crash report */
sections
);
}
return
info
;
return
info
;
}
}
...
...
src/server.h
View file @
e91d9a6f
...
@@ -1528,6 +1528,7 @@ void moduleAcquireGIL(void);
...
@@ -1528,6 +1528,7 @@ void moduleAcquireGIL(void);
void
moduleReleaseGIL
(
void
);
void
moduleReleaseGIL
(
void
);
void
moduleNotifyKeyspaceEvent
(
int
type
,
const
char
*
event
,
robj
*
key
,
int
dbid
);
void
moduleNotifyKeyspaceEvent
(
int
type
,
const
char
*
event
,
robj
*
key
,
int
dbid
);
void
moduleCallCommandFilters
(
client
*
c
);
void
moduleCallCommandFilters
(
client
*
c
);
sds
modulesCollectInfo
(
sds
info
,
sds
section
,
int
for_crash_report
,
int
sections
);
/* Utils */
/* Utils */
long
long
ustime
(
void
);
long
long
ustime
(
void
);
...
...
tests/modules/Makefile
View file @
e91d9a6f
...
@@ -13,12 +13,17 @@ endif
...
@@ -13,12 +13,17 @@ endif
.SUFFIXES
:
.c .so .xo .o
.SUFFIXES
:
.c .so .xo .o
all
:
commandfilter.so
all
:
commandfilter.so
infotest.so
.c.xo
:
.c.xo
:
$(CC)
-I
../../src
$(CFLAGS)
$(SHOBJ_CFLAGS)
-fPIC
-c
$<
-o
$@
$(CC)
-I
../../src
$(CFLAGS)
$(SHOBJ_CFLAGS)
-fPIC
-c
$<
-o
$@
commandfilter.xo
:
../../src/redismodule.h
commandfilter.xo
:
../../src/redismodule.h
infotest.xo
:
../../src/redismodule.h
commandfilter.so
:
commandfilter.xo
commandfilter.so
:
commandfilter.xo
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
infotest.so
:
infotest.xo
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
tests/modules/infotest.c
0 → 100644
View file @
e91d9a6f
#include "redismodule.h"
#include <string.h>
void
InfoFunc
(
RedisModuleInfoCtx
*
ctx
,
int
for_crash_report
)
{
RedisModule_AddInfoSection
(
ctx
,
"Spanish"
);
RedisModule_AddInfoFieldCString
(
ctx
,
"uno"
,
"one"
);
RedisModule_AddInfoFieldLongLong
(
ctx
,
"dos"
,
2
);
RedisModule_AddInfoSection
(
ctx
,
"Italian"
);
RedisModule_AddInfoFieldLongLong
(
ctx
,
"due"
,
2
);
RedisModule_AddInfoFieldDouble
(
ctx
,
"tre"
,
3
.
3
);
if
(
for_crash_report
)
{
RedisModule_AddInfoSection
(
ctx
,
"Klingon"
);
RedisModule_AddInfoFieldCString
(
ctx
,
"one"
,
"wa’"
);
RedisModule_AddInfoFieldCString
(
ctx
,
"two"
,
"cha’"
);
RedisModule_AddInfoFieldCString
(
ctx
,
"three"
,
"wej"
);
}
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"infotest"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_RegisterInfoFunc
(
ctx
,
InfoFunc
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
tests/unit/moduleapi/infotest.tcl
0 → 100644
View file @
e91d9a6f
set testmodule
[
file normalize tests/modules/infotest.so
]
# Return value for INFO property
proc field
{
info property
}
{
if
{[
regexp
"
\r\n
$property:
(.*?)
\r\n
"
$info _ value
]}
{
set _ $value
}
}
start_server
{
tags
{
"modules"
}}
{
r module load $testmodule log-key 0
test
{
module info all
}
{
set info
[
r info all
]
# info all does not contain modules
assert
{
!
[
string match
"*Spanish*"
$info
]
}
assert
{
[
string match
"*used_memory*"
$info
]
}
}
test
{
module info everything
}
{
set info
[
r info everything
]
# info everything contains all default sections, but not ones for crash report
assert
{
[
string match
"*Spanish*"
$info
]
}
assert
{
[
string match
"*Italian*"
$info
]
}
assert
{
[
string match
"*used_memory*"
$info
]
}
assert
{
!
[
string match
"*Klingon*"
$info
]
}
field $info infotest_dos
}
{
2
}
test
{
module info modules
}
{
set info
[
r info modules
]
# info all does not contain modules
assert
{
[
string match
"*Spanish*"
$info
]
}
assert
{
!
[
string match
"*used_memory*"
$info
]
}
}
test
{
module info one module
}
{
set info
[
r info INFOTEST
]
# info all does not contain modules
assert
{
[
string match
"*Spanish*"
$info
]
}
assert
{
!
[
string match
"*used_memory*"
$info
]
}
}
test
{
module info one section
}
{
set info
[
r info INFOTEST_SPANISH
]
assert
{
!
[
string match
"*used_memory*"
$info
]
}
assert
{
!
[
string match
"*Italian*"
$info
]
}
field $info infotest_uno
}
{
one
}
# TODO: test crash report.
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment