Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
hiredis
Commits
9723a4fc
Commit
9723a4fc
authored
May 22, 2011
by
Pieter Noordhuis
Browse files
More control over host/port and unix path in test.c
parent
5793b999
Changes
1
Hide whitespace changes
Inline
Side-by-side
test.c
View file @
9723a4fc
...
...
@@ -10,6 +10,24 @@
#include "hiredis.h"
enum
connection_type
{
CONN_TCP
,
CONN_UNIX
};
struct
config
{
enum
connection_type
type
;
struct
{
const
char
*
host
;
int
port
;
}
tcp
;
struct
{
const
char
*
path
;
}
unix
;
};
/* The following lines make up our testing "framework" :) */
static
int
tests
=
0
,
fails
=
0
;
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
...
...
@@ -21,16 +39,60 @@ static long long usec(void) {
return
(((
long
long
)
tv
.
tv_sec
)
*
1000000
)
+
tv
.
tv_usec
;
}
static
int
use_unix
=
0
;
static
int
port
=
6379
;
static
redisContext
*
blocking_context
=
NULL
;
static
void
__connect
(
redisContext
**
target
)
{
*
target
=
blocking_context
=
(
use_unix
?
redisConnectUnix
(
"/tmp/redis.sock"
)
:
redisConnect
((
char
*
)
"127.0.0.1"
,
port
));
if
(
blocking_context
->
err
)
{
printf
(
"Connection error: %s
\n
"
,
blocking_context
->
errstr
);
static
redisContext
*
select_database
(
redisContext
*
c
)
{
redisReply
*
reply
;
/* Switch to DB 9 for testing, now that we know we can chat. */
reply
=
redisCommand
(
c
,
"SELECT 9"
);
assert
(
reply
!=
NULL
);
freeReplyObject
(
reply
);
/* Make sure the DB is emtpy */
reply
=
redisCommand
(
c
,
"DBSIZE"
);
assert
(
reply
!=
NULL
);
if
(
reply
->
type
==
REDIS_REPLY_INTEGER
&&
reply
->
integer
==
0
)
{
/* Awesome, DB 9 is empty and we can continue. */
freeReplyObject
(
reply
);
}
else
{
printf
(
"Database #9 is not empty, test can not continue
\n
"
);
exit
(
1
);
}
return
c
;
}
static
void
disconnect
(
redisContext
*
c
)
{
redisReply
*
reply
;
/* Make sure we're on DB 9. */
reply
=
redisCommand
(
c
,
"SELECT 9"
);
assert
(
reply
!=
NULL
);
freeReplyObject
(
reply
);
reply
=
redisCommand
(
c
,
"FLUSHDB"
);
assert
(
reply
!=
NULL
);
freeReplyObject
(
reply
);
/* Free the context as well. */
redisFree
(
c
);
}
static
redisContext
*
connect
(
struct
config
config
)
{
redisContext
*
c
;
if
(
config
.
type
==
CONN_TCP
)
{
c
=
redisConnect
(
config
.
tcp
.
host
,
config
.
tcp
.
port
);
}
else
if
(
config
.
type
==
CONN_UNIX
)
{
c
=
redisConnectUnix
(
config
.
unix
.
path
);
}
else
{
assert
(
NULL
);
}
if
(
c
->
err
)
{
printf
(
"Connection error: %s
\n
"
,
c
->
errstr
);
exit
(
1
);
}
return
select_database
(
c
);
}
static
void
test_format_commands
(
void
)
{
...
...
@@ -123,151 +185,6 @@ static void test_format_commands(void) {
free
(
cmd
);
}
static
void
test_blocking_connection
(
void
)
{
redisContext
*
c
;
redisReply
*
reply
;
int
major
,
minor
;
test
(
"Returns error when host cannot be resolved: "
);
c
=
redisConnect
((
char
*
)
"idontexist.local"
,
port
);
test_cond
(
c
->
err
==
REDIS_ERR_OTHER
&&
strcmp
(
c
->
errstr
,
"Can't resolve: idontexist.local"
)
==
0
);
redisFree
(
c
);
test
(
"Returns error when the port is not open: "
);
c
=
redisConnect
((
char
*
)
"localhost"
,
56380
);
test_cond
(
c
->
err
==
REDIS_ERR_IO
&&
strcmp
(
c
->
errstr
,
"Connection refused"
)
==
0
);
redisFree
(
c
);
__connect
(
&
c
);
test
(
"Is able to deliver commands: "
);
reply
=
redisCommand
(
c
,
"PING"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_STATUS
&&
strcasecmp
(
reply
->
str
,
"pong"
)
==
0
)
freeReplyObject
(
reply
);
/* Switch to DB 9 for testing, now that we know we can chat. */
reply
=
redisCommand
(
c
,
"SELECT 9"
);
freeReplyObject
(
reply
);
/* Make sure the DB is emtpy */
reply
=
redisCommand
(
c
,
"DBSIZE"
);
if
(
reply
->
type
!=
REDIS_REPLY_INTEGER
||
reply
->
integer
!=
0
)
{
printf
(
"Database #9 is not empty, test can not continue
\n
"
);
exit
(
1
);
}
freeReplyObject
(
reply
);
test
(
"Is a able to send commands verbatim: "
);
reply
=
redisCommand
(
c
,
"SET foo bar"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_STATUS
&&
strcasecmp
(
reply
->
str
,
"ok"
)
==
0
)
freeReplyObject
(
reply
);
test
(
"%%s String interpolation works: "
);
reply
=
redisCommand
(
c
,
"SET %s %s"
,
"foo"
,
"hello world"
);
freeReplyObject
(
reply
);
reply
=
redisCommand
(
c
,
"GET foo"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_STRING
&&
strcmp
(
reply
->
str
,
"hello world"
)
==
0
);
freeReplyObject
(
reply
);
test
(
"%%b String interpolation works: "
);
reply
=
redisCommand
(
c
,
"SET %b %b"
,
"foo"
,
3
,
"hello
\x00
world"
,
11
);
freeReplyObject
(
reply
);
reply
=
redisCommand
(
c
,
"GET foo"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_STRING
&&
memcmp
(
reply
->
str
,
"hello
\x00
world"
,
11
)
==
0
)
test
(
"Binary reply length is correct: "
);
test_cond
(
reply
->
len
==
11
)
freeReplyObject
(
reply
);
test
(
"Can parse nil replies: "
);
reply
=
redisCommand
(
c
,
"GET nokey"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_NIL
)
freeReplyObject
(
reply
);
/* test 7 */
test
(
"Can parse integer replies: "
);
reply
=
redisCommand
(
c
,
"INCR mycounter"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_INTEGER
&&
reply
->
integer
==
1
)
freeReplyObject
(
reply
);
test
(
"Can parse multi bulk replies: "
);
freeReplyObject
(
redisCommand
(
c
,
"LPUSH mylist foo"
));
freeReplyObject
(
redisCommand
(
c
,
"LPUSH mylist bar"
));
reply
=
redisCommand
(
c
,
"LRANGE mylist 0 -1"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_ARRAY
&&
reply
->
elements
==
2
&&
!
memcmp
(
reply
->
element
[
0
]
->
str
,
"bar"
,
3
)
&&
!
memcmp
(
reply
->
element
[
1
]
->
str
,
"foo"
,
3
))
freeReplyObject
(
reply
);
/* m/e with multi bulk reply *before* other reply.
* specifically test ordering of reply items to parse. */
test
(
"Can handle nested multi bulk replies: "
);
freeReplyObject
(
redisCommand
(
c
,
"MULTI"
));
freeReplyObject
(
redisCommand
(
c
,
"LRANGE mylist 0 -1"
));
freeReplyObject
(
redisCommand
(
c
,
"PING"
));
reply
=
(
redisCommand
(
c
,
"EXEC"
));
test_cond
(
reply
->
type
==
REDIS_REPLY_ARRAY
&&
reply
->
elements
==
2
&&
reply
->
element
[
0
]
->
type
==
REDIS_REPLY_ARRAY
&&
reply
->
element
[
0
]
->
elements
==
2
&&
!
memcmp
(
reply
->
element
[
0
]
->
element
[
0
]
->
str
,
"bar"
,
3
)
&&
!
memcmp
(
reply
->
element
[
0
]
->
element
[
1
]
->
str
,
"foo"
,
3
)
&&
reply
->
element
[
1
]
->
type
==
REDIS_REPLY_STATUS
&&
strcasecmp
(
reply
->
element
[
1
]
->
str
,
"pong"
)
==
0
);
freeReplyObject
(
reply
);
{
/* Find out Redis version to determine the path for the next test */
const
char
*
field
=
"redis_version:"
;
char
*
p
,
*
eptr
;
reply
=
redisCommand
(
c
,
"INFO"
);
p
=
strstr
(
reply
->
str
,
field
);
major
=
strtol
(
p
+
strlen
(
field
),
&
eptr
,
10
);
p
=
eptr
+
1
;
/* char next to the first "." */
minor
=
strtol
(
p
,
&
eptr
,
10
);
freeReplyObject
(
reply
);
}
test
(
"Returns I/O error when the connection is lost: "
);
reply
=
redisCommand
(
c
,
"QUIT"
);
if
(
major
>=
2
&&
minor
>
0
)
{
/* > 2.0 returns OK on QUIT and read() should be issued once more
* to know the descriptor is at EOF. */
test_cond
(
strcasecmp
(
reply
->
str
,
"OK"
)
==
0
&&
redisGetReply
(
c
,(
void
**
)
&
reply
)
==
REDIS_ERR
);
freeReplyObject
(
reply
);
}
else
{
test_cond
(
reply
==
NULL
);
}
/* On 2.0, QUIT will cause the connection to be closed immediately and
* the read(2) for the reply on QUIT will set the error to EOF.
* On >2.0, QUIT will return with OK and another read(2) needed to be
* issued to find out the socket was closed by the server. In both
* conditions, the error will be set to EOF. */
assert
(
c
->
err
==
REDIS_ERR_EOF
&&
strcmp
(
c
->
errstr
,
"Server closed the connection"
)
==
0
);
redisFree
(
c
);
__connect
(
&
c
);
test
(
"Returns I/O error on socket timeout: "
);
struct
timeval
tv
=
{
0
,
1000
};
assert
(
redisSetTimeout
(
c
,
tv
)
==
REDIS_OK
);
test_cond
(
redisGetReply
(
c
,(
void
**
)
&
reply
)
==
REDIS_ERR
&&
c
->
err
==
REDIS_ERR_IO
&&
errno
==
EAGAIN
);
redisFree
(
c
);
/* Context should be connected */
__connect
(
&
c
);
}
static
void
test_reply_reader
(
void
)
{
redisReader
*
reader
;
void
*
reply
;
...
...
@@ -424,11 +341,160 @@ static void test_reader_functions(void) {
redisReaderFree
(
reader
);
}
static
void
test_throughput
(
void
)
{
static
void
test_blocking_connection_errors
(
void
)
{
redisContext
*
c
;
test
(
"Returns error when host cannot be resolved: "
);
c
=
redisConnect
((
char
*
)
"idontexist.local"
,
6379
);
test_cond
(
c
->
err
==
REDIS_ERR_OTHER
&&
strcmp
(
c
->
errstr
,
"Can't resolve: idontexist.local"
)
==
0
);
redisFree
(
c
);
test
(
"Returns error when the port is not open: "
);
c
=
redisConnect
((
char
*
)
"localhost"
,
1
);
test_cond
(
c
->
err
==
REDIS_ERR_IO
&&
strcmp
(
c
->
errstr
,
"Connection refused"
)
==
0
);
redisFree
(
c
);
test
(
"Returns error when the unix socket path doesn't accept connections: "
);
c
=
redisConnectUnix
((
char
*
)
"/tmp/idontexist.sock"
);
test_cond
(
c
->
err
==
REDIS_ERR_IO
);
/* Don't care about the message... */
redisFree
(
c
);
}
static
void
test_blocking_connection
(
struct
config
config
)
{
redisContext
*
c
;
redisReply
*
reply
;
c
=
connect
(
config
);
test
(
"Is able to deliver commands: "
);
reply
=
redisCommand
(
c
,
"PING"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_STATUS
&&
strcasecmp
(
reply
->
str
,
"pong"
)
==
0
)
freeReplyObject
(
reply
);
test
(
"Is a able to send commands verbatim: "
);
reply
=
redisCommand
(
c
,
"SET foo bar"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_STATUS
&&
strcasecmp
(
reply
->
str
,
"ok"
)
==
0
)
freeReplyObject
(
reply
);
test
(
"%%s String interpolation works: "
);
reply
=
redisCommand
(
c
,
"SET %s %s"
,
"foo"
,
"hello world"
);
freeReplyObject
(
reply
);
reply
=
redisCommand
(
c
,
"GET foo"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_STRING
&&
strcmp
(
reply
->
str
,
"hello world"
)
==
0
);
freeReplyObject
(
reply
);
test
(
"%%b String interpolation works: "
);
reply
=
redisCommand
(
c
,
"SET %b %b"
,
"foo"
,
3
,
"hello
\x00
world"
,
11
);
freeReplyObject
(
reply
);
reply
=
redisCommand
(
c
,
"GET foo"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_STRING
&&
memcmp
(
reply
->
str
,
"hello
\x00
world"
,
11
)
==
0
)
test
(
"Binary reply length is correct: "
);
test_cond
(
reply
->
len
==
11
)
freeReplyObject
(
reply
);
test
(
"Can parse nil replies: "
);
reply
=
redisCommand
(
c
,
"GET nokey"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_NIL
)
freeReplyObject
(
reply
);
/* test 7 */
test
(
"Can parse integer replies: "
);
reply
=
redisCommand
(
c
,
"INCR mycounter"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_INTEGER
&&
reply
->
integer
==
1
)
freeReplyObject
(
reply
);
test
(
"Can parse multi bulk replies: "
);
freeReplyObject
(
redisCommand
(
c
,
"LPUSH mylist foo"
));
freeReplyObject
(
redisCommand
(
c
,
"LPUSH mylist bar"
));
reply
=
redisCommand
(
c
,
"LRANGE mylist 0 -1"
);
test_cond
(
reply
->
type
==
REDIS_REPLY_ARRAY
&&
reply
->
elements
==
2
&&
!
memcmp
(
reply
->
element
[
0
]
->
str
,
"bar"
,
3
)
&&
!
memcmp
(
reply
->
element
[
1
]
->
str
,
"foo"
,
3
))
freeReplyObject
(
reply
);
/* m/e with multi bulk reply *before* other reply.
* specifically test ordering of reply items to parse. */
test
(
"Can handle nested multi bulk replies: "
);
freeReplyObject
(
redisCommand
(
c
,
"MULTI"
));
freeReplyObject
(
redisCommand
(
c
,
"LRANGE mylist 0 -1"
));
freeReplyObject
(
redisCommand
(
c
,
"PING"
));
reply
=
(
redisCommand
(
c
,
"EXEC"
));
test_cond
(
reply
->
type
==
REDIS_REPLY_ARRAY
&&
reply
->
elements
==
2
&&
reply
->
element
[
0
]
->
type
==
REDIS_REPLY_ARRAY
&&
reply
->
element
[
0
]
->
elements
==
2
&&
!
memcmp
(
reply
->
element
[
0
]
->
element
[
0
]
->
str
,
"bar"
,
3
)
&&
!
memcmp
(
reply
->
element
[
0
]
->
element
[
1
]
->
str
,
"foo"
,
3
)
&&
reply
->
element
[
1
]
->
type
==
REDIS_REPLY_STATUS
&&
strcasecmp
(
reply
->
element
[
1
]
->
str
,
"pong"
)
==
0
);
freeReplyObject
(
reply
);
disconnect
(
c
);
}
static
void
test_blocking_io_errors
(
struct
config
config
)
{
redisContext
*
c
;
redisReply
*
reply
;
int
major
,
minor
;
/* Connect to target given by config. */
c
=
connect
(
config
);
{
/* Find out Redis version to determine the path for the next test */
const
char
*
field
=
"redis_version:"
;
char
*
p
,
*
eptr
;
reply
=
redisCommand
(
c
,
"INFO"
);
p
=
strstr
(
reply
->
str
,
field
);
major
=
strtol
(
p
+
strlen
(
field
),
&
eptr
,
10
);
p
=
eptr
+
1
;
/* char next to the first "." */
minor
=
strtol
(
p
,
&
eptr
,
10
);
freeReplyObject
(
reply
);
}
test
(
"Returns I/O error when the connection is lost: "
);
reply
=
redisCommand
(
c
,
"QUIT"
);
if
(
major
>=
2
&&
minor
>
0
)
{
/* > 2.0 returns OK on QUIT and read() should be issued once more
* to know the descriptor is at EOF. */
test_cond
(
strcasecmp
(
reply
->
str
,
"OK"
)
==
0
&&
redisGetReply
(
c
,(
void
**
)
&
reply
)
==
REDIS_ERR
);
freeReplyObject
(
reply
);
}
else
{
test_cond
(
reply
==
NULL
);
}
/* On 2.0, QUIT will cause the connection to be closed immediately and
* the read(2) for the reply on QUIT will set the error to EOF.
* On >2.0, QUIT will return with OK and another read(2) needed to be
* issued to find out the socket was closed by the server. In both
* conditions, the error will be set to EOF. */
assert
(
c
->
err
==
REDIS_ERR_EOF
&&
strcmp
(
c
->
errstr
,
"Server closed the connection"
)
==
0
);
redisFree
(
c
);
c
=
connect
(
config
);
test
(
"Returns I/O error on socket timeout: "
);
struct
timeval
tv
=
{
0
,
1000
};
assert
(
redisSetTimeout
(
c
,
tv
)
==
REDIS_OK
);
test_cond
(
redisGetReply
(
c
,(
void
**
)
&
reply
)
==
REDIS_ERR
&&
c
->
err
==
REDIS_ERR_IO
&&
errno
==
EAGAIN
);
redisFree
(
c
);
}
static
void
test_throughput
(
struct
config
config
)
{
redisContext
*
c
=
connect
(
config
);
redisReply
**
replies
;
int
i
,
num
;
long
long
t1
,
t2
;
redisContext
*
c
=
blocking_context
;
redisReply
**
replies
;
test
(
"Throughput:
\n
"
);
for
(
i
=
0
;
i
<
500
;
i
++
)
...
...
@@ -485,18 +551,8 @@ static void test_throughput(void) {
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
printf
(
"
\t
(%dx LRANGE with 500 elements (pipelined): %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
}
static
void
cleanup
(
void
)
{
redisContext
*
c
=
blocking_context
;
redisReply
*
reply
;
/* Make sure we're on DB 9 */
reply
=
redisCommand
(
c
,
"SELECT 9"
);
assert
(
reply
!=
NULL
);
freeReplyObject
(
reply
);
reply
=
redisCommand
(
c
,
"FLUSHDB"
);
assert
(
reply
!=
NULL
);
freeReplyObject
(
reply
);
redisFree
(
c
);
disconnect
(
c
);
}
// static long __test_callback_flags = 0;
...
...
@@ -599,21 +655,54 @@ static void cleanup(void) {
// }
int
main
(
int
argc
,
char
**
argv
)
{
if
(
argc
>
1
)
{
if
(
strcmp
(
argv
[
1
],
"-s"
)
==
0
)
use_unix
=
1
;
if
((
strcmp
(
argv
[
1
],
"-p"
)
==
0
)
&&
(
argc
==
3
))
port
=
atoi
(
argv
[
2
]);
struct
config
cfg
=
{
.
tcp
=
{
.
host
=
"127.0.0.1"
,
.
port
=
6379
},
.
unix
=
{
.
path
=
"/tmp/redis.sock"
}
};
/* Ignore broken pipe signal (for I/O error tests). */
signal
(
SIGPIPE
,
SIG_IGN
);
/* Parse command line options. */
argv
++
;
argc
--
;
while
(
argc
)
{
if
(
argc
>=
2
&&
!
strcmp
(
argv
[
0
],
"-h"
))
{
argv
++
;
argc
--
;
cfg
.
tcp
.
host
=
argv
[
0
];
}
else
if
(
argc
>=
2
&&
!
strcmp
(
argv
[
0
],
"-p"
))
{
argv
++
;
argc
--
;
cfg
.
tcp
.
port
=
atoi
(
argv
[
0
]);
}
else
if
(
argc
>=
2
&&
!
strcmp
(
argv
[
0
],
"-s"
))
{
argv
++
;
argc
--
;
cfg
.
unix
.
path
=
argv
[
0
];
}
else
{
fprintf
(
stderr
,
"Invalid argument: %s
\n
"
,
argv
[
0
]);
exit
(
1
);
}
argv
++
;
argc
--
;
}
signal
(
SIGPIPE
,
SIG_IGN
);
test_format_commands
();
test_reply_reader
();
test_reader_functions
();
test_blocking_connection
();
// test_nonblocking_connection();
test_throughput
();
cleanup
();
test_blocking_connection_errors
();
printf
(
"
\n
Testing against TCP connection (%s:%d):
\n
"
,
cfg
.
tcp
.
host
,
cfg
.
tcp
.
port
);
cfg
.
type
=
CONN_TCP
;
test_blocking_connection
(
cfg
);
test_blocking_io_errors
(
cfg
);
test_throughput
(
cfg
);
printf
(
"
\n
Testing against Unix socket connection (%s):
\n
"
,
cfg
.
unix
.
path
);
cfg
.
type
=
CONN_UNIX
;
test_blocking_connection
(
cfg
);
test_blocking_io_errors
(
cfg
);
test_throughput
(
cfg
);
if
(
fails
==
0
)
{
printf
(
"ALL TESTS PASSED
\n
"
);
...
...
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