using System; using System.Collections.Generic; using System.Linq; using Amazon.JSII.Runtime.Deputy; using Amazon.JSII.Tests.CalculatorNamespace; using Amazon.JSII.Tests.CalculatorNamespace.Cdk16625; using Amazon.JSII.Tests.CalculatorNamespace.Cdk22369; using CompositeOperation = Amazon.JSII.Tests.CalculatorNamespace.Composition.CompositeOperation; using Amazon.JSII.Tests.CalculatorNamespace.LibNamespace; using Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace; using Amazon.JSII.Tests.CalculatorNamespace.LibNamespace.DeprecationRemoval; using Newtonsoft.Json.Linq; using Xunit; using Xunit.Abstractions; [assembly: CollectionBehavior(DisableTestParallelization = true)] #pragma warning disable CS0612 namespace Amazon.JSII.Runtime.IntegrationTests { /// /// Ported from packages/jsii-java-runtime/src/test/java/org/jsii/testing/ComplianceTest.java. /// public sealed class ComplianceTests : IClassFixture, IDisposable { class RuntimeException : Exception { public RuntimeException(string message) : base(message) { } } const string Prefix = nameof(IntegrationTests) + ".Compliance."; private readonly IDisposable _serviceContainerFixture; public ComplianceTests(ITestOutputHelper outputHelper, ServiceContainerFixture serviceContainerFixture) { serviceContainerFixture.SetOverride(outputHelper); _serviceContainerFixture = serviceContainerFixture; } void IDisposable.Dispose() { _serviceContainerFixture.Dispose(); } [Fact(DisplayName = Prefix + nameof(UseNestedStruct))] public void UseNestedStruct() { StaticConsumer.Consume( new Amazon.JSII.Tests.CustomSubmoduleName.NestingClass.NestedStruct { Name = "Bond, James Bond" } ); } [Fact(DisplayName = Prefix + nameof(PrimitiveTypes))] public void PrimitiveTypes() { AllTypes types = new AllTypes(); // boolean types.BooleanProperty = true; Assert.True(types.BooleanProperty); // string types.StringProperty = "foo"; Assert.Equal("foo", types.StringProperty); // number types.NumberProperty = 1234; Assert.Equal(1234d, types.NumberProperty); // date types.DateProperty = DateTime.UnixEpoch.AddMilliseconds(123); Assert.Equal(DateTime.UnixEpoch.AddMilliseconds(123), types.DateProperty); // json types.JsonProperty = JObject.Parse(@"{ ""Foo"": { ""Bar"": 123 } }"); Assert.Equal(123d, types.JsonProperty["Foo"]?["Bar"]?.Value()); } [Fact(DisplayName = Prefix + nameof(Dates))] public void Dates() { var types = new AllTypes(); // strong type types.DateProperty = DateTime.UnixEpoch.AddMilliseconds(123); Assert.Equal(DateTime.UnixEpoch.AddMilliseconds(123), types.DateProperty); // weak type types.AnyProperty = DateTime.UnixEpoch.AddSeconds(999); Assert.Equal(DateTime.UnixEpoch.AddSeconds(999), types.AnyProperty); } [Fact(DisplayName = Prefix + nameof(CollectionTypes))] public void CollectionTypes() { AllTypes types = new AllTypes(); // array types.ArrayProperty = new[] {"Hello", "World"}; Assert.Equal("World", types.ArrayProperty[1]); // map IDictionary map = new Dictionary(); map["Foo"] = new Number(123); types.MapProperty = map; Assert.Equal(123d, types.MapProperty["Foo"].Value); } [Fact(DisplayName = Prefix + nameof(ComplexCollectionTypes))] public void ComplexCollectionTypes() { // See https://github.com/aws/aws-cdk/issues/2496 AllTypes types = new AllTypes(); // complex map IDictionary map = new Dictionary(); map.Add("Foo", new Dictionary() { {"Key", 123d}}); types.AnyMapProperty = map; var dict = (Dictionary)types.AnyMapProperty["Foo"]; Assert.Equal(123d, dict["Key"]); } [Fact(DisplayName = Prefix + nameof(DynamicTypes))] public void DynamicTypes() { AllTypes types = new AllTypes(); // boolean types.AnyProperty = false; Assert.False((bool) types.AnyProperty); // string types.AnyProperty = "String"; Assert.Equal("String", types.AnyProperty); // number types.AnyProperty = 12; Assert.Equal(12d, types.AnyProperty); // date types.AnyProperty = DateTime.UnixEpoch.AddSeconds(1234); Assert.Equal(DateTime.UnixEpoch.AddSeconds(1234), types.AnyProperty); // json (notice that when deserialized, it is deserialized as a map). types.AnyProperty = new Dictionary { { "Goo", new object[] { "Hello", new Dictionary { { "World", 123 } } } } }; var @object = (IDictionary) types.AnyProperty; var array = (object[]) @object["Goo"]; var innerObject = (IDictionary) array[1]; Assert.Equal(123d, innerObject["World"]); // array types.AnyProperty = new[] {"Hello", "World"}; Assert.Equal("Hello", ((object[]) types.AnyProperty)[0]); Assert.Equal("World", ((object[]) types.AnyProperty)[1]); // array of any types.AnyArrayProperty = new object[] {"Hybrid", new Number(12), 123, false}; Assert.Equal(123d, types.AnyArrayProperty[2]); // map IDictionary map = new Dictionary(); map["MapKey"] = "MapValue"; types.AnyProperty = map; Assert.Equal("MapValue", ((IDictionary) types.AnyProperty)["MapKey"]); // map of any map["Goo"] = 19289812; types.AnyMapProperty = map; Assert.Equal(19289812d, types.AnyMapProperty["Goo"]); // classes var mult = new Multiply(new Number(10), new Number(20)); types.AnyProperty = mult; Assert.Same(types.AnyProperty, mult); Assert.IsType(types.AnyProperty); Assert.Equal(200d, ((Multiply) types.AnyProperty).Value); } [Fact(DisplayName = Prefix + nameof(UnionTypes))] public void UnionTypes() { AllTypes types = new AllTypes(); // single valued property types.UnionProperty = 1234; Assert.Equal(1234d, types.UnionProperty); types.UnionProperty = "Hello"; Assert.Equal("Hello", types.UnionProperty); types.UnionProperty = new Multiply(new Number(2), new Number(12)); Assert.Equal(24d, ((Multiply) types.UnionProperty).Value); // NOTE: union collections are untyped in C# (System.Object) // map // TODO: This is ported from the Java test, but it doesn't seem right. // UnionMapProperty has type Map // PrimitiveType.Number is not the same as the Number type, so I would expect // this to fail (which it does). /* IDictionary map = new Dictionary(); map["Foo"] = new Multiply(new Number(2), new Number(00)); types.UnionMapProperty = map; // array types.UnionArrayProperty = new object[] { "Hello", 123, new Number(3) }; Assert.Equal((double)33, ((Number)types.UnionArrayProperty[2]).Value); */ } [Fact(DisplayName = Prefix + nameof(CreateObjectAndCtorOverloads))] public void CreateObjectAndCtorOverloads() { new Calculator(); var calc = new Calculator(new CalculatorProps() { InitialValue = 100 }); Assert.Equal(100, calc.Value); } [Fact(DisplayName = Prefix + nameof(GetSetPrimitiveProperties))] public void GetSetPrimitiveProperties() { var number = new Number(20); Assert.Equal(20d, number.Value); Assert.Equal(40d, number.DoubleValue); Assert.Equal(-30d, new Negate(new Add(new Number(20), new Number(10))).Value); Assert.Equal(20d, new Multiply(new Add(new Number(5), new Number(5)), new Number(2)).Value); Assert.Equal(3d * 3d * 3d * 3d, new Power(new Number(3), new Number(4)).Value); Assert.Equal(999d, new Power(new Number(999), new Number(1)).Value); Assert.Equal(1d, new Power(new Number(999), new Number(0)).Value); } [Fact(DisplayName = Prefix + nameof(CallMethods))] public void CallMethods() { var calc = new Calculator(); calc.Add(10); Assert.Equal(10d, calc.Value); calc.Mul(2); Assert.Equal(20d, calc.Value); calc.Pow(5); Assert.Equal(20d * 20d * 20d * 20d * 20d, calc.Value); calc.Neg(); Assert.Equal(-3200000d, calc.Value); } [Fact(DisplayName = Prefix + nameof(UnmarkshallIntoAbstractType))] public void UnmarkshallIntoAbstractType() { var calc = new Calculator(); calc.Add(120); var value = calc.Curr; Assert.Equal(120d, value.Value); } [Fact(DisplayName = Prefix + nameof(GetAndSetNotPrimitiveProperties))] public void GetAndSetNotPrimitiveProperties() { var calc = new Calculator(); calc.Add(3200000); calc.Neg(); calc.Curr = new Multiply(new Number(2), calc.Curr); Assert.Equal(-6400000d, calc.Value); } [Fact(DisplayName = Prefix + nameof(GetAndSetEnumValues))] public void GetAndSetEnumValues() { // TODO: Generator should create a parameterless constructor. Calculator calc = new Calculator(new CalculatorProps()); calc.Add(9); calc.Pow(3); Assert.Equal(CompositeOperation.CompositionStringStyle.NORMAL, calc.StringStyle); calc.StringStyle = CompositeOperation.CompositionStringStyle.DECORATED; Assert.Equal(CompositeOperation.CompositionStringStyle.DECORATED, calc.StringStyle); Assert.Equal("<<[[{{(((1 * (0 + 9)) * (0 + 9)) * (0 + 9))}}]]>>", calc.ToString()); } [Fact(DisplayName = Prefix + nameof(EnumFromScopedModule))] public void UseEnumFromScopedModule() { ReferenceEnumFromScopedPackage obj = new ReferenceEnumFromScopedPackage(); Assert.Equal(EnumFromScopedModule.VALUE2, obj.Foo); obj.Foo = EnumFromScopedModule.VALUE1; Assert.Equal(EnumFromScopedModule.VALUE1, obj.LoadFoo()); obj.SaveFoo(EnumFromScopedModule.VALUE2); Assert.Equal(EnumFromScopedModule.VALUE2, obj.Foo); } [Fact(DisplayName = Prefix + nameof(UndefinedAndNull))] public void UndefinedAndNull() { // TODO: Generator should create a parameterless constructor. Calculator calculator = new Calculator(new CalculatorProps()); Assert.Null(calculator.MaxValue); calculator.MaxValue = null; } [Fact(DisplayName = Prefix + nameof(Arrays))] public void Arrays() { var sum = new Sum { Parts = new NumericValue[] {new Number(5), new Number(10), new Multiply(new Number(2), new Number(3))} }; Assert.Equal(10d + 5d + 2d * 3d, sum.Value); Assert.Equal(5d, sum.Parts[0].Value); Assert.Equal(6d, sum.Parts[2].Value); Assert.Equal("(((0 + 5) + 10) + (2 * 3))", sum.ToString()); } [Fact(DisplayName = Prefix + nameof(Maps))] public void Maps() { // TODO: Generator should create a parameterless constructor. var calc = new Calculator(new CalculatorProps()); calc.Add(10); calc.Add(20); calc.Mul(2); Assert.Collection( calc.OperationsMap["add"], val => { }, val => Assert.Equal(30d, val.Value) ); Assert.Collection( calc.OperationsMap["mul"], val => { } ); } [Fact(DisplayName = Prefix + nameof(FluentApi))] public void FluentApi() { Calculator calc = new Calculator(new CalculatorProps { InitialValue = 20, MaximumValue = 30, }); calc.Add(3); Assert.Equal(23, calc.Value); } [Fact(DisplayName = Prefix + nameof(Exceptions))] public void Exceptions() { var calc = new Calculator(new CalculatorProps { InitialValue = 20, MaximumValue = 30, }); calc.Add(3); Assert.Equal(23d, calc.Value); Assert.Throws(() => calc.Add(10)); calc.MaxValue = 40; calc.Add(10); Assert.Equal(33d, calc.Value); } [Fact(DisplayName = Prefix + nameof(ExceptionMessage))] public void ExceptionMessage() { var e = Assert.Throws(() => new AcceptsPath(new AcceptsPathProps { SourcePath = "A Bad Path" }) ); Assert.Equal("Cannot find asset", e.Message); } [Fact(DisplayName = Prefix + nameof(UnionProperties))] public void UnionProperties() { var calc = new Calculator(); calc.UnionProperty = new Multiply(new Number(9), new Number(3)); Assert.IsType(calc.UnionProperty); Assert.Equal(9d * 3, calc.ReadUnionValue()); calc.UnionProperty = new Power(new Number(10), new Number(3)); Assert.IsType(calc.UnionProperty); } [Fact(DisplayName = Prefix + nameof(SubClassing))] public void SubClassing() { var calc = new Calculator(); calc.Curr = new AddTen(33); calc.Neg(); Assert.Equal(-43d, calc.Value); } [Fact(DisplayName = Prefix + nameof(TestJsObjectLiteralToNative))] public void TestJsObjectLiteralToNative() { var obj = new JSObjectLiteralToNative(); var obj2 = obj.ReturnLiteral(); Assert.Equal("Hello", obj2.PropA); Assert.Equal(102d, obj2.PropB); } [Fact(DisplayName = Prefix + nameof(CreationOfNativeObjectsFromJavaScriptObjects))] public void CreationOfNativeObjectsFromJavaScriptObjects() { var types = new AllTypes(); var jsObj = new Number(44); types.AnyProperty = jsObj; var unmarshalledJSObj = types.AnyProperty; Assert.IsType(unmarshalledJSObj); var nativeObj = new AddTen(10); types.AnyProperty = nativeObj; var result1 = types.AnyProperty; Assert.Same(nativeObj, result1); var nativeObj2 = new MulTen(20); types.AnyProperty = nativeObj2; var unmarshalledNativeObj = types.AnyProperty; Assert.IsType(unmarshalledNativeObj); Assert.Same(nativeObj2, unmarshalledNativeObj); } class AsyncVirtualMethodsChild : AsyncVirtualMethods { public override double OverrideMe(double mult) { throw new RuntimeException("Thrown by native code"); } } [Fact(DisplayName = Prefix + nameof(AsyncOverrides_CallAsyncMethod))] public void AsyncOverrides_CallAsyncMethod() { AsyncVirtualMethods obj = new AsyncVirtualMethods(); Assert.Equal(128d, obj.CallMe()); Assert.Equal(528d, obj.OverrideMe(44)); } [Fact(DisplayName = Prefix + nameof(AsyncOverrides_OverrideAsyncMethod))] public void AsyncOverrides_OverrideAsyncMethod() { OverrideAsyncMethods obj = new OverrideAsyncMethods(); Assert.Equal(4452d, obj.CallMe()); } [Fact(DisplayName = Prefix + nameof(AsyncOverrides_OverrideAsyncMethodByParentClass))] public void AsyncOverrides_OverrideAsyncMethodByParentClass() { OverrideAsyncMethodsByBaseClass obj = new OverrideAsyncMethodsByBaseClass(); Assert.Equal(4452d, obj.CallMe()); } [Fact(DisplayName = Prefix + nameof(AsyncOverrides_OverrideCallsSuper))] public void AsyncOverrides_OverrideCallsSuper() { OverrideCallsSuper obj = new OverrideCallsSuper(); Assert.Equal(1441d, obj.OverrideMe(12)); Assert.Equal(1209d, obj.CallMe()); } [Fact(DisplayName = Prefix + nameof(AsyncOverrides_TwoOverrides))] public void AsyncOverrides_TwoOverrides() { TwoOverrides obj = new TwoOverrides(); Assert.Equal(684d, obj.CallMe()); } [Fact(DisplayName = Prefix + nameof(AsyncOverrides_OverrideThrows))] public void AsyncOverrides_OverrideThrows() { AsyncVirtualMethodsChild obj = new AsyncVirtualMethodsChild(); var exception = Assert.Throws(() => obj.CallMe()); Assert.Contains("Thrown by native code", exception.Message); } class SyncVirtualMethodsChild_Set_CallsSuper : SyncVirtualMethods { public override string TheProperty { get => base.TheProperty; set => base.TheProperty = $"{value}:by override"; } } class SyncVirtualMethodsChild_Get_CallsSuper : SyncVirtualMethods { public override string TheProperty { get => $"super:{base.TheProperty}"; set => base.TheProperty = value; } } class SyncVirtualMethodsChild_Throws : SyncVirtualMethods { public override string TheProperty { get => throw new RuntimeException("Oh no, this is bad"); set => throw new RuntimeException("Exception from overloaded setter"); } } class InterfaceWithProperties : DeputyBase, IInterfaceWithProperties { string? _x; public string ReadOnlyString => "READ_ONLY_STRING"; public string ReadWriteString { get => $"{_x}?"; set => _x = $"{value}!"; } } [Fact(DisplayName = Prefix + nameof(PropertyOverrides_Get_Set))] public void PropertyOverrides_Get_Set() { SyncOverrides so = new SyncOverrides(); Assert.Equal("I am an override!", so.RetrieveValueOfTheProperty()); so.ModifyValueOfTheProperty("New Value"); Assert.Equal("New Value", so.AnotherTheProperty); } [Fact(DisplayName = Prefix + nameof(PropertyOverrides_Get_CallsSuper))] public void PropertyOverrides_Get_CallsSuper() { SyncVirtualMethodsChild_Get_CallsSuper so = new SyncVirtualMethodsChild_Get_CallsSuper(); Assert.Equal("super:initial value", so.RetrieveValueOfTheProperty()); Assert.Equal("super:initial value", so.TheProperty); } [Fact(DisplayName = Prefix + nameof(PropertyOverrides_Get_Throws))] public void PropertyOverrides_Get_Throws() { SyncVirtualMethodsChild_Throws so = new SyncVirtualMethodsChild_Throws(); var exception = Assert.Throws(() => so.RetrieveValueOfTheProperty()); Assert.Contains("Oh no, this is bad", exception.Message); } [Fact(DisplayName = Prefix + nameof(PropertyOverrides_Set_CallsSuper))] public void PropertyOverrides_Set_CallsSuper() { SyncVirtualMethodsChild_Set_CallsSuper so = new SyncVirtualMethodsChild_Set_CallsSuper(); so.ModifyValueOfTheProperty("New Value"); Assert.Equal("New Value:by override", so.TheProperty); } [Fact(DisplayName = Prefix + nameof(PropertyOverrides_Set_Throws))] public void PropertyOverrides_Set_Throws() { SyncVirtualMethodsChild_Throws so = new SyncVirtualMethodsChild_Throws(); var exception = Assert.Throws(() => so.ModifyValueOfTheProperty("Hii")); Assert.Contains("Exception from overloaded setter", exception.Message); } [Fact(DisplayName = Prefix + nameof(PropertyOverrides_Interfaces))] public void PropertyOverrides_Interfaces() { InterfaceWithProperties obj = new InterfaceWithProperties(); UsesInterfaceWithProperties interact = new UsesInterfaceWithProperties(obj); Assert.Equal("READ_ONLY_STRING", interact.JustRead()); Assert.Equal("Hello!?", interact.WriteAndRead("Hello")); } [Fact(DisplayName = Prefix + nameof(SyncOverrides_SyncOverrides))] public void SyncOverrides_SyncOverrides() { SyncOverrides obj = new SyncOverrides(); Assert.Equal(10d * 5, obj.CallerIsMethod()); // affect the result obj.Multiplier = 5; Assert.Equal(10d * 5 * 5, obj.CallerIsMethod()); // verify callbacks are invoked from a property Assert.Equal(10d * 5 * 5, obj.CallerIsProperty); // and from an async method obj.Multiplier = 3; Assert.Equal(10d * 5 * 3, obj.CallerIsAsync()); } [Fact(DisplayName = Prefix + nameof(SyncOverrides_CallsSuper))] public void SyncOverrides_CallsSuper() { SyncOverrides obj = new SyncOverrides(); Assert.Equal(10d * 5, obj.CallerIsProperty); obj.ReturnSuper = true; // js code returns n * 2 Assert.Equal(10d * 2, obj.CallerIsProperty); } [Fact(DisplayName = Prefix + nameof(SyncOverrides_CallsDoubleAsyncMethodFails))] public void SyncOverrides_CallsDoubleAsyncMethodFails() { SyncOverrides obj = new SyncOverrides(); obj.CallAsync = true; Assert.Throws(() => obj.CallerIsMethod()); } [Fact(DisplayName = Prefix + nameof(SyncOverrides_CallsDoubleAsyncPropertyGetterFails))] public void SyncOverrides_CallsDoubleAsyncPropertyGetterFails() { SyncOverrides obj = new SyncOverrides(); obj.CallAsync = true; Assert.Throws(() => obj.CallerIsProperty); } [Fact(DisplayName = Prefix + nameof(SyncOverrides_CallsDoubleAsyncPropertySetterFails))] public void SyncOverrides_CallsDoubleAsyncPropertySetterFails() { SyncOverrides obj = new SyncOverrides(); obj.CallAsync = true; Assert.Throws(() => obj.CallerIsProperty = 12); } [Fact(DisplayName = Prefix + nameof(TestInterfaces))] public void TestInterfaces() { IFriendly friendly; IFriendlier friendlier; IRandomNumberGenerator randomNumberGenerator; IFriendlyRandomGenerator friendlyRandomGenerator; Add add = new Add(new Number(10), new Number(20)); friendly = add; // friendlier = add // <-- shouldn't compile since Add implements IFriendly Assert.Equal("Hello, I am a binary operation. What's your name?", friendly.Hello()); Multiply multiply = new Multiply(new Number(10), new Number(30)); friendly = multiply; friendlier = multiply; randomNumberGenerator = multiply; // friendlyRandomGenerator = multiply; // <-- shouldn't compile Assert.Equal("Hello, I am a binary operation. What's your name?", friendly.Hello()); Assert.Equal("Goodbye from Multiply!", friendlier.Goodbye()); Assert.Equal(89d, randomNumberGenerator.Next()); friendlyRandomGenerator = new DoubleTrouble(); Assert.Equal("world", friendlyRandomGenerator.Hello()); Assert.Equal(12d, friendlyRandomGenerator.Next()); Polymorphism poly = new Polymorphism(); Assert.Equal("oh, Hello, I am a binary operation. What's your name?", poly.SayHello(friendly)); Assert.Equal("oh, world", poly.SayHello(friendlyRandomGenerator)); Assert.Equal("oh, SubclassNativeFriendlyRandom", poly.SayHello(new SubclassNativeFriendlyRandom())); Assert.Equal("oh, I am a native!", poly.SayHello(new PureNativeFriendlyRandom())); } /** * This test verifies that native objects passed to jsii code as interfaces will remain "stable" * across invocation. For native objects that derive from JsiiObject, that's natural, because the objref * is stored at the JsiiObject level. But for "pure" native objects, which are not part of the JsiiObject * hierarchy, there's some magic going on: when the pure object is first passed to jsii, an empty javascript * object is created for it (extends Object.prototype) and any native method overrides are assigned (like any * other jsii object). The resulting objref is stored at the engine level (in "objects"). * * We verify two directions: * 1. objref => obj: when .getGenerator() is called, we get back an objref and we assert that it is the *same* * as the one we originally passed. * 2. obj => objref: when we call .isSameGenerator(x) we pass the pure native object back to jsii and we expect * that a new object is not created again. */ [Fact(DisplayName = Prefix + nameof(TestNativeObjectsWithInterfaces))] public void TestNativeObjectsWithInterfaces() { // create a pure and native object, not part of the jsii hierarchy, only implements a jsii interface PureNativeFriendlyRandom pureNative = new PureNativeFriendlyRandom(); SubclassNativeFriendlyRandom subclassedNative = new SubclassNativeFriendlyRandom(); NumberGenerator generatorBoundToPSubclassedObject = new NumberGenerator(subclassedNative); Assert.Same(subclassedNative, generatorBoundToPSubclassedObject.Generator); generatorBoundToPSubclassedObject.IsSameGenerator(subclassedNative); Assert.Equal(10000d, generatorBoundToPSubclassedObject.NextTimes100()); // when we invoke nextTimes100 again, it will use the objref and call into the same object. Assert.Equal(20000d, generatorBoundToPSubclassedObject.NextTimes100()); NumberGenerator generatorBoundToPureNative = new NumberGenerator(pureNative); Assert.Same(pureNative, generatorBoundToPureNative.Generator); generatorBoundToPureNative.IsSameGenerator(pureNative); Assert.Equal(100000d, generatorBoundToPureNative.NextTimes100()); Assert.Equal(200000d, generatorBoundToPureNative.NextTimes100()); } [Fact(DisplayName = Prefix + nameof(TestLiteralInterface))] public void TestLiteralInterface() { JSObjectLiteralForInterface obj = new JSObjectLiteralForInterface(); IFriendly friendly = obj.GiveMeFriendly(); Assert.Equal("I am literally friendly!", friendly.Hello()); IFriendlyRandomGenerator gen = obj.GiveMeFriendlyGenerator(); Assert.Equal("giveMeFriendlyGenerator", gen.Hello()); Assert.Equal(42d, gen.Next()); } [Fact(DisplayName = Prefix + nameof(TestInterfaceParameter))] public void TestInterfaceParameter() { var obj = new JSObjectLiteralForInterface(); var friendly = obj.GiveMeFriendly(); Assert.Equal("I am literally friendly!", friendly.Hello()); var greetingAugmenter = new GreetingAugmenter(); var betterGreeting = greetingAugmenter.BetterGreeting(friendly); Assert.Equal("I am literally friendly! Let me buy you a drink!", betterGreeting); } [Fact(DisplayName = Prefix + nameof(Structs_SerializeToJsii))] public void Structs_SerializeToJsii() { MyFirstStruct firstStruct = new MyFirstStruct { Astring = "FirstString", Anumber = 999, FirstOptional = new[] {"First", "Optional"} }; DoubleTrouble doubleTrouble = new DoubleTrouble(); DerivedStruct derivedStruct = new DerivedStruct { NonPrimitive = doubleTrouble, Bool = false, AnotherRequired = DateTime.Now, Astring = "String", Anumber = 1234, FirstOptional = new[] {"one", "two"} }; GiveMeStructs gms = new GiveMeStructs(); Assert.Equal(999, gms.ReadFirstNumber(firstStruct)); Assert.Equal(1234, gms.ReadFirstNumber(derivedStruct)); // since derived inherits from first Assert.Same(doubleTrouble, gms.ReadDerivedNonPrimitive(derivedStruct)); IStructWithOnlyOptionals literal = gms.StructLiteral; Assert.Equal("optional1FromStructLiteral", literal.Optional1); Assert.False(literal.Optional3); Assert.Null(literal.Optional2); } [Fact(DisplayName = Prefix + nameof(StaticsTest))] public void StaticsTest() { Assert.Equal("hello ,Yoyo!", Statics.StaticMethod("Yoyo")); Assert.Equal("default", Statics.Instance.Value); Statics newStatics = new Statics("new value"); Statics.Instance = newStatics; Assert.Same(Statics.Instance, newStatics); Assert.Equal("new value", Statics.Instance.Value); Assert.Equal(100, Statics.NonConstStatic); } [Fact(DisplayName = Prefix + nameof(Consts))] public void Consts() { Assert.Equal("hello", Statics.Foo); DoubleTrouble obj = Statics.ConstObj; Assert.Equal("world", obj.Hello()); Assert.Equal(1234, Statics.BAR); Assert.Equal("world", Statics.ZooBar["hello"]); } [Fact(DisplayName = Prefix + nameof(ReservedKeywordsAreSlugifiedInMethodNames), Skip = "TODO")] public void ReservedKeywordsAreSlugifiedInMethodNames() { throw new NotImplementedException(); } [Fact(DisplayName = Prefix + nameof(NodeStandardLibrary))] public void NodeStandardLibrary() { NodeStandardLibrary obj = new NodeStandardLibrary(); Assert.Equal("Hello, resource!", obj.FsReadFile()); Assert.Equal("Hello, resource! SYNC!", obj.FsReadFileSync()); Assert.True(obj.OsPlatform.Length > 0); Assert.Equal("6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50", obj.CryptoSha256()); } [Fact(DisplayName = Prefix + nameof(ReturnAbstract))] public void ReturnAbstract() { var obj = new AbstractClassReturner(); var obj2 = obj.GiveMeAbstract(); Assert.Equal("Hello, John!!", obj2.AbstractMethod("John")); Assert.Equal("propFromInterfaceValue", obj2.PropFromInterface); Assert.Equal(42, obj2.NonAbstractMethod()); var iface = obj.GiveMeInterface(); Assert.Equal("propFromInterfaceValue", iface.PropFromInterface); Assert.Equal("hello-abstract-property", obj.ReturnAbstractFromProperty.AbstractProperty); } [Fact(DisplayName = Prefix + nameof(TestClassWithPrivateConstructorAndAutomaticProperties))] public void TestClassWithPrivateConstructorAndAutomaticProperties() { var obj = ClassWithPrivateConstructorAndAutomaticProperties.Create("Hello", "Bye"); Assert.Equal("Bye", obj.ReadWriteString); obj.ReadWriteString = "Hello"; Assert.Equal("Hello", obj.ReadOnlyString); } [Fact(DisplayName = Prefix + nameof(TestReturnInterfaceFromOverride))] public void TestReturnInterfaceFromOverride() { var n = 1337; var obj = new OverrideReturnsObject(); var arg = new NumberReturner(n); Assert.Equal(4 * n, obj.Test(arg)); } [Fact(DisplayName = Prefix + nameof(NullShouldBeTreatedAsUndefined))] public void NullShouldBeTreatedAsUndefined() { // ctor var obj = new NullShouldBeTreatedAsUndefined("param1"); // method argument obj.GiveMeUndefined(); // inside object obj.GiveMeUndefinedInsideAnObject(new NullShouldBeTreatedAsUndefinedData { ThisShouldBeUndefined = null, #pragma warning disable CS8625 ArrayWithThreeElementsAndUndefinedAsSecondArgument = new object[] {"hello", null, "world"} #pragma warning restore CS8625 }); // property obj.ChangeMeToUndefined = null; obj.VerifyPropertyIsUndefined(); } [Fact(DisplayName = Prefix + nameof(OptionalAndVariadicArgumentsTest))] public void OptionalAndVariadicArgumentsTest() { // ctor new NullShouldBeTreatedAsUndefined("param1", null); var objWithoutOptionalProvided = new NullShouldBeTreatedAsUndefined("param1"); // method argument called with null value objWithoutOptionalProvided.GiveMeUndefined(null); // method argument called without null value objWithoutOptionalProvided.GiveMeUndefined(); // Array with no value in constructor params var variadicClassNoParams = new VariadicMethod(); // Array with null value in constructor params #pragma warning disable CS8625 new VariadicMethod(null); #pragma warning restore CS8625 // Array with one value in constructor params new VariadicMethod(1); // Array with multiple values in constructor params new VariadicMethod(1, 2, 3, 4); // Variadic parameter with null passed #pragma warning disable CS8625 variadicClassNoParams.AsArray(double.MinValue, null); #pragma warning restore CS8625 // Variadic parameter with default value used variadicClassNoParams.AsArray(double.MinValue); var list = new List(); // Variadic parameter with array with no value variadicClassNoParams.AsArray(double.MinValue, list.ToArray()); // Variadic parameter with array with one value list.Add(1d); variadicClassNoParams.AsArray(double.MinValue, list.ToArray()); // Variadic parameter with array with multiple value list.Add(2d); list.Add(3d); list.Add(4d); list.Add(5d); list.Add(6d); variadicClassNoParams.AsArray(double.MinValue, list.ToArray()); } [Fact(DisplayName = Prefix + nameof(JsiiAgentIsCorrect))] public void JsiiAgentIsCorrect() { Assert.Equal("DotNet/" + Environment.Version + "/.NETCoreApp,Version=v3.1/1.0.0.0", JsiiAgent.Value); } [Fact(DisplayName = Prefix + nameof(ReceiveInstanceOfPrivateClass))] public void ReceiveInstanceOfPrivateClass() { Assert.True(new ReturnsPrivateImplementationOfInterface().PrivateImplementation.Success); } [Fact(DisplayName = Prefix + nameof(ObjRefsAreLabelledUsingWithTheMostCorrectType))] public void ObjRefsAreLabelledUsingWithTheMostCorrectType() { var classRef = Constructors.MakeClass(); var ifaceRef = Constructors.MakeInterface(); Assert.Equal(typeof(InbetweenClass), classRef.GetType()); Assert.NotEqual(typeof(InbetweenClass), ifaceRef.GetType()); } [Fact(DisplayName = Prefix + nameof(EraseUnsetDataValues))] public void EraseUnsetDataValues() { var opts = new EraseUndefinedHashValuesOptions { Option1 = "option1" }; Assert.True(EraseUndefinedHashValues.DoesKeyExist(opts, "option1")); Assert.False(EraseUndefinedHashValues.DoesKeyExist(opts, "option2")); Assert.Equal(new Dictionary { ["prop2"] = "value2" }, EraseUndefinedHashValues.Prop1IsNull()); Assert.Equal(new Dictionary { [ "prop1"] = "value1" }, EraseUndefinedHashValues.Prop2IsUndefined()); } [Fact(DisplayName = Prefix + nameof(ObjectIdDoesNotGetReallocatedWhenTheConstructorPassesThisOut))] public void ObjectIdDoesNotGetReallocatedWhenTheConstructorPassesThisOut() { var reflector = new PartiallyInitializedThisConsumerImpl(); var obj = new ConstructorPassesThisOut(reflector); Assert.NotNull(obj); } [Fact(DisplayName = Prefix + nameof(CorrectlyReturnsFromVoidCallback))] public void CorrectlyReturnsFromVoidCallback() { var voidCallback = new VoidCallbackImpl(); voidCallback.CallMe(); Assert.True(voidCallback.MethodWasCalled); } [Fact(DisplayName = Prefix + nameof(CallbacksCorrectlyDeserializeArguments))] public void CallbacksCorrectlyDeserializeArguments() { var obj = new DataRendererSubclass(); Assert.Equal("{\n \"anumber\": 42,\n \"astring\": \"bazinga!\"\n}", obj.Render(null)); Assert.Equal("{\n \"Key\": {},\n \"Baz\": \"Zinga\"\n}", obj.RenderArbitrary(new Dictionary() { { "Key", obj }, { "Baz", "Zinga" } })); } [Fact(DisplayName = Prefix + nameof(MethodCanReturnArraysOfInterfaces))] public void MethodCanReturnArraysOfInterfaces() { var interfaces = InterfacesMaker.MakeInterfaces(4); Assert.Equal(4, interfaces.Length); } [Fact(DisplayName = Prefix + nameof(CanLeverageIndirectInterfacePolymorphism))] public void CanLeverageIndirectInterfacePolymorphism() { var provider = new AnonymousImplementationProvider(); Assert.Equal(1337d, provider.ProvideAsClass().Value); Assert.Equal(1337d, provider.ProvideAsInterface().Value); Assert.Equal("to implement", provider.ProvideAsInterface().Verb()); } [Fact(DisplayName = Prefix + nameof(CorrectlyDeserializesStructUnions))] public void CorrectlyDeserializesStructUnions() { var a0 = new StructA { RequiredString = "Present!", OptionalString = "Bazinga!" }; var a1 = new StructA { RequiredString = "Present!", OptionalNumber = 1337 }; var b0 = new StructB { RequiredString = "Present!", OptionalBoolean = true }; var b1 = new StructB { RequiredString = "Present!", OptionalStructA = a1 }; Assert.True(StructUnionConsumer.IsStructA(a0)); Assert.True(StructUnionConsumer.IsStructA(a1)); Assert.False(StructUnionConsumer.IsStructA(b0)); Assert.False(StructUnionConsumer.IsStructA(b1)); Assert.False(StructUnionConsumer.IsStructB(a0)); Assert.False(StructUnionConsumer.IsStructB(a1)); Assert.True(StructUnionConsumer.IsStructB(b0)); Assert.True(StructUnionConsumer.IsStructB(b1)); } [Fact(DisplayName = Prefix + nameof(VariadicCallbacksAreHandledCorrectly))] public void VariadicCallbacksAreHandledCorrectly() { var method = new OverrideVariadicMethod(); var invoker = new VariadicInvoker(method); Assert.Equal(new double[]{2d}, invoker.AsArray(1)); Assert.Equal(new double[]{2d, 3d}, invoker.AsArray(1, 2)); Assert.Equal(new double[]{2d, 3d, 4d}, invoker.AsArray(1, 2, 3)); } [Fact(DisplayName = Prefix + nameof(ReturnSubclassThatImplementsInterface976))] public void ReturnSubclassThatImplementsInterface976() { var obj = SomeTypeJsii976.ReturnReturn(); Assert.Equal(333, obj.Foo); } private sealed class OverrideVariadicMethod : VariadicMethod { public override double[] AsArray(double first, params double[] others) { #pragma warning disable CS8604 return base.AsArray(first + 1, others?.Select(n => n + 1).ToArray()); #pragma warning restore CS8604 } } [Fact(DisplayName = Prefix + nameof(OptionalCallbackArgumentsAreHandledCorrectly))] public void OptionalCallbackArgumentsAreHandledCorrectly() { var noOption = new InterfaceWithOptionalMethodArguments(); new OptionalArgumentInvoker(noOption).InvokeWithoutOptional(); Assert.True(noOption.Invoked); var option = new InterfaceWithOptionalMethodArguments(1337); new OptionalArgumentInvoker(option).InvokeWithOptional(); Assert.True(option.Invoked); } private sealed class InterfaceWithOptionalMethodArguments : DeputyBase, IInterfaceWithOptionalMethodArguments { private readonly double? _optionalValue; public InterfaceWithOptionalMethodArguments(double? optionalValue = null) { _optionalValue = optionalValue; } public Boolean Invoked { get; private set; } public void Hello(string arg1, double? arg2 = null) { Invoked = true; Assert.Equal("Howdy", arg1); Assert.Equal(_optionalValue, arg2); } } class DataRendererSubclass : DataRenderer { public override string RenderMap(IDictionary map) { return base.RenderMap(map); } } class VoidCallbackImpl : VoidCallback { protected override void OverrideMe() { // Do nothing! } } class PartiallyInitializedThisConsumerImpl : PartiallyInitializedThisConsumer { public override String ConsumePartiallyInitializedThis(ConstructorPassesThisOut obj, DateTime dt, AllTypesEnum ev) { Assert.NotNull(obj); Assert.Equal(DateTime.UnixEpoch, dt); Assert.Equal(AllTypesEnum.THIS_IS_GREAT, ev); return "OK"; } } class NumberReturner : DeputyBase, IReturnsNumber { public NumberReturner(double number) { NumberProp = new Number(number); } public Number NumberProp { get; } public IDoublable ObtainNumber() { return new Doublable(this.NumberProp); } class Doublable : DeputyBase, IDoublable { public Doublable(Number number) { this.DoubleValue = number.DoubleValue; } public Double DoubleValue { get; } } } class MulTen : Multiply { public MulTen(int value) : base(new Number(value), new Number(10)) { } } class AddTen : Add { public AddTen(int value) : base(new Number(value), new Number(10)) { } } class OverrideAsyncMethods : AsyncVirtualMethods { public override double OverrideMe(double mult) { return Foo() * 2; } public int Foo() { return 2222; } } class OverrideAsyncMethodsByBaseClass : OverrideAsyncMethods { } class OverrideCallsSuper : AsyncVirtualMethods { public override double OverrideMe(double mult) { double superRet = base.OverrideMe(mult); return ((int) superRet) * 10 + 1; } } class TwoOverrides : AsyncVirtualMethods { public override double OverrideMe(double mult) { return 666; } public override double OverrideMeToo() { return 10; } } class SyncOverrides : SyncVirtualMethods { public override double VirtualMethod(double n) { if (ReturnSuper) { return base.VirtualMethod(n); } if (CallAsync) { OverrideAsyncMethods obj = new OverrideAsyncMethods(); return obj.CallMe(); } return 5 * ((int) n) * Multiplier; } public int Multiplier { get; set; } = 1; public bool ReturnSuper { get; set; } public bool CallAsync { get; set; } public override string TheProperty { get => "I am an override!"; set => AnotherTheProperty = value; } public string? AnotherTheProperty { get; set; } } class PureNativeFriendlyRandom : DeputyBase, IFriendlyRandomGenerator { int _nextNumber = 1000; public double Next() { int n = _nextNumber; _nextNumber += 1000; return n; } public string Hello() { return "I am a native!"; } } class SubclassNativeFriendlyRandom : Number, IFriendly, IRandomNumberGenerator { int _nextNumber; public SubclassNativeFriendlyRandom() : base(908) { _nextNumber = 100; } public string Hello() { return "SubclassNativeFriendlyRandom"; } public double Next() { int next = _nextNumber; _nextNumber += 100; return next; } } [Fact(DisplayName = Prefix + nameof(StructsCanBeDowncastedToParentType))] public void StructsCanBeDowncastedToParentType() { Assert.NotNull(Demonstrate982.TakeThis()); Assert.NotNull(Demonstrate982.TakeThisToo()); } [Fact(DisplayName = Prefix + nameof(NullIsAValidOptionalList))] public void NullIsAValidOptionalList() { Assert.Null(DisappointingCollectionSource.MaybeList); } [Fact(DisplayName = Prefix + nameof(NullIsAValidOptionalMap))] public void NullIsAValidOptionalMap() { Assert.Null(DisappointingCollectionSource.MaybeMap); } [Fact(DisplayName = Prefix + nameof(CanUseInterfaceSetters))] public void CanUseInterfaceSetters() { var obj = ObjectWithPropertyProvider.Provide(); obj.Property = "New Value"; Assert.True(obj.WasSet()); } [Fact(DisplayName = Prefix + nameof(StructsAreUndecoratedOntheWayToKernel))] public void StructsAreUndecoratedOntheWayToKernel() { var json = JsonFormatter.Stringify(new StructB {RequiredString = "Bazinga!", OptionalBoolean = false})!; var actual = JObject.Parse(json); var expected = new JObject(); expected.Add("RequiredString", "Bazinga!"); expected.Add("OptionalBoolean", false); Assert.Equal(expected, actual); } [Fact(DisplayName = Prefix + nameof(CanObtainReferenceWithOverloadedSetters))] public void CanObtainReferenceWithOverloadedSetters() { Assert.NotNull(ConfusingToJackson.MakeInstance()); } [Fact(DisplayName = Prefix + nameof(CanObtainStructReferenceWithOverloadedSetters))] public void CanObtainStructReferenceWithOverloadedSetters() { Assert.NotNull(ConfusingToJackson.MakeStructInstance()); } [Fact(DisplayName = Prefix + nameof(PureInterfacesCanBeUsedTransparently))] public void PureInterfacesCanBeUsedTransparently() { var expected = new StructB { RequiredString = "It's Britney b**ch!" }; var del = new StructReturningDelegate(expected); var consumer = new ConsumePureInterface(del); Assert.Equal(expected.RequiredString, consumer.WorkItBaby().RequiredString); } private sealed class StructReturningDelegate: DeputyBase, IStructReturningDelegate { internal StructReturningDelegate(StructB expected) { Expected = expected; } private IStructB Expected { get; } public IStructB ReturnStruct() { return Expected; } } [Fact(DisplayName = Prefix + nameof(PureInterfacesCanBeUsedTransparently_WhenTransitivelyImplemented))] public void PureInterfacesCanBeUsedTransparently_WhenTransitivelyImplemented() { var expected = new StructB { RequiredString = "It's Britney b**ch!" }; var del = new IndirectlyImplementsStructReturningDelegate(expected); var consumer = new ConsumePureInterface(del); Assert.Equal(expected.RequiredString, consumer.WorkItBaby().RequiredString); } private sealed class IndirectlyImplementsStructReturningDelegate : ImplementsStructReturningDelegate { internal IndirectlyImplementsStructReturningDelegate(StructB @struct) : base(@struct) {} } private class ImplementsStructReturningDelegate : DeputyBase, IStructReturningDelegate { private StructB Struct; protected ImplementsStructReturningDelegate(StructB @struct) { this.Struct = @struct; } public IStructB ReturnStruct() { return Struct; } } [Fact(DisplayName = Prefix + nameof(PureInterfacesCanBeUsedTransparently_WhenAddedToJsiiType))] public void PureInterfacesCanBeUsedTransparently_WhenAddedToJsiiType() { var expected = new StructB { RequiredString = "It's Britney b**ch!" }; var del = new ImplementsAdditionalInterface(expected); var consumer = new ConsumePureInterface(del); Assert.Equal(expected.RequiredString, consumer.WorkItBaby().RequiredString); } private sealed class ImplementsAdditionalInterface : AllTypes, IStructReturningDelegate { private StructB Struct; internal ImplementsAdditionalInterface(StructB @struct) { this.Struct = @struct; } public IStructB ReturnStruct() { return Struct; } } [Fact(DisplayName = Prefix + nameof(LiftedKwargWithSameNameAsPositionalArg))] public void LiftedKwargWithSameNameAsPositionalArg() { // This is a replication of a test that mostly affects languages with keyword arguments (e.g: Python, Ruby, ...) var bell = new Bell(); var amb = new AmbiguousParameters(bell, new StructParameterType { Scope = "Driiiing!" }); Assert.Equal(bell, amb.Scope); Assert.Equal("Driiiing!", amb.Props.Scope); } [Fact(DisplayName = Prefix + nameof(AbstractMembersAreCorrectlyHandled))] public void AbstractMembersAreCorrectlyHandled() { var abstractSuite = new AbstractSuiteImpl(); Assert.Equal("Wrapped>", abstractSuite.WorkItAll("Oomf!")); } private sealed class AbstractSuiteImpl : AbstractSuite { private string _property = ""; public AbstractSuiteImpl() {} protected override string SomeMethod(string str) { return $"Wrapped<{str}>"; } protected override string Property { get => _property; set => _property = $"String<{value}>"; } } [Fact(DisplayName = Prefix + nameof(CollectionOfInterfaces_ListOfStructs))] public void CollectionOfInterfaces_ListOfStructs() { foreach (var elt in InterfaceCollections.ListOfStructs()) { Assert.IsAssignableFrom(elt); } } [Fact(DisplayName = Prefix + nameof(CollectionOfInterfaces_ListOfInterfaces))] public void CollectionOfInterfaces_ListOfInterfaces() { foreach (var elt in InterfaceCollections.ListOfInterfaces()) { Assert.IsAssignableFrom(elt); } } [Fact(DisplayName = Prefix + nameof(CollectionOfInterfaces_MapOfStructs))] public void CollectionOfInterfaces_MapOfStructs() { foreach (var elt in InterfaceCollections.MapOfStructs().Values) { Assert.IsAssignableFrom(elt); } } [Fact(DisplayName = Prefix + nameof(CollectionOfInterfaces_MapOfInterfaces))] public void CollectionOfInterfaces_MapOfInterfaces() { foreach (var elt in InterfaceCollections.MapOfInterfaces().Values) { Assert.IsAssignableFrom(elt); } } [Fact(DisplayName = Prefix + nameof(BurriedAnonymousObject))] public void BurriedAnonymousObject() { var subject = new BurriedAnonymousObjectImpl(); Assert.True(subject.Check()); } private sealed class BurriedAnonymousObjectImpl : BurriedAnonymousObject { public override object GiveItBack(object value) { return value; } } [Fact(DisplayName = Prefix + nameof(Iso8601DoesNotDeserializeToDate))] public void Iso8601DoesNotDeserializeToDate() { var now = $"{DateTime.UtcNow.ToString("s")}Z"; var wallClock = new WallClock(now); var entropy = new MildEntropy(wallClock); Assert.Equal(now, entropy.Increase()); } private sealed class WallClock: DeputyBase, IWallClock { private String _frozenTime; public WallClock(String frozenTime) { _frozenTime = frozenTime; } public String Iso8601Now() { return _frozenTime; } } private sealed class MildEntropy: Entropy { public MildEntropy(IWallClock clock): base(clock) { } public override String Repeat(String word) { return word; } } [Fact(DisplayName = Prefix + nameof(ArrayOfInterfaces))] public void ArrayOfInterfaces() { var bells = new IBell[1][]; bells[0] = new IBell[1]; bells[0][0] = new Bell(); var allTypes = new AllTypes(); allTypes.AnyProperty = bells; Assert.Equal(bells, allTypes.AnyProperty); } [Fact(DisplayName = Prefix + nameof(ClassCanBeUsedWhenNotExpressedlyLoaded))] public void ClassCanBeUsedWhenNotExpressedlyLoaded() { new Cdk16625Impl().Test(); } private sealed class Cdk16625Impl: Cdk16625 { protected override double Unwrap(IRandomNumberGenerator rng) { return rng.Next(); } } [Fact(DisplayName = Prefix + nameof(StrippedDeprecatedMemberCanBeReceived))] public void StrippedDeprecatedMemberCanBeReceived() { Assert.NotNull(InterfaceFactory.Create()); } } }