Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix test script resolution to work with NET 8 artifacts output #1690

Merged
merged 1 commit into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions Jint.Tests.PublicInterface/ShadowRealmTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void MultipleShadowRealmsDoNotInterfere()
engine.SetValue("message", "world");
engine.Evaluate("function hello() {return message}");

Assert.Equal("world",engine.Evaluate("hello();"));
Assert.Equal("world", engine.Evaluate("hello();"));

var shadowRealm = engine.Realm.Intrinsics.ShadowRealm.Construct();
shadowRealm.SetValue("message", "realm 1");
Expand Down Expand Up @@ -97,16 +97,32 @@ private static string GetBasePath()
var assemblyDirectory = new DirectoryInfo(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory);

var current = assemblyDirectory;
while (current is not null && current.GetDirectories().All(x => x.Name != "Jint.Tests"))
var binDirectory = $"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}";
while (current is not null)
{
current = current.Parent;
if (current.FullName.Contains(binDirectory) || current.Name == "bin")
{
current = current.Parent;
continue;
}

var testDirectory = current.GetDirectories("Jint.Tests").FirstOrDefault();
if (testDirectory == null)
{
current = current.Parent;
continue;
}

// found it
current = testDirectory;
break;
}

if (current is null)
{
throw new NullReferenceException($"Could not find tests base path, assemblyPath: {assemblyDirectory}");
}

