-
Notifications
You must be signed in to change notification settings - Fork 1
ScriptTest
This article describes the tool for script-based project testing. It is located in "tools/scriptTest/" directory. The tool accepts files written in a custom scripting language that allows sending and receiving messages to cache/slave servers imitating game client. The responses can be checked for validity. It also supports making SQL queries to database.
The script-based testing is very useful during the development, especially when the client-side functionality is not yet ready. It can also be used to periodically check the whole project functionality for any bugs that might have crept up with code changes.
The "tools/scriptTest/include/" directory contains some useful scripts ready to be included in your project scripts. The "login.txt" and "login2.txt" scripts both register a new user and login into the game server. The difference is that they do it on different connections so you can have both users connected at the same time and possibly do some multiplayer game logic.
The configuration file for scriptTest is called "test.cfg" and contains the following variables:
Configuration variable | Description |
---|---|
database.host | Database host. |
database.name | Database name. |
database.password | Database password. |
database.port | Database port. |
database.user | Database user. |
server.host.cache | Cache server host. |
server.port.cache | Cache server port. |
server.host.game | Game server host. |
server.port.game | Game server port. |
server.host.<type> | Host for slave server of this type. |
server.port.<type> | Port for slave server of this type. |
test.showScript | Output compiled Haxe script to stdout before execution. |
The tool accepts a single command-line parameter with either a file name or a directory name. If the parameter is a directory, all files will be read and executed from it.
Let's start with a script example that should give you an idea of what to expect:
// user 1 registration + login
$name = { 'Auto ' + Std.random(100000000) + ' [$scriptName]'; }
$type = FB
@send.game user.register
name = $name
gender = 1
networkid = $name
networktype = $type
@recv.game
errorCode = ok
@send.game user.login
name = $name
networkid = $name
password =
lang = en
@recv.game user.login
errorCode = ok
id = @save id
This script registers a new user and logs into the game server.
And here is the script that includes login script:
@include include/login.txt
// get self user info
@send.game user.info
@recv.game
errorCode = ok
You can put commentaries in the script with //
in front.
@include <file name>
control token will execute a different script as part of this one. The variables declared in the included script will still be available after it finishes execution.
Usage example:
@include include/login.txt
You can declare script variables with $<variable name> = <value>
line.
Usage examples:
$type = FB
$amount = 100
The right side of the assignment supports executing a Haxe script (also called hscript). The Haxe script is a simplified subset of Haxe language for scripting purposes.
Usage example:
$name = { 'Auto ' + Std.random(100000000) + ' [$scriptName]'; }
// "name" variable will hold something like "Auto 356920 [login.txt]"
At the moment, only Std
, Reflect
and haxe.crypto.Md5
(as Md5
) classes are available inside the script. Note also there is a variable inside. The "scriptName" is a special variable that is always defined. It holds the current name of the executed script file.
Sending the message is done with the @send.<server type> <message type>
control token. Note that the message will not be sent immediately, only the state "sending message" will be set. The message will not be sent until the next control token. The lines after the send token should hold message parameters.
When sending message to cache server, use @send.cache
token.
Usage example:
@send.game user.register
name = $name
gender = 1
networkid = $name
networktype = $type
test = false
Note the variable usage. The message parameters will be set to variable values with proper types.
By default, all parameters are strings. There are two special cases supported: true
and false
. In these cases the parameter will be a boolean type. Since the server handles integer parameters with Params.getInt()
call, if the parameter will actually be a string, it will be converted without problems to integer type.
The message parameter assignment also supports Haxe script with the same limitations. More usage examples:
// "log" is an array
log = { [ 1, 2, 3 ]; }
// "speed" is a float parameter
speed = { 47.30221201; }
// "stage" is an anonymous object
stage = { { test: 10, test2: 20 }; }
The scripting language supports multiple connections to the same server. You can use the extended @send.<server type>.<connection id> <message type>
token for that.
Example usage:
@send.chat.2 user.login
id = $id2
password =
lang = en
info = no info
The tool will open new connection the first time it sees a send token with new connection ID.
Receiving message is done with the @recv.<server type> <message type>
control token. Just like the send token, the extended token is supported for multiple connections: @recv.<server type>.<connection id> <message type>
. The message will also not be received until the next control token. In this case, however, lines after the receive token act as checks, whether the received message parameter actually has that value.
There is also a simplified token without the message type. In that case the script assumes the message is of the same type as the last one sent and will return an error if it is not so.
Usage example:
@recv.game
errorCode = ok
When the message is received, the tool will check for message type and for the "errorCode" parameter to equal "ok".
After receiving the message you can save its parameters as variables. This is done with inline control token @save <variable name>
.
Usage example:
@recv.game user.login
errorCode = ok
id = @save id
You can use ANY keyword instead of a specific message type, that will skip the message type check allowing the script to receive any message. For example:
@recv.game ANY
errorCode = ok
You can go even further by wrapping the generated code for receiving message of any type into a loop, allowing you, for example, to receive state updates from a real-time game (uses Haxe code embedding):
`
for (i in 0...200)
{
_state = _script.receiveMessage("ANY", "game", 1);
var _val = _script.getNameValue("errorCode = ok", false);
}
`
You can disconnect immediately from any server with the dc.<server type>
or dc.<server type>.<connection id>
control token.
Usage example:
@dc.cache
@dc.game.2
You can make SQL queries into database with the @db <query>
control token. If it is a SELECT query, you can check the results for validity like when receiving messages.
Usage example:
@db SELECT * FROM UsersLinked WHERE OldUserID = $id2 AND NewUserID = $id
// will check that the returning row has "olduserid" field and it equals the "id2" variable
olduserid = $id2
Note that SQL queries also support script variables. Each variable will be quoted automatically.
Pausing the script execution is done with the @pause <seconds>
token.
Usage example:
@pause 5
This is usually needed when dealing with the pending SQL queries.
There comes a time when you need to use some conditional expressions, control flow statements or some other things you would expect from a modern programming language. For this, the scripting language has implemented Haxe code embedding. Putting the "`" symbol at the line start will tell the script interpreter that the following lines need to be treated as Haxe script code. Since the tool will compile the script into Haxe script before execution these lines will go into it as is. Which means you can input any Haxe code with the usual Haxe script limitations (described here).
Let's take a look at the example:
// user 1 registration + login
$name = { 'Auto ' + Std.random(100000000) + ' [$scriptName]'; }
$type = FB
@send.game user.register
name = $name
gender = 1
networkid = $name
networktype = $type
@recv.game
errorCode = ok
@send.game user.login
name = $name
networkid = $name
password =
lang = en
@recv.game user.login
errorCode = ok
id = @save id
`
var a = 10;
var b = 'test15';
var c = "test15";
var d = 51.6;
var e = Std.random(100);
_script.setVar('a', a);
_script.setVar('b', b);
_script.setVar('c', c);
_script.setVar('d', d);
_script.setVar('e', e);
trace('a:' + _script.getVar('a'));
trace('b:' + _script.getVar('b'));
trace('c:' + _script.getVar('c'));
trace('d:' + _script.getVar('d'));
trace('e:' + _script.getVar('e'));
if (e > 50)
trace('e > 50!');
else trace('e <= 50!');
`
@pause 5
`
trace(a);
trace(b);
trace(c);
trace(d);
trace(e);
`
Note the usage of Std
class. At the moment the following standard Haxe classes are available in the scripts: Std
, Reflect
, Md5
and Math
.
Also note the usage of "_script" variable. This is one of a few global script variables available for usage. It is a link to the script interpreter itself. You can use the following methods from it (technically you can use any other methods but this is unintentional and may not work as intended):
-
_script.getVar(String)
: returns the script variable value. Returns null if the variable is not found. -
_script.setVar(String, Dynamic)
: sets the script variable to a given value.
If you want to take a look at the compiled script for any reason, you can do that by setting the "test.showScript" configuration variable to 1.
Here's another usage example:
@include include/login.txt
`
for (i in 1...25)
{
_script.setVar('i', i);
`
// start level
@send.game level.start
id = $i
@recv.game
errorCode = ok
// finish level
@send.game level.finish
id = $i
points = 25000
@recv.game
errorCode = ok
`
}
`
In this example we wrap the message sending and responce in Haxe "for" loop. Since we need the loop variable "i" in a message we set the script variable on each loop iteration.
There's a shortcut API that tries to modify user attributes and variables. The user block has to be unlocked for it to work. The API is accessed through the "_user" script variable.
`
_user.setAttr($id, 'testInternal', 10);
`
This example sets the "testInternal" user attribute to 10 for user "$id" (script variable).
`
_user.setVar($id, 'testVariable', 10);
`
This example sets the "testVariable" user variable to 10 for user "$id" (script variable).
There are also a couple of handy tricks worth mentioning. Since the editor communicates with cache server using the same connection mechanism and protocol, you can emulate editor messages for various purposes. Some of the most useful are described in this section.
This example directly sets user attribute whether that user is currently online or offline:
// set moderator flag
@send.cache core/edit.update
type = core/user.block
id = $id
group = attrs
key = isModerator
// you have to use Haxe script to have the parameter of integer type
val = { 1; }
@recv.cache core/user.block
errorCode = ok
This message works exactly like EditServer.updateUser()
and _user.setAttr()
methods.
This example does the same thing through the generic block updating mechanism:
// set duel count
@send.cache core/edit.update
type = core/cache.block
blockType = user
blockID = $id
// since we need a "duel.count" object property, we set it through reflection
blockParams = { var a = { params: { vars: {} } }; Reflect.setField(a.params.vars, "duel.count", 30); return a; }
@recv.cache cache.block
errorCode = ok
This message works exactly like the EditServer.updateBlock()
and _user.setVar()
method. Keep in mind that this is also subject to the same limitations: this message will fail if the block is currently locked.
This example sends a message to reload calendar events:
// reload calendar events
@send.cache core/edit.reload
type = core/calendar.*
@recv.cache core/edit.reload
errorCode = ok
This message works exactly like EditServer.reload()
method.