diff --git a/src/main/java/dan200/computercraft/core/apis/OSAPI.java b/src/main/java/dan200/computercraft/core/apis/OSAPI.java
index 9b7b0c943..0ada26bb2 100644
--- a/src/main/java/dan200/computercraft/core/apis/OSAPI.java
+++ b/src/main/java/dan200/computercraft/core/apis/OSAPI.java
@@ -494,7 +494,9 @@ public class OSAPI implements ILuaAPI
DateTimeFormatterBuilder formatter = new DateTimeFormatterBuilder();
LuaDateTime.format( formatter, format );
- return formatter.toFormatter( Locale.ROOT ).format( date );
+ // ROOT would be more sensible, but US appears more consistent with the default C locale
+ // on Linux.
+ return formatter.toFormatter( Locale.US ).format( date );
}
}
diff --git a/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java b/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java
index 095ede05f..b9991d028 100644
--- a/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java
+++ b/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java
@@ -28,8 +28,10 @@ import org.opentest4j.AssertionFailedError;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.BufferedWriter;
+import java.io.File;
import java.io.IOException;
import java.io.Writer;
+import java.net.URI;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
@@ -43,8 +45,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
-import static dan200.computercraft.api.lua.LuaValues.getType;
-
/**
* Loads tests from {@code test-rom/spec} and executes them.
*
@@ -195,36 +195,45 @@ public class ComputerTestDelegate
private static class DynamicNodeBuilder
{
private final String name;
+ private final URI uri;
private final Map children;
private final Executable executor;
- DynamicNodeBuilder( String name )
+ DynamicNodeBuilder( String name, String path )
{
this.name = name;
+ this.uri = getUri( path );
this.children = new HashMap<>();
this.executor = null;
}
- DynamicNodeBuilder( String name, Executable executor )
+ DynamicNodeBuilder( String name, String path, Executable executor )
{
this.name = name;
+ this.uri = getUri( path );
this.children = Collections.emptyMap();
this.executor = executor;
}
+ private static URI getUri( String path )
+ {
+ // Unfortunately ?line=xxx doesn't appear to work with IntelliJ, so don't worry about getting it working.
+ return path == null ? null : new File( "src/test/resources" + path.substring( 0, path.indexOf( ':' ) ) ).toURI();
+ }
+
DynamicNodeBuilder get( String name )
{
DynamicNodeBuilder child = children.get( name );
- if( child == null ) children.put( name, child = new DynamicNodeBuilder( name ) );
+ if( child == null ) children.put( name, child = new DynamicNodeBuilder( name, null ) );
return child;
}
- void runs( String name, Executable executor )
+ void runs( String name, String uri, Executable executor )
{
if( this.executor != null ) throw new IllegalStateException( name + " is leaf node" );
if( children.containsKey( name ) ) throw new IllegalStateException( "Duplicate key for " + name );
- children.put( name, new DynamicNodeBuilder( name, executor ) );
+ children.put( name, new DynamicNodeBuilder( name, uri, executor ) );
}
boolean isActive()
@@ -241,8 +250,8 @@ public class ComputerTestDelegate
DynamicNode build()
{
return executor == null
- ? DynamicContainer.dynamicContainer( name, buildChildren() )
- : DynamicTest.dynamicTest( name, executor );
+ ? DynamicContainer.dynamicContainer( name, uri, buildChildren() )
+ : DynamicTest.dynamicTest( name, uri, executor );
}
Stream buildChildren()
@@ -376,16 +385,17 @@ public class ComputerTestDelegate
{
// Submit several tests and signal for #get to run
LOG.info( "Received tests from computer" );
- DynamicNodeBuilder root = new DynamicNodeBuilder( "" );
- for( Object key : tests.keySet() )
+ DynamicNodeBuilder root = new DynamicNodeBuilder( "", null );
+ for( Map.Entry, ?> entry : tests.entrySet() )
{
- if( !(key instanceof String) ) throw new LuaException( "Non-key string " + getType( key ) );
+ String name = (String) entry.getKey();
+ Map, ?> details = (Map, ?>) entry.getValue();
+ String def = (String) details.get( "definition" );
- String name = (String) key;
String[] parts = name.split( "\0" );
DynamicNodeBuilder builder = root;
for( int i = 0; i < parts.length - 1; i++ ) builder = builder.get( parts[i] );
- builder.runs( parts[parts.length - 1], () -> {
+ builder.runs( parts[parts.length - 1], def, () -> {
// Run it
lock.lockInterruptibly();
try
diff --git a/src/test/resources/test-rom/mcfly.lua b/src/test/resources/test-rom/mcfly.lua
index a5caec7a8..258a51236 100644
--- a/src/test/resources/test-rom/mcfly.lua
+++ b/src/test/resources/test-rom/mcfly.lua
@@ -424,6 +424,8 @@ local tests_locked = false
local test_list = {}
local test_map, test_count = {}, 0
+local function format_loc(info) return ("%s:%d"):format(info.short_src, info.currentline) end
+
--- Add a new test to our queue.
--
-- @param test The descriptor of this test
@@ -432,7 +434,7 @@ local function do_test(test)
if not test.name then test.name = table.concat(test_stack, "\0", 1, test_stack.n) end
test_count = test_count + 1
test_list[test_count] = test
- test_map[test.name] = test_count
+ test_map[test.name] = { idx = test_count, definition = test.definition }
end
--- Get the "friendly" name of this test.
@@ -456,7 +458,7 @@ local function describe(name, body)
local ok, err = try(body)
-- We count errors as a (failing) test.
- if not ok then do_test { error = err } end
+ if not ok then do_test { error = err, definition = format_loc(debug.getinfo(2, "Sl")) } end
test_stack.n = n - 1
end
@@ -475,7 +477,7 @@ local function it(name, body)
local n = test_stack.n + 1
test_stack[n], test_stack.n, tests_locked = name, n, true
- do_test { action = body }
+ do_test { action = body, definition = format_loc(debug.getinfo(2, "Sl")) }
-- Pop the test from the stack
test_stack.n, tests_locked = n - 1, false
@@ -488,12 +490,11 @@ local function pending(name)
check('it', 1, 'string', name)
if tests_locked then error("Cannot create test while running tests", 2) end
- local _, loc = pcall(error, "", 3)
- loc = loc:gsub(":%s*$", "")
+ local trace = format_loc(debug.getinfo(2, "Sl"))
local n = test_stack.n + 1
test_stack[n], test_stack.n = name, n
- do_test { pending = true, trace = loc }
+ do_test { pending = true, trace = trace, definition = trace }
test_stack.n = n - 1
end
@@ -667,7 +668,7 @@ if cct_test then
while true do
local _, name = os.pullEvent("cct_test_run")
if not name then break end
- do_run(test_list[test_map[name]])
+ do_run(test_list[test_map[name].idx])
end
else
for _, test in pairs(test_list) do do_run(test) end