return Path.Combine(current.FullName, "Jint.Tests", "Runtime", "Scripts");
return Path.Combine(current.FullName, "Runtime", "Scripts");
}
}
103 changes: 67 additions & 36 deletions Jint.Tests/Runtime/ModuleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public ModuleTests()
[Fact]
public void ShouldExportNamed()
{
_engine.AddModule("my-module", @"export const value = 'exported value';");
_engine.AddModule("my-module", "export const value = 'exported value';");
var ns = _engine.ImportModule("my-module");

Assert.Equal("exported value", ns.Get("value").AsString());
Expand All @@ -24,7 +24,7 @@ public void ShouldExportNamed()
[Fact]
public void ShouldExportNamedListRenamed()
{
_engine.AddModule("my-module", @"const value1 = 1; const value2 = 2; export { value1 as renamed1, value2 as renamed2 }");
_engine.AddModule("my-module", "const value1 = 1; const value2 = 2; export { value1 as renamed1, value2 as renamed2 }");
var ns = _engine.ImportModule("my-module");

Assert.Equal(1, ns.Get("renamed1").AsInteger());
Expand All @@ -34,7 +34,7 @@ public void ShouldExportNamedListRenamed()
[Fact]
public void ShouldExportDefault()
{
_engine.AddModule("my-module", @"export default 'exported value';");
_engine.AddModule("my-module", "export default 'exported value';");
var ns = _engine.ImportModule("my-module");

Assert.Equal("exported value", ns.Get("default").AsString());
Expand All @@ -43,8 +43,8 @@ public void ShouldExportDefault()
[Fact]
public void ShouldExportAll()
{
_engine.AddModule("module1", @"export const value = 'exported value';");
_engine.AddModule("module2", @"export * from 'module1';");
_engine.AddModule("module1", "export const value = 'exported value';");
_engine.AddModule("module2", "export * from 'module1';");
var ns = _engine.ImportModule("module2");

Assert.Equal("exported value", ns.Get("value").AsString());
Expand All @@ -53,8 +53,8 @@ public void ShouldExportAll()
[Fact]
public void ShouldImportNamed()
{
_engine.AddModule("imported-module", @"export const value = 'exported value';");
_engine.AddModule("my-module", @"import { value } from 'imported-module'; export const exported = value;");
_engine.AddModule("imported-module", "export const value = 'exported value';");
_engine.AddModule("my-module", "import { value } from 'imported-module'; export const exported = value;");
var ns = _engine.ImportModule("my-module");

Assert.Equal("exported value", ns.Get("exported").AsString());
Expand All @@ -63,8 +63,8 @@ public void ShouldImportNamed()
[Fact]
public void ShouldImportRenamed()
{
_engine.AddModule("imported-module", @"export const value = 'exported value';");
_engine.AddModule("my-module", @"import { value as renamed } from 'imported-module'; export const exported = renamed;");
_engine.AddModule("imported-module", "export const value = 'exported value';");
_engine.AddModule("my-module", "import { value as renamed } from 'imported-module'; export const exported = renamed;");
var ns = _engine.ImportModule("my-module");

Assert.Equal("exported value", ns.Get("exported").AsString());
Expand All @@ -73,8 +73,8 @@ public void ShouldImportRenamed()
[Fact]
public void ShouldImportDefault()
{
_engine.AddModule("imported-module", @"export default 'exported value';");
_engine.AddModule("my-module", @"import imported from 'imported-module'; export const exported = imported;");
_engine.AddModule("imported-module", "export default 'exported value';");
_engine.AddModule("my-module", "import imported from 'imported-module'; export const exported = imported;");
var ns = _engine.ImportModule("my-module");

Assert.Equal("exported value", ns.Get("exported").AsString());
Expand All @@ -83,8 +83,8 @@ public void ShouldImportDefault()
[Fact]
public void ShouldImportAll()
{
_engine.AddModule("imported-module", @"export const value = 'exported value';");
_engine.AddModule("my-module", @"import * as imported from 'imported-module'; export const exported = imported.value;");
_engine.AddModule("imported-module", "export const value = 'exported value';");
_engine.AddModule("my-module", "import * as imported from 'imported-module'; export const exported = imported.value;");
var ns = _engine.ImportModule("my-module");

Assert.Equal("exported value", ns.Get("exported").AsString());
Expand All @@ -95,7 +95,7 @@ public void ShouldImportDynamically()
{
var received = false;
_engine.AddModule("imported-module", builder => builder.ExportFunction("signal", () => received = true));
_engine.AddModule("my-module", @"import('imported-module').then(ns => { ns.signal(); });");
_engine.AddModule("my-module", "import('imported-module').then(ns => { ns.signal(); });");

_engine.ImportModule("my-module");

Expand All @@ -105,8 +105,8 @@ public void ShouldImportDynamically()
[Fact]
public void ShouldPropagateParseError()
{
_engine.AddModule("imported", @"export const invalid;");
_engine.AddModule("my-module", @"import { invalid } from 'imported';");
_engine.AddModule("imported", "export const invalid;");
_engine.AddModule("my-module", "import { invalid } from 'imported';");

var exc = Assert.Throws<JavaScriptException>(() => _engine.ImportModule("my-module"));
Assert.Equal("Error while loading module: error in module 'imported': Line 1: Missing initializer in const declaration", exc.Message);
Expand All @@ -116,8 +116,8 @@ public void ShouldPropagateParseError()
[Fact]
public void ShouldPropagateLinkError()
{
_engine.AddModule("imported", @"export invalid;");
_engine.AddModule("my-module", @"import { value } from 'imported';");
_engine.AddModule("imported", "export invalid;");
_engine.AddModule("my-module", "import { value } from 'imported';");

var exc = Assert.Throws<JavaScriptException>(() => _engine.ImportModule("my-module"));
Assert.Equal("Error while loading module: error in module 'imported': Line 1: Unexpected identifier", exc.Message);
Expand All @@ -127,7 +127,7 @@ public void ShouldPropagateLinkError()
[Fact]
public void ShouldPropagateExecuteError()
{
_engine.AddModule("my-module", @"throw new Error('imported successfully');");
_engine.AddModule("my-module", "throw new Error('imported successfully');");

var exc = Assert.Throws<JavaScriptException>(() => _engine.ImportModule("my-module"));
Assert.Equal("imported successfully", exc.Message);
Expand All @@ -137,8 +137,8 @@ public void ShouldPropagateExecuteError()
[Fact]
public void ShouldPropagateThrowStatementThroughJavaScriptImport()
{
_engine.AddModule("imported-module", @"throw new Error('imported successfully');");
_engine.AddModule("my-module", @"import 'imported-module';");
_engine.AddModule("imported-module", "throw new Error('imported successfully');");
_engine.AddModule("my-module", "import 'imported-module';");

var exc = Assert.Throws<JavaScriptException>(() => _engine.ImportModule("my-module"));
Assert.Equal("imported successfully", exc.Message);
Expand All @@ -156,8 +156,11 @@ public void ShouldAddModuleFromJsValue()
[Fact]
public void ShouldAddModuleFromClrInstance()
{
_engine.AddModule("imported-module", builder => builder.ExportObject("value", new ImportedClass { Value = "instance value" }));
_engine.AddModule("my-module", @"import { value } from 'imported-module'; export const exported = value.value;");
_engine.AddModule("imported-module", builder => builder.ExportObject("value", new ImportedClass
{
Value = "instance value"
}));
_engine.AddModule("my-module", "import { value } from 'imported-module'; export const exported = value.value;");
var ns = _engine.ImportModule("my-module");

Assert.Equal("instance value", ns.Get("exported").AsString());
Expand All @@ -178,7 +181,7 @@ public void ShouldAllowInvokeUserDefinedClass()
public void ShouldAddModuleFromClrType()
{
_engine.AddModule("imported-module", builder => builder.ExportType<ImportedClass>());
_engine.AddModule("my-module", @"import { ImportedClass } from 'imported-module'; export const exported = new ImportedClass().value;");
_engine.AddModule("my-module", "import { ImportedClass } from 'imported-module'; export const exported = new ImportedClass().value;");
var ns = _engine.ImportModule("my-module");

Assert.Equal("hello world", ns.Get("exported").AsString());
Expand Down Expand Up @@ -207,8 +210,20 @@ public void ShouldAddModuleFromClrFunction()
export const result = [fns.act_noargs(), fns.act_args('ok'), fns.fn_noargs(), fns.fn_args('ok')];");
var ns = _engine.ImportModule("my-module");

Assert.Equal(new[] { "act_noargs", "act_args:ok", "fn_noargs", "fn_args:ok" }, received.ToArray());
Assert.Equal(new[] { "undefined", "undefined", "ret", "ret" }, ns.Get("result").AsArray().Select(x => x.ToString()).ToArray());
Assert.Equal(new[]
{
"act_noargs",
"act_args:ok",
"fn_noargs",
"fn_args:ok"
}, received.ToArray());
Assert.Equal(new[]
{
"undefined",
"undefined",
"ret",
"ret"
}, ns.Get("result").AsArray().Select(x => x.ToString()).ToArray());
}

private class ImportedClass
Expand All @@ -222,7 +237,7 @@ public void ShouldAllowExportMultipleImports()
_engine.AddModule("@mine/import1", builder => builder.ExportValue("value1", JsNumber.Create(1)));
_engine.AddModule("@mine/import2", builder => builder.ExportValue("value2", JsNumber.Create(2)));
_engine.AddModule("@mine", "export * from '@mine/import1'; export * from '@mine/import2'");
_engine.AddModule("app", @"import { value1, value2 } from '@mine'; export const result = `${value1} ${value2}`");
_engine.AddModule("app", "import { value1, value2 } from '@mine'; export const result = `${value1} ${value2}`");
var ns = _engine.ImportModule("app");

Assert.Equal("1 2", ns.Get("result").AsString());
Expand Down Expand Up @@ -258,7 +273,7 @@ public void ShouldImportOnlyOnce()
{
var called = 0;
_engine.AddModule("imported-module", builder => builder.ExportFunction("count", args => called++));
_engine.AddModule("my-module", @"import { count } from 'imported-module'; count();");
_engine.AddModule("my-module", "import { count } from 'imported-module'; count();");
_engine.ImportModule("my-module");
_engine.ImportModule("my-module");

Expand All @@ -268,14 +283,14 @@ public void ShouldImportOnlyOnce()
[Fact]
public void ShouldAllowSelfImport()
{
_engine.AddModule("my-globals", @"export const globals = { counter: 0 };");
_engine.AddModule("my-globals", "export const globals = { counter: 0 };");
_engine.AddModule("my-module", @"
import { globals } from 'my-globals';
import {} from 'my-module';
globals.counter++;
export const count = globals.counter;
");
var ns= _engine.ImportModule("my-module");
var ns = _engine.ImportModule("my-module");

Assert.Equal(1, ns.Get("count").AsInteger());
}
Expand All @@ -285,8 +300,8 @@ public void ShouldAllowCyclicImport()
{
// https://tc39.es/ecma262/#sec-example-cyclic-module-record-graphs

_engine.AddModule("B", @"import { a } from 'A'; export const b = 'b';");
_engine.AddModule("A", @"import { b } from 'B'; export const a = 'a';");
_engine.AddModule("B", "import { a } from 'A'; export const b = 'b';");
_engine.AddModule("A", "import { b } from 'B'; export const a = 'a';");

var nsA = _engine.ImportModule("A");
var nsB = _engine.ImportModule("B");
Expand All @@ -301,7 +316,7 @@ public void ShouldSupportConstraints()
var engine = new Engine(opts => opts.TimeoutInterval(TimeSpan.FromTicks(1)));

engine.AddModule("sleep", builder => builder.ExportFunction("sleep", () => Thread.Sleep(100)));
engine.AddModule("my-module", @"import { sleep } from 'sleep'; for(var i = 0; i < 100; i++) { sleep(); } export const result = 'ok';");
engine.AddModule("my-module", "import { sleep } from 'sleep'; for(var i = 0; i < 100; i++) { sleep(); } export const result = 'ok';");
Assert.Throws<TimeoutException>(() => engine.ImportModule("my-module"));
}

Expand Down Expand Up @@ -350,14 +365,30 @@ public void CanReuseModule()
}
}

internal static string GetBasePath()
private static string GetBasePath()
{
var assemblyDirectory = new DirectoryInfo(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory);

var current = assemblyDirectory;
while (current is not null && current.Name != "Jint.Tests")
var binDirectory = $"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}";
while (current is not null)
{
current = current.Parent;
if (current.FullName.Contains(binDirectory) || current.Name == "bin")
{
current = current.Parent;
continue;
}

var testDirectory = current.GetDirectories("Jint.Tests").FirstOrDefault();
if (testDirectory == null)
{
current = current.Parent;
continue;
}

// found it
current = testDirectory;
break;
}

if (current is null)
Expand Down
4 changes: 2 additions & 2 deletions Jint/Runtime/ExceptionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,9 @@ public static void ThrowExecutionCanceledException()
}

[DoesNotReturn]
public static void ThrowModuleResolutionException(string resolverAlgorithmError, string specifier, string? parent)
public static void ThrowModuleResolutionException(string message, string specifier, string? parent, string? filePath = null)
{
throw new ModuleResolutionException(resolverAlgorithmError, specifier, parent);
throw new ModuleResolutionException(message, specifier, parent, filePath);
}
}
}
12 changes: 4 additions & 8 deletions Jint/Runtime/Modules/DefaultModuleLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ public sealed class DefaultModuleLoader : IModuleLoader
private readonly Uri _basePath;
private readonly bool _restrictToBasePath;

public DefaultModuleLoader(string basePath) : this(basePath, true)
{

}

public DefaultModuleLoader(string basePath, bool restrictToBasePath)
public DefaultModuleLoader(string basePath, bool restrictToBasePath = true)
{
if (string.IsNullOrWhiteSpace(basePath))
{
Expand Down Expand Up @@ -74,7 +69,7 @@ public ResolvedSpecifier Resolve(string? referencingModuleLocation, string speci
return new ResolvedSpecifier(
specifier,
specifier,
null,
Uri: null,
SpecifierType.Bare
);
}
Expand Down Expand Up @@ -120,10 +115,11 @@ public Module LoadModule(Engine engine, ResolvedSpecifier resolved)
{
ExceptionHelper.ThrowInvalidOperationException($"Module '{resolved.Specifier}' of type '{resolved.Type}' has no resolved URI.");
}

var fileName = Uri.UnescapeDataString(resolved.Uri.AbsolutePath);
if (!File.Exists(fileName))
{
ExceptionHelper.ThrowArgumentException("Module Not Found: ", resolved.Specifier);
ExceptionHelper.ThrowModuleResolutionException("Module Not Found", resolved.Specifier, parent: null, fileName);
return default;
}

Expand Down
16 changes: 9 additions & 7 deletions Jint/Runtime/Modules/ModuleResolutionException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ namespace Jint.Runtime.Modules;

public sealed class ModuleResolutionException : JintException
{
public string ResolverAlgorithmError { get; }
public string Specifier { get; }

public ModuleResolutionException(string message, string specifier, string? parent)
: base($"{message} in module '{parent ?? "(null)"}': '{specifier}'")
public ModuleResolutionException(string resolverAlgorithmError, string specifier, string? parent, string? filePath)
: base($"{resolverAlgorithmError} in module '{parent ?? "(null)"}': '{specifier}'")
{
ResolverAlgorithmError = message;
ResolverAlgorithmError = resolverAlgorithmError;
Specifier = specifier;
FilePath = filePath;
}
}

public string ResolverAlgorithmError { get; }
public string Specifier { get; }
public string? FilePath { get; }
}