C Socket question!

Started by
14 comments, last by hplus0603 14 years, 4 months ago
On many systems select() is implemented internally as poll anyways. epoll() is my favorite wait mechanism for high traffic daemons for 2 reasons: 1) You add the fd once, no building of lists each cycle, and 2) part of the structure used contains a void * param making it very quick to find the object ( class / struct / etc. ) that you need to operate on.

For server testing, I create a tool that will do a specific task related to what I am testing. Then I can run this tool with the server in gdb to test. I also run the daemon used by the other developers under gdb so when they cause it to crash, I have a backtrace.

--Z
Advertisement
What kind of tools do you use to test your server? Which ones? :o
Quote:On many systems select() is implemented internally as poll anyways.


Which makes select() even less efficient, because it needs to build and tear down the list of file descriptors for each call to the kernel. For large numbers of file descriptors (dense select() bit arrays) this gets really expensive.

Quote:testing


There are two layers of testing (at least): Unit testing, and functional testing.

With unit testing, you write code inside your .cpp files. That code runs when in test mode, and calls an individual function of an individual object instance, to verify that given known inputs, the function returns a known output. This is done one function at a time, with as much insulation as possible from the rest of the system. If the function called depends on a file system, a network, a timer etc, then those services are generally mocked (replaced with narrow shims that just return known dummy data).

With functional testing, you generally build some mode into your client such that it can be scripted from the command line, or load and run a script when invoked. That script performs user-level operations (connect to server at address X, send login Y/Z, issue request W, ...). It then verifies that the right thing happens. Generally, you want the ability for a server to run in a "sandbox" where it is self-contained, and you can set up a known good initial state. If you generally use MySQL for database operations, the sandbox might use a different database name, or may even use SQLite instead, for example. For persistent files, the sandbox may put them under /tmp/sandbox instead of whatever the persistent location usually is.

Both kinds of testing are important. Personally, I find it hard to break down certain systems all the way down to unit test level, and some of my unit tests will have a functional flavor -- like, to test the sockets implementation, I may create two sockets, listen on one, connect on the other, and send "hello, world!" and then verify that I can receive that. That exercises more than just a single function on the sockets class, but it can still be run at unit test time because it doesn't need any other information or support (outside of the underlying BSD sockets implementation, natch :-)

Similarly, at other places where your classes interact with the kernel, you may need to make "functional" style unit tests. For example, the function that tests your wrapper for epoll() will likely need to use multiple objects. A simple test for that might look something like:

create three sockets; one listening, two idle
run the epoll with a timeout
verify that you get nothing back
connect one of the idle sockets
run the epoll with a timeout, until timeout expires (fail) or you get "writable" from the connecting, and "readable" from the listening/accepting socket
create a fourth socket based on the "accepting" socket incoming connection
run the epoll with a timeout
verify that you do not get "readable" from any of the sockets
...
enum Bool { True, False, FileNotFound };
Thank you for reply!

So I guess I will need to do more functional test than unit tests. I am using CppTest, I guess it was made to do unit tests, but I will use it for both :)

Here is what I will try:

Create a socket in the server representing the client and do tests like you said, send "Bla" and see if you receive "Bla", all looking like unit tests :p

Is this a good idea?
Google talk on
">Clean Code Talks has an overview of designing code that lends itself well to testing (I'm not sure that is the right video, I only searched by title).

I personally do not fully agree with the topic of constructors being forbidden of doing anything at all (especially in C++ it prevents RAII), but emphasis seems to be on strict Dependency Injection frameworks which invoke indeterminate number of constructors which need to be as light-weight as possible.

All of the rest, especially de-coupling allocation from use is a sound advice, especially in C++.
Because the OS doesn't support dependency injection (without really tricky and expensive work), you will end up having to use functional testing for the OS interface code. There's really no good way around that.
Anything that sits on top of your OS abstraction, though, should be unit testable at the single-function level, if you design it with that as a goal.
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement