Load testing script or program.

Posted by Robert Powell on Mon 10 Dec 2007 11:31 PM — 18 posts, 74,777 views.

Australia #0
Im looking for a program or script that is capable of logging in multiple characters and having them perform a series of command over and over, for testing out new code.

Currently i have been doing this in kmuddy with a bunch of triggers and macros, hardly eloquent but works well enough with up to 10 or 15 chars connected.

Idealy i was after a headless solution to this that would run in the background without any interface and have it load up 40 or more chars for testing.

Any ideas or tips on how to write such a beast would be appreciated.

USA #1
If you don't mind using Lua, the LuaSocket library makes it particularly easy to talk to a server and send data to it. So you would write a script that took a character name and password, and then connected to the game, sending those in. Then it would just start sending whatever commands you want.

Of course, if you need trigger support and all that, you'll need to do something a little more complicated. There are terminal MUD clients out there, I just don't know of any offhand... anyhow, one of those might suit your purposes if you need more complicated than just sending data back and forth.
Australia #2

f you don't mind using Lua, the LuaSocket library makes it particularly easy to talk to a server and send data to it. So you would write a script that took a character name and password, and then connected to the game, sending those in. Then it would just start sending whatever commands you want.


Actually that would be perfect, all i need is for it to establish and maintain a connection to the server and send a bunch of commands to the server that i supply it.

I dont know any Lua, but it might be time to learn some high level language like Lua that makes some things simple.
USA #3
Once you have Lua and LuaSocket installed, you would do something like this:


require 'socket'

local port = 1234
local conn = socket.connect("localhost", port)

conn:send("user\n")
conn:send("password\n")
conn:send("\n") -- if you have a MOTD you need to skip for instance

for i = 1, 1000 do
  conn:send("look\n")
  conn:send("north\n")
  conn:send("west\n")
  -- whatever
end


I think that's ridiculously easy... a lot easier than doing it in C at least. (Java is easier than C, but still...)

Most high-level languages are about this easy. Lua happens to be the one I know best...

If you're running a Linux-based system it will be very easy to install Lua. Under Debian-based systems, it would be something like:

sudo aptitude install lua5.1
sudo aptitude install liblua5.1-socket2

Australia #4
I did a bit of reading about lua and came to the conclusion that writing a basic script to do what i want will be ridiculously easy, and thanks for demo as well.

I use Sabayon, a gentoo derivative, so ill emerge the lua packages i need later tonight when i have time to play with it.
Australia #5
Ok i get this error when i try and run that code,

lua: (command line):1: '=' expected near '<eof>'
USA #6
Hmm, how are you running it? If you try to run it line-by-line you might have trouble; I would put it into a single file and then run it like so:

lua foo.lua
Australia #7
If i run lua foo.lua it returns to the prompt and there is no connection made to the server.

But what i was doing was lua -e foo.lua which may not be the right thing to be doing i guess.

ok, so i guess the next question is why didnt i see any connection made to the server.
USA #8
Yeah... lua -e isn't what you want, as that is telling it to run the command "foo.lua" as a Lua string as opposed to reading the contents of the file foo.lua.

I'm also having trouble with the script as given; it appears to be writing too quickly and losing the connecting before the MUD gets a chance to say "hello". I'll look into it more tomorrow.

I guess it couldn't be that easy... :-P
Australia #9
In the mean time i will go and do some more reading on lua and see if i can learn something, Thanks for the help so far.
Australia #10
As nothing much became of this thread, i thought i would revive it to see if anyone made any head way with it or has a script in any language that can spawn a telnet session and send commands to the server.

I have been trying to do this in bash using expect, i can get the script to spawn the session and to enter the username, but it fails on password.


#!/usr/bin/expect 
   spawn telnet localhost 8000
   expect -re "user" 
   send "SomeUserName\r" 
   expect -re "Password" 
   send "SomePassWord\r" 


My idea was i could set up 50 or so scripts like this, start them all from a master script and have them output to nohup and leave them run in an infinite loop till i decide to kill them. Also they would run on the server itself so that i would not have the overhead of all that data traveling to and fro as i currently do running 20 players in mushclients.

Any thoughts and help would be great thanks.
Amended on Thu 05 Jun 2008 05:19 AM by Robert Powell
Australia Forum Administrator #11
I am no expert on expect, but it looks to me like your script would sent to the terminal, not to the MUD server.

