|
|||||||
New to SitePoint Forums? Register here for free!
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
SitePoint Member
Join Date: Jun 2005
Location: Guadalajara, Mexico
Posts: 19
|
Request for comments about Testing sockets code
Hi every one. We have a couple of classes that we wrote beofore deciding to use TDD (Test Driven Development) using simpletest framework. So, this classes need to pass several tests before we continue. The more important classes are:
- AmiProxy - ApSignalHandler AmiProxy is intented to be daemonized later, for now is just a server that connects to Asterisk (VoIP server) and login in it. Then start listening for client connections, so, it works like a proxy between its clients ant Asterisk Server. I have to test several functionality : - connect successfull to Asterisk - Accept clients connections - Relay clients connections.. - and may be more. So, anyway, the point is, i have 2 main problems. The simpler, does it exists in simpletest framework a Console reporter? i would like to use it instead the HtmlReporter()? this is just a dump question, because i will look into the code later, and if its not implemented i will came up with something. The important question, or request for comments, is if is "correct" the approach im taking to test this thing. The code follows below, with a couple of comments: PHP Code:
- The test start, when enter the method testConnectingToManager, removes the flag file (/tmp/etcetc..) - then forks the proces so the parent continue, and the first child start the AmiProxy instance and waits for clients connections - the parent then forks again, so the parent continue, and the child (by now the second child) starts a client and try to send a command (ping) to the proxy. If the proxy is connected, then it should send the command to asterisk, asterisk wil return a response, the response sent to the client, the client will then execute the method specified by OnActionResponse(), then the method writes to the flag file. - the parent will be cheking if the file exists for a while, if the file does not show up, we can tell that something is wrong and kill both childs and assert to false. Any comment will be greatly appreciated.
__________________
Be near me when my light is low, When the blood creeps, and the nerves ***** And tingle; and the heart is sick, And all the wheels of Being slow. |
|
|
|
|
|
#2 | |
|
SitePoint Addict
![]() ![]() ![]() Join Date: Jan 2005
Location: Ireland
Posts: 356
|
Quote:
I am feeling a bit lazy now to look at your test case, but no doubt someone will give it a gander. |
|
|
|
|
|
|
#3 |
|
SitePoint Wizard
![]() ![]() Join Date: Nov 2000
Location: Switzerland
Posts: 2,898
|
You're brave to being doing this in PHP but in a general sense, as far as the test goes, looks you're doing things "right", whatever that means.
Two observations that spring to mind (which may be because I haven't fully understood) - wouldn't it be better to have the server running seperately from the test (perhaps fired up by the test as a shell command - seems like trying to manage both the client and server makes the test alot more complicated? Also, if you'll be using files to exchange test results, it might be worth having some utility functions around to make locking simpler, so you don't get race conditions and the tests are easier to code. Although it's Perl, this blog may have some useful ideas. Otherwise, do the tests have to be written in PHP, even if your server is PHP, given you've got a "api" in the form of a network protocol and execution via the shell. Proc::Simple, for example, might make writing tests much easier and perhaps you'd even find complete test tools ready to use for this kind of problem. I just see nightmares ahead but perhaps that's me. |
|
|
|
|
|
#4 | ||||||
|
SitePoint Member
Join Date: Jun 2005
Location: Guadalajara, Mexico
Posts: 19
|
thanks!
Hi!, thanks both of you for your comments and help.
Quote:
![]() Quote:
Quote:
I have done some research on the web and yep, it seems that more people has problems with test on sockets. But no concrete answers. I saw in guy blog, programming in C++, a couple of thest that take that approach (using a mock server), so that option is getting interesting. Quote:
Quote:
Quote:
any other ideas? im considering seriousely to launch a mock server, i guess it may work better that forking() around. regards.
__________________
Be near me when my light is low, When the blood creeps, and the nerves ***** And tingle; and the heart is sick, And all the wheels of Being slow. |
||||||
|
|
|
|
|
#5 | |
|
eschew sesquipedalians
![]() Join Date: Jun 2003
Location: Iowa, USA
Posts: 3,795
|
Quote:
__________________
Jason Sweat ZCE - jsweat_php@yahoo.com Book: PHP Patterns Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI Detestable (adjective): software that isn't testable. |
|
|
|
|
|
|
#6 | |||
|
SitePoint Wizard
![]() ![]() Join Date: Nov 2000
Location: Switzerland
Posts: 2,898
|
Quote:
Would be interesting to get a detailed handle on how garbage collection works in PHP and how much of the problem is outstanding bugs vs. fundamental issues with that way PHP does things. PHP uses reference counting for garbage collection (as do many scripting languages, as it's easy to implement) so there's the fundamental danger of circular references which require "manual" clean up. This is very similar to the type of issues you can have with Javascript in IE (which also uses ref counting I believe). So assuming you're careful to clean up circular references yourself, in theory PHP shouldn't leak. That said what's been going on with references in PHP recently (which Jeff has now summarized nicely here) may also have been part of the problem - the "memory corruption" that recent fixes have been designed to prevent may also have been a source of "leaks" - memory PHP no longer knows how to free. Cal Henderson's Flickr (PDF) presentation alluded to garbage collection problems they have - they were using PHP as a process to handle images if I remember right (e.g. resize a 1.2mb image) which is where they encountered problems. Otherwise a couple of good links on PHP GC here and here. Quote:
Quote:
|
|||
|
|
|
|
|
#7 |
|
SitePoint Victim
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Apr 2003
Location: London
Posts: 2,385
|
Hi.
I had a smaller scale problem (about 1% of the complexity of yours) testing e-mail. See the fakemail page (http://www.lastcraft.com/fakemail.php) for the eventual solution. I fire up a demon and kill it on every test run. I had more than a few socket issues and some help from Pawel (LIMB) before I was done. Linux is a bit fussy about releasing file descriptors. That was Perl, but I would expect the same problems from an external PHP script. PHP is a lousy testbed for this stuff until Apache/PHP gets proper thread support. Firing up a separate script/process seemed to be the safest to me. yours, Marcus
__________________
Marcus Baker Testing: SimpleTest, Cgreen, Fakemail Other: Phemto dependency injector Books: PHP in Action, 97 things |
|
|
|
|
|
#8 | |
|
eschew sesquipedalians
![]() Join Date: Jun 2003
Location: Iowa, USA
Posts: 3,795
|
Quote:
__________________
Jason Sweat ZCE - jsweat_php@yahoo.com Book: PHP Patterns Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI Detestable (adjective): software that isn't testable. |
|
|
|
|
|
|
#9 |
|
SitePoint Member
Join Date: Jun 2005
Location: Guadalajara, Mexico
Posts: 19
|
taking a decission
It seems to me like i have enough reasons to takea fair decission. I have been reading all the references that all you guys kindly posted. I cannot deny that im a bit "afraid" of what could happend to my daemons, but i guess that with discipline and carefull design it will succeed.
About the testing, i came up with a simple solution that is a mix of what i had and what Marcus made. Not completly decided yet, but the server and client thing has been abstracted into a method, so i can further decide if I fire up two separate scripts with shell commands, or I continue using forks(). Time will tell if forks was not the path to follow. But i guess the PHP developers put a little of effort providing that functions so some dummy guy has to test them, and that would be me -_- Lets give a review of my testing script: - First just fireup a proxy client and a proxy server, the proxy client connects to the proxy server. The proxy server connects at the same time with the Asterisk Manager. Then the proxy client proof the connectivity using a 'ping' action. Until that moment, the first true assert is done if the ping response is received sucessfully, the connectivity is OK. - Second test, client proxy start sending random commands to the Server, the server proxy the actions and responses to/from Asterisk Server. Then i compare the received responses with the expected responses and if are equal, other test passed successfully. - Third test, i will instance a MockAmiConnector, this is a mock instance of AmiConnector. AmiConnector is the class that helps the Proxy Server to get connected with the AsteriskManager. So i will need to fake this connection in order to send fake Events to the Proxy Server. From that events, only the events wich the proxy client is suscribed to must be sent. So i will compare the sent events against the received ones, and again, a true assert will be issued if everything is ok. : well, despite the long explanation, the test is simple. Now, what do you think of this: Im testing this system as a whole, because i think that is better ( and simpler ) to test if it works, or not; than testing small pieces in the system isolated. Advantages and disadvantages?? well, i see: Advantages: - simple, and simple, and simple - Does not compromisse quality, since the test will tell us if it works, or not. - Still is extendable to test more specific parts of the system. Disadvantages: - Is not concrete. So when the test does not pass, it wont tell the developers the exact point of failure. It could be at the socket read function, at the argument parsing etc. etc.... Suggestions? critics?? Best Regards.
__________________
Be near me when my light is low, When the blood creeps, and the nerves ***** And tingle; and the heart is sick, And all the wheels of Being slow. |
|
|
|
|
|
#10 |
|
SitePoint Victim
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Apr 2003
Location: London
Posts: 2,385
|
Hi.
I think you are going the right way having a few end to end tests. It would be nice to have unit tests for each part, but what can you do? You acceptance test until you feel confident that the code is working. You unit test as an aid to writing the code, but if you are writing many times more test code than real code you could achieve better quality with some other technique. Code inspection for example. yours, Marcus
__________________
Marcus Baker Testing: SimpleTest, Cgreen, Fakemail Other: Phemto dependency injector Books: PHP in Action, 97 things |
|
|
|
|
|
#11 |
|
SitePoint Member
Join Date: Jun 2005
Location: Guadalajara, Mexico
Posts: 19
|
ok, im done :)
Marcus: thanks for the suggestion about code inspection. Only time will tell me if this is the correct technique to use.
Getting back to my problem. I was able to solve it, but with a workaround for the events tests, that was the one giving me a headache. Ok, the final code is in here: http://phpmexic.u33.0web-hosting.com...iProxyTest.php this is my simple way of showing you the architecture ( Asterisk VoIP server <--> AmiConnector <--> AmiProxy <--> ApSignalHandler a.k.a. Clients... ) <--> AmiProxyTest ok, first, the logic is always the same 1. The Test class (AmiProxyTest) launch two parallel processes, one of the processes run AmiProxy (the server) and the other one run the client ApSignalHandler 2. The client process is actually an instance of ApSignalHandler getting as reference for the callbacks $this (AmiProxyTest). Thats why some methods in AmiProxyTest are callbacks that are called when the client process receives data. 3. The triggered callbacks write meaninfull data to the defined assert file 4. The parent process waits about 10 seconds or less. After that time the parent kill both child processes (client and server) and then read the information in the assert file to decide the assertions. Ok, that is what happend each time a test method is run. Now, the test methods are: 1. testConnectingToManager() this test sends a ping action from the client, and waits for a pong response from the server. if the response is received ( the callback method triggered ), the response is written to the assert file. Then the parent process will check at the end of the test method if the file has the correct data. 2. testSendingCommands() some commands are predefined in the test, and its expected responses. the client then sends all the commands and waits for the respective responses, then the callback methods write the responses to the file and the parent process will check the responses again. 3. testReceiveAndRelayEvents() this is the difficult part. Events are things that happen when some user dials a number, hangup a call, transfers a call, listen to some recording etc. So events should be random, and cannot be generated from the client (actually we can, but we want to test server events ). So, i think i had 2 clear options. Create a dummy VoIP server, or just fake the events mocking the class AmiConnector, that is the one that helps the proxy to communicate with the VoIP server. So i choosed the option 2. But, sockets are tricky to mock. I could not find an easy way to trick the socket_select() php function, thus i had a new problem. That was resolved using partial mocking, so the connection still was done, then i only mocked the method AmiConnector::ReceiveManagerPackets(). The used some setReturnValueAt() to fake random events. the random events of course, had fixed data, that the parent process read from the assert file and checked it for consistency. ok, now, one thing that just come to my mind to add in the great Marcus simpletest framework is a setReturnRandomValues() or something, because my workaround is ugly. I just made a loop 100 times adding the same chain of values for the events. But that will break when the calls to the mocked method are more than 100 * $number_of_events, after that it will start returning the default value, and for applications that run forever is usefull to remain returning random values. well, but that are just my 2 cents. I will check it out, and may be send a patch to marcus, unless that functionality is already implemented and i missed it. finally i would like to tell that YES, TDD leads you to find better ways of doing things. Through the develop of this simple testing suite, i have refactored some things in the architecture in the proxy that could have lead to failures later. Regards,
__________________
Be near me when my light is low, When the blood creeps, and the nerves ***** And tingle; and the heart is sick, And all the wheels of Being slow. |
|
|
|
|
|
#12 | |
|
SitePoint Addict
![]() ![]() ![]() Join Date: May 2005
Posts: 272
|
Quote:
I have a billing daemon that runs in PHP. I stress tested it by processing about 50 transactions per second for 8 days straight. Memory usage never climbed over 150MB. Sure, it's not as good as writing a daemon in, say, C, but it works quite well. PHP's socket implementation is surprisingly good. |
|
|
|
|
![]() |
| Bookmarks |
«
Previous Thread
|
Next Thread
»
| Thread Tools | |
| Display Modes | |
|
|
|
All times are GMT -7. The time now is 06:17.












Linear Mode
