Riju has far too many languages to rely on manual testing to ensure that features are not broken by upstream updates. As such, we have a system to automatically generate test cases which are validated whenever language configuration changes.
Here are the tests that can be configured for each language:
run
(mandatory for all languages): Verify that thetemplate
code printsHello, world!
when run using therun
command.repl
(mandatory for all languages withrepl
): Verify that submitting123 * 234
to therepl
command causes28782
to be printed.runrepl
(mandatory for all languages withrepl
): Same asrepl
, but using therun
command instead of therepl
command (i.e., test that therun
command starts a REPL after executingmain
).scope
(optional, only for languages with variable-scope-preservingrepl
): Verify that if a variable assignment is appended tomain
, then that variable can be evaluated from the REPL using therun
command.format
(mandatory for all languages withformat
): Verify that a misformatted version of thetemplate
code is correctly formatted back to its canonical form when executingformat.run
.lsp
(mandatory for all languages withlsp
): Verify that the language server produces a given autocompletion in a given context.ensure
(optional): Verify that a specific shell command executes successfully. This is currently unused.
See jsonschema.yaml
for full
documentation.
run
- If your language can't be made to print exactly
Hello, world!
, specify the actual output using thehello
key.- In extraordinary circumstances, a language may be unable to
produce deterministic output (e.g. Entropy). In such cases,
hello
can also be a JavaScript-compatible regular expression, and you must specifyhelloMaxLength
which is the maximum possible length of theHello, world
output matched by the regex.
- In extraordinary circumstances, a language may be unable to
produce deterministic output (e.g. Entropy). In such cases,
- Some languages require user input at the REPL to run the code,
despite our best efforts to the contrary. In this case, you can
specify the required input in the
helloInput
key. (See "Specifying user input" below.) - If a language doesn't have
repl
, then therun
command is expected to terminate after executing user code. By default the expected exit status is 0, and therun
test will fail otherwise. If for some reason your language exits with a nonzero status even in the absence of an error, then you can specify the expected status in thehelloStatus
key.
- If your language can't be made to print exactly
repl
- We try to compute
123 * 234
in most languages' REPLs. Naturally, the syntax may vary depending on the language, so you can specify an alternate input using theinput
key. (See "Specifying user input" below.) - The result of
123 * 234
is generally28782
. In the case that we get the output in some other format, you can specify the expected output using theoutput
key.
- We try to compute
runrepl
- In the case that
input
needs to be different forrunrepl
than forrepl
, you can override it specifically forrunrepl
using therunReplInput
key. - In the case that
output
needs to be different forrunrepl
than forrepl
, you can override it specifically forrunrepl
using therunReplOutput
key.
- In the case that
scope
- Required: In
scope.code
, specify the code that is needed to assign123 * 234
to a local variable namedx
, or as close to that as the language can manage. For example, in Python, this would bex = 123 * 234
. - By default,
scope.code
is appended at the end oftemplate
. However, if it needs to go in the middle, you can specifyscope.after
, which should match an entire line oftemplate
. Thenscope.code
will be placed on the next list afterscope.after
. - By default, it's expected that typing
x
into the REPL will produce28782
. You can override the input to something else by specifyingscope.input
. (See "Specifying user input" below.) - If the expected output is something other than
28782
, you can override it usingscope.output
.
- Required: In
format
- Required: In
format.input
, specify the input code. This should be distinct fromtemplate
, but should turn intotemplate
when theformat
command is run on it. - In the case that you can't come up with input that formats to
template
, you can specifyformat.output
as the expected result of formattingformat.input
.
- Required: In
lsp
- Required: In
lsp.code
, specify input (not necessarily an entire line) that we should pretend the user has typed. We expect an autocompletion to be presented with the cursor at the end of this input. - Required: In
lsp.item
, specify the text of an autocompletion that we expect the language server to generate. This should not match any text that actually appears intemplate
orlsp.code
. - In
lsp.after
, you can specify a string that will match exactly one place intemplate
. This is where the cursor will be positioned beforelsp.code
is inserted. Iflsp.after
is not specified, thenlsp.code
will be inserted at the end oftemplate
on a new line.
- Required: In
ensure
- If you want to use an
ensure
test, just supply the shell command using theensure
key.
- If you want to use an
We have a couple different formats for common types of user input.
This will type eval
and send a newline:
input: |
eval
This will type eval
, send a newline, then type go
and send another
newline:
input: |
eval
go
This will type foo
, send a newline, wait 1 second, then type bar
and send another newline:
input: |
foo
DELAY: 1
bar
Why is this useful? Unfortunately, many languages have race conditions and will fail to notice input if you send it before they have finished starting up.
Finally, this will type foo
and then send a newline followed by an
EOF:
input: |
foo
EOF
We try very hard to get tests working for every newly added language. However, sometimes there's something truly puzzling going on that's not worth blocking a new language being added. For that reason it's possible to mark a test as temporarily skipped, e.g.:
skip:
- repl
- runrepl
- scope
- lsp
This is unfortunately currently the case for many of the LSP tests due to the fragility of most language servers.