You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

286 lines
8.6 KiB

==========
Test cases
==========
Introduction
============
There are two separate test case sets for Duktape:
1. Ecmascript test cases for testing Ecmascript compliance
2. Duktape API test cases for testing that the exposed user API works
Ecmascript test cases
=====================
How to test?
------------
There are many unit testing frameworks for Ecmascript such as `Jasmine`_
(see also `List of unit testing frameworks`_). However, when testing an
Ecmascript *implementation*, a testing framework cannot always assume
that even simple language features like functions or exceptions work
correctly.
How to do automatic testing then?
.. _Jasmine: http://pivotal.github.com/jasmine/
.. _List of unit testing frameworks: http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#JavaScript
The current solution is to run an Ecmascript test case file with a command
line interpreter and compare the resulting ``stdout`` text to expected.
Control information, including expected ``stdout`` results, are embedded
into Ecmascript comments which the test runner parses.
The intent of the test cases is to test various features of the implementation
against the specification *and real world behavior*. Thus, the tests are
*not* intended to be strict conformance tests: implementation specific
features and internal behavior are also covered by tests. However, whenever
possible, test output can be compared to output of other Ecmascript engines,
currently: Rhino, NodeJS (V8), and Smjs.
Test case scripts write their output using the ``print()`` function. If
``print()`` is not available for a particular interpretation (as is the case
with NodeJS), a prologue defining it is injected.
Test case format
----------------
Test cases are plain Ecmascript files ending with the extension ``.js`` with
special markup inside comments.
Example::
/*
* Example test.
*
* Expected result is delimited as follows; the expected response
* here is "hello world\n".
*/
/*@include util-foo.js@*/
/*---
{
"slow": false,
"_comment": "optional metadata is encoded as a single JSON object"
}
---*/
/*===
hello world
===*/
if (1) {
print("hello world"); /* automatic newline */
} else {
print("not quite");
}
/*===
second test
===*/
/* there can be multiple "expected" blocks (but only one metadata block) */
print("second test");
The ``/*@include ... @*/`` line is replaced with another Ecmascript file which
is automatically minified to a one-liner. This ensures that utilities can be
included without changing line numbers in the testcase, which is important for
tests checking for linenumbers in stack traces etc. The utilities may be used
both in strict and non-strict programs so they must be compatible with both
(it's probably easiest to make helpers 'use strict' so they'll be strict in
both cases).
The metadata block and all metadata keys are optional. Boolean flags
default to false if metadata block or the key is not present. Current
metadata keys:
* ``slow``: if true, test is slow and increased timelimits are applied
to avoid incorrect timeout errors.
* ``skip``: if true, test is not finished yet, and a failure is not
counted towards failcount.
* ``custom``: if true, some implementation dependent features are tested,
and comparison to other Ecmascript engines is not relevant.
* ``nonstandard``: if true, expected behavior is not standards conformant
but matches real world expectations (different from a purely Duktape
specific features).
* ``endianness``: if set, indicates that testcase requires a specific
endianness. Needed for e.g. some TypedArray testcases. Values:
"little", "big", "mixed".
* ``comment``: optional string to comment on testcase very shortly.
Testcases used to be executable directly prior to Duktape 1.1. With the
include mechanism testcases need to be "prepared" before execution, but it
can be done with a simple utility and the resulting prepped testcases are
independent of the rest of the testing framework.
Known issues
------------
There are several testcases that won't pass at a given time but with the
bugs considered known issues (perhaps even permanently). Earlier these
were part of the testcase metadata, but since the known issues status is
not really testcase related, it is now stored in a separate testcase status
file, ``doc/testcase-known-issues.yaml``.
The YAML file is a list of items with the keys:
* ``test``: testcase name, e.g. ``test-dev-mandel2-func.js``.
* ``specialoptions``: if true, expected behavior requires special feature
options to be given and the test case is expected to fail without them.
If set to a string, the string also indicates that special feature options
are needed, and can explain the options.
* ``knownissue``: if true, the test is categorized as failure but is shown
as a known issue which does not need immediate fixing (i.e. they are not
release blockers). Bugs marked as known issues may be difficult to fix,
may be low priority, or it may uncertain what the desired behavior is.
If set to a string, the string can summarize the issue and is shown in
test case output.
* ``comment``: optional string to comment on testcase status, failure
reason, etc.
Practices
---------
Indentation
:::::::::::
Indent with space, 4 spaces.
Verifying exception type
::::::::::::::::::::::::
Since Ecmascript doesn't require specific error messages for errors
thrown, the messages should not be inspected or printed out in test
cases. Ecmascript does require specific error types though (such as
``TypeError``. These can be verified by printing the ``name``
property of an error object.
For instance::
try {
null.foo = 1;
} catch (e) {
print(e.name);
}
prints::
TypeError
When an error is not supposed to occur in a successful test run, the
exception message can (and should) be printed, as it makes it easier
to resolve a failing test case. This can be done most easily as::
try {
null.foo = 1;
} catch (e) {
print(e.stack || e);
}
This is portable and prints a stack trace when available.
Test cases
----------
Test cases filenames consist of lowercase words delimited by dashes, e.g.::
test-stmt-trycatch.js
The first part of each test case is ``test``. The second part indicates a
major test category. The test categories are not very strictly defined, and
there is currently no tracking of specification coverage.
For example, the following prefix are used:
* ``test-dev-``: development time test cases which demonstrate a particular
issue and may not be very well documented.
* ``test-bug-``: illustrate a particular development time bug which has usually
already been fixed.
* ``test-bi-xxx-``: builtin tests for "xxx", e.g. ``test-bi-string-`` prefix
is for String built-in tests.
Duktape API test cases
======================
Test case format
----------------
Test case files are C files with a ``test()`` function. The test function
gets as its argument an already initialized ``duk_context *`` and print out
text to ``stdout``. The test case can assume ``duktape.h`` and common headers
like ``stdio.h`` have been included. There are also some predefined macros
(like ``TEST_SAFE_CALL()`` and ``TEST_PCALL()``) to minimize duplication in
test case code.
Expected output and metadata is defined as for Ecmascript test cases.
Example::
/*===
Hello world from Ecmascript!
Hello world from C!
===*/
void test(duk_context *ctx) {
duk_push_string("print('Hello world from Ecmascript!');");
duk_eval(ctx);
printf("Hello world from C!\n");
}
Known issues
------------
As for Ecmascript testcases, known issue status is stored in
``doc/testcase-known-issues.yaml``.
Test runner
===========
The current test runner is a NodeJS program which handles both Ecmascript
and API testcases. See ``runtests/runtests.js``.
Remote tests can be executed with a shell script wrapper which copies the
test case over with scp and then executes it with ssh. For instance::
#!/bin/sh
TESTCASE=""
while [ "$1" ]
do
# Parse whatever options may be present, assume last argument is
# testcase.
case "$1" in
--restrict-memory)
shift
;;
*)
TESTCASE=$1
shift
;;
esac
done
scp "$TESTCASE" user@192.168.100.20:/tmp >/dev/null
ssh user@192.168.100.20 "./duk /tmp/`basename $TESTCASE`"
Exit code should be passed through if possible. Be careful about normalizing
newlines (plain LF) if something in the process uses CR LF.
Future work
===========
* Put test cases in a directory hierarchy instead (``test/stmt/trycatch.js``),
perhaps scales better (at the expense of adding hassle to e.g. grepping).