You probably should explore using luasocket, but it isn't totally trivial, especially in one script. It might be simpler if you just spawned one script per user, because then it could be "blocking".
Australia #12
Isn't luasocket what we attempted earlier with David but could not get to work?

Im not sure what expect is doing, i can see from the output in the server that telnet connects, that the user is accepted, but it does nothing with the password. Im a a huge loss right now. LOL
Australia Forum Administrator #13
I think using luasocket is the way to go, it is pretty easy, considering the complexities of the underlying problem. I got David's example to work, with a bit of modification:


require 'socket'

local port = 4000
local host = "localhost"  -- or wherever it is
local conn = socket.connect (host, port)

assert (conn:send ("playername\n"))
socket.sleep (1)
assert (conn:send ("password\n"))
socket.sleep (1)
assert (conn:send ("\n\n\n")) -- if you have a MOTD you need to skip for instance
socket.sleep (1)

for i = 1, 10 do
  conn:send ("tell admin hi\n")
  conn:send ("look\n")
  conn:send ("north\n")
  conn:send ("west\n")
  socket.sleep (1)
  -- whatever
end


I threw in a few sleeps, partly because you would want to anyway (no player types that fast!) - and partly because it didn't work without them. According to the documentation for "send" the output is not buffered - effectively that means that if it can't be sent immediately, it is not sent at all.

Any sort of TCP/IP application needs to take into account that nothing is instantaneous. The connection defaults to blocking, I think, which is why you get away with sending the player name right after connecting, however remember that connecting, in itself, takes time.

If you can't get luasocket to work, read this thread:

http://www.gammon.com.au/forum/?id=8319

Once it is basically working, you should be able to run it outside MUSHclient. I did it from the command line, and basicallly to do that you do "lua <filename>" (eg. "lua stresstest.lua").

What programs like MUSHclient do (to send stuff) is something like this:

  • When you need to send, if stuff is outstanding, add it to the back of the queue
  • When the previous send completes (you use "select" to test for this) you take the first item from the queue, and do a "send".
  • The call to "send" tells you how much actually got sent (quite possibly less than you wanted), so you re-add the unsent part to the head of the queue.


I did a tiny client a while ago, see:

http://www.gammon.com.au/forum/?id=2206

That is quite a small C program that nonetheless shows the general technique. Its main loop (the processoutput function) basically loops around indefinitely, pausing on the "select" statement (there is select in the luasocket as well).

The select function is specifically designed to wait for "timeout" seconds, or until you can do something with your connection, whichever comes sooner. In this case, "doing something" could mean "ready to send more stuff". That is where you pull things out of your queue and send them, if required. The timeout can be used to gradually pump more stuff out, even if there is nothing outstanding.

A suitably designed script could set up dozens of connections, and then loop around checking if any of them are ready to do something (since select can check more than one socket at once).

I am reluctant to release a finished script that would do that, as it could be used by "script kiddies" to launch a denial-of-service attack on a MUD server with minimal effort.


Australia #14
I get this following error when i attempt to run the script,

lua: login.lua:7: attempt to index local 'conn' (a nil value)
stack traceback:
login.lua:7: in main chunk
[C]: ?
Any ideas?
Australia Forum Administrator #15
You need strategies to deal with this sort of situation. Looking at the documentation for luasocket, it says under "connect":


In case of error, the method returns nil followed by a string describing the error. In case of success, the method returns 1.


That is a hint that you can use "assert" here with success. That is:


local conn = assert (socket.connect (host, port))


Now without the assert, if I try my suggested code with an invalid port number, I see what you see:


lua: stresstest.lua:7: attempt to index local 'conn' (a nil value)
stack traceback:
	stresstest.lua:7: in main chunk
	[C]: ?


That means there is an error message lurking there.

Now with the assert there I see:


lua: stresstest.lua:5: connection refused
stack traceback:
	[C]: in function 'assert'
	stresstest.lua:5: in main chunk
	[C]: ?


OK, "connection refused". That means either the wrong port, the wrong server, or the MUD isn't up.
Australia #16
I was just going through tinyclient code, its pretty neat, simple and even i can work out whats going on.

I think i might spend a few days playing with it and see what i can come up with.
Australia #17
Ok nick i got it working, actually it works a treat, and it was my mistake earlier also, because i killed of kdevelop and it was in a konsole in kdevelop that i had the server running.

Rebooted the server and it it ran perfectly. Now i need to install lua and luasocket on my server and start to create a heap of scripts to use for testing.