From 6035eaf540f41627fce8a8348e997f32d7ebe954 Mon Sep 17 00:00:00 2001 From: Jacob Trimble Date: Thu, 7 Apr 2016 20:02:49 -0700 Subject: [PATCH] Add unit tests for standard libraries. This adds a number of unit tests for the standard libraries. These tests are also end-to-end tests that test the whole library. They are simply Lua code that calls the libraries and checks the results. This also fixes a number of bugs found. Issue #8 --- ModMaker.Lua.Net/Compiler/CodeCompiler.cs | 4 +- ModMaker.Lua.Net/NetHelpers.cs | 2 +- .../LuaLibraries/LuaLibraries.Bit32.cs | 105 ++-- .../Runtime/LuaLibraries/LuaLibraries.Math.cs | 2 +- .../LuaLibraries/LuaLibraries.String.cs | 91 +++- .../LuaLibraries/LuaLibraries.Table.cs | 28 +- .../Runtime/LuaLibraries/LuaLibraries.cs | 8 + ModMaker.Lua/Helpers.cs | 2 +- ModMaker.Lua/ModMaker.Lua.csproj | 43 ++ ModMaker.Lua/Parser/PlainParser.cs | 4 +- ModMaker.Lua/Runtime/LuaValues/LuaFunction.cs | 2 +- Unit Tests/Net/Runtime/LuaLibraries/Bit32.cs | 458 ++++++++++++++++++ .../Runtime/LuaLibraries/LibraryTestBase.cs | 97 ++++ Unit Tests/Net/Runtime/LuaLibraries/Math.cs | 203 ++++++++ Unit Tests/Net/Runtime/LuaLibraries/String.cs | 446 +++++++++++++++++ Unit Tests/Net/Runtime/LuaLibraries/Table.cs | 254 ++++++++++ Unit Tests/Unit Tests.csproj | 5 + 17 files changed, 1677 insertions(+), 77 deletions(-) create mode 100644 Unit Tests/Net/Runtime/LuaLibraries/Bit32.cs create mode 100644 Unit Tests/Net/Runtime/LuaLibraries/LibraryTestBase.cs create mode 100644 Unit Tests/Net/Runtime/LuaLibraries/Math.cs create mode 100644 Unit Tests/Net/Runtime/LuaLibraries/String.cs create mode 100644 Unit Tests/Net/Runtime/LuaLibraries/Table.cs diff --git a/ModMaker.Lua.Net/Compiler/CodeCompiler.cs b/ModMaker.Lua.Net/Compiler/CodeCompiler.cs index 8294790..b052d29 100644 --- a/ModMaker.Lua.Net/Compiler/CodeCompiler.cs +++ b/ModMaker.Lua.Net/Compiler/CodeCompiler.cs @@ -130,7 +130,7 @@ public ILuaValue Compile(ILuaEnvironment E, IParseItem item, string name) // compile the code CompilerVisitor cVisitor = new CompilerVisitor(cb); item.Accept(cVisitor); - var ret = cb.CreateChunk(E);this.Save("test.dll", true); + var ret = cb.CreateChunk(E); return ret; } /// @@ -153,7 +153,7 @@ public Delegate CreateDelegate(ILuaEnvironment E, Type type, ILuaValue method) throw new ArgumentNullException("type"); if (method == null) throw new ArgumentNullException("method"); - if (typeof(Delegate).IsAssignableFrom(type)) + if (!typeof(Delegate).IsAssignableFrom(type.BaseType)) throw new ArgumentException(Resources.DeriveFromDelegate); // search through the cache for a compatible delegate helper diff --git a/ModMaker.Lua.Net/NetHelpers.cs b/ModMaker.Lua.Net/NetHelpers.cs index 1f6d056..6cc7747 100644 --- a/ModMaker.Lua.Net/NetHelpers.cs +++ b/ModMaker.Lua.Net/NetHelpers.cs @@ -112,7 +112,7 @@ public static ModuleBuilder GetModuleBuilder() /// The newly created type. public static TypeBuilder DefineGlobalType(string prefix) { - return _mb.DefineType(prefix + "_" + (_tid++)); + return GetModuleBuilder().DefineType(prefix + "_" + (_tid++)); } /// diff --git a/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Bit32.cs b/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Bit32.cs index 9277566..0e06bf4 100644 --- a/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Bit32.cs +++ b/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Bit32.cs @@ -12,97 +12,126 @@ static class Bit32 public static void Initialize(ILuaEnvironment E) { ILuaValue bit32 = E.Runtime.CreateTable(); - Register(E, bit32, (Func)arshift); - Register(E, bit32, (Func)band); - Register(E, bit32, (Func)bnot); - Register(E, bit32, (Func)bor); - Register(E, bit32, (Func)btest); - Register(E, bit32, (Func)bxor); - Register(E, bit32, (Func)extract); - Register(E, bit32, (Func)replace); - Register(E, bit32, (Func)lrotate); - Register(E, bit32, (Func)lshift); - Register(E, bit32, (Func)rrotate); - Register(E, bit32, (Func)rshift); + Register(E, bit32, (Func)arshift); + Register(E, bit32, (Func)band); + Register(E, bit32, (Func)bnot); + Register(E, bit32, (Func)bor); + Register(E, bit32, (Func)btest); + Register(E, bit32, (Func)bxor); + Register(E, bit32, (Func)extract); + Register(E, bit32, (Func)replace); + Register(E, bit32, (Func)lrotate); + Register(E, bit32, (Func)lshift); + Register(E, bit32, (Func)rrotate); + Register(E, bit32, (Func)rshift); E.GlobalsTable.SetItemRaw(E.Runtime.CreateValue("bit32"), bit32); } - static uint arshift(double x, double disp) + // NOTE: This uses double as an argument since using Convert.ToUint will fail to + // convert larger numbers. So we accept a double and then cast to uint manually + // which will truncate the number to 2^32. + + [IgnoreExtraArguments] + static uint arshift(double x, int disp) { if (System.Math.Abs(disp) > 31) - return 0; + return x >= 0x800000 && disp > 0 ? 0xffffffff : 0; - return (uint)(x / System.Math.Pow(2, disp)); + var xAsInt = (int)((uint)x & 0xffffffff); + if (disp >= 0) + return (uint)(xAsInt >> disp); + else + return (uint)xAsInt << -disp; } - static uint band(params uint[] args) + static uint band(params double[] args) { - return args.Aggregate((a, b) => a & b); + return args.Select(a => (uint)a).Aggregate(uint.MaxValue, (a, b) => a & b); } - static uint bnot(uint x) + [IgnoreExtraArguments] + static uint bnot(double x) { - return ~x; + return ~(uint)x; } - static uint bor(params uint[] args) + static uint bor(params double[] args) { - return args.Aggregate((a, b) => a | b); + return args.Select(a => (uint)a).Aggregate(0u, (a, b) => a | b); } - static bool btest(params uint[] args) + static bool btest(params double[] args) { - return bor(args) != 0; + return band(args) != 0; } - static uint bxor(params uint[] args) + static uint bxor(params double[] args) { - return args.Aggregate((a, b) => a ^ b); + return args.Select(a => (uint)a).Aggregate(0u, (a, b) => a ^ b); } - static uint extract(uint source, int field, int width = 1) + [IgnoreExtraArguments] + static uint extract(double sourceDouble, int field, int width = 1) { - if (field > 31 || width + field > 31 || field < 0 || width < 0) - throw new ArgumentException("Attempt to access bits outside the allowed range."); + if (width + field > 31 || field < 0 || width < 0) + { + throw new ArgumentException( + "Attempt to access bits outside the allowed range."); + } + uint source = (uint)sourceDouble; uint mask = (uint)((1 << width) - 1); return ((source >> field) & mask); } - static uint replace(uint source, uint repl, int field, int width = 1) + [IgnoreExtraArguments] + static uint replace(double sourceDouble, double replDouble, int field, int width = 1) { - if (field > 31 || width + field > 31 || field < 0 || width < 0) - throw new ArgumentException("Attempt to access bits outside the allowed range."); + uint source = (uint)sourceDouble; + uint repl = (uint)replDouble; + if (width + field > 31 || field < 0 || width < 0) + { + throw new ArgumentException( + "Attempt to access bits outside the allowed range."); + } - uint mask = (uint)((1 << width) - 1); + uint mask = (1u << width) - 1; repl &= mask; source &= ~(mask << field); return (source | (repl << field)); } - static uint lrotate(uint x, int disp) + [IgnoreExtraArguments] + static uint lrotate(double xDouble, int disp) { // % will still remain negative. + uint x = (uint)xDouble; disp %= 32; if (disp >= 0) return ((x << disp) | (x >> (32 - disp))); else return ((x >> -disp) | (x << (32 + disp))); } - static uint lshift(uint x, int disp) + [IgnoreExtraArguments] + static uint lshift(double xDouble, int disp) { - if (disp > 31) + uint x = (uint)xDouble; + if (System.Math.Abs(disp) > 31) return 0; else if (disp >= 0) return x << disp; else return x >> -disp; } - static uint rrotate(uint x, int disp) + [IgnoreExtraArguments] + static uint rrotate(double xDouble, int disp) { // % will still remain negative. + uint x = (uint)xDouble; disp %= 32; if (disp >= 0) return ((x >> disp) | (x << (32 - disp))); else return ((x << -disp) | (x >> (32 + disp))); } - static uint rshift(uint x, int disp) + [IgnoreExtraArguments] + static uint rshift(double xDouble, int disp) { - if (disp > 31) + uint x = (uint)xDouble; + if (System.Math.Abs(disp) > 31) return 0; else if (disp >= 0) return x >> disp; diff --git a/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Math.cs b/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Math.cs index b15014c..a20bfe4 100644 --- a/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Math.cs +++ b/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Math.cs @@ -109,7 +109,7 @@ static double random(int? min = null, int? max = null) if (min == null) return rand_.NextDouble(); else if (max == null) - return rand_.Next(min.Value); + return rand_.Next(1, min.Value); else return rand_.Next(min.Value, max.Value); } diff --git a/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.String.cs b/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.String.cs index e1ce89f..5b9e101 100644 --- a/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.String.cs +++ b/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.String.cs @@ -18,34 +18,39 @@ static class String public static void Initialize(ILuaEnvironment E) { var str = E.Runtime.CreateTable(); - Register(E, str, (Func>)byte_, "byte"); + Register(E, str, (Func>)byte_, "byte"); Register(E, str, (Func)char_, "char"); Register(E, str, (Func)find); Register(E, str, (Func)format); Register(E, str, (Func)gmatch); - Register(E, str, (Func)gsub); + Register(E, str, (Func)gsub); Register(E, str, (Func)len); Register(E, str, (Func)lower); Register(E, str, (Func>)match); Register(E, str, (Func)rep); Register(E, str, (Func)reverse); - Register(E, str, (Func)sub); + Register(E, str, (Func)sub); Register(E, str, (Func)upper); E.GlobalsTable.SetItemRaw(E.Runtime.CreateValue("string"), str); } [MultipleReturn] - static IEnumerable byte_(string source, int i = 1, int? j = null) + static IEnumerable byte_(string source, int i = 1, int? j = null) { - return sub(source, i, j); + CheckNotNull("string.byte", source); + return sub(source, i, j ?? i).Select(c => (int)c); } static string char_(params int[] chars) { StringBuilder ret = new StringBuilder(chars.Length); foreach (int c in chars) { - if (c <= 0xFFFF) + if (c < 0) + { + throw new ArgumentException("Character out of range for 'string.char'."); + } + else if (c <= 0xFFFF) { // This may be a surrogate pair, assume they know what they are doing. ret.Append((char)c); @@ -65,62 +70,81 @@ static string char_(params int[] chars) [MultipleReturn] static object[] find(string source, string pattern, int start = 1, bool plain = false) { + CheckNotNull("string.find", source); + CheckNotNull("string.find", pattern); + if (start >= source.Length) + return new object[0]; + start = normalizeIndex_(source.Length, start); if (plain) { - int i = source.IndexOf(pattern, start, StringComparison.CurrentCulture); + int i = source.IndexOf(pattern, start - 1, StringComparison.CurrentCulture); if (i == -1) return new object[0]; else - return new object[] { i, (i + pattern.Length) }; + return new object[] { i + 1, (i + pattern.Length) }; } Regex reg = new Regex(pattern); - Match match = reg.Match(source, start); - if (match == null) + Match match = reg.Match(source, start - 1); + if (match == null || !match.Success) return new object[0]; - return new object[] { match.Index, (match.Index + match.Length) } - .Concat(match.Captures.Cast().Select(c => c.Value)) + return new object[] { match.Index + 1, (match.Index + match.Length) } + .Concat(match.Groups.Cast().Skip(1).Select(c => c.Value)) .ToArray(); } static string format(string format, params object[] args) { + CheckNotNull("string.format", format); return System.String.Format(format, args); } static object gmatch(string source, string pattern) { + CheckNotNull("string.gmatch", source); + CheckNotNull("string.gmatch", pattern); var helper = new gmatchIter(Regex.Matches(source, pattern)); return (Func)helper.gmatch_iter; } - static string gsub(string source, string pattern, ILuaValue repl) + static string gsub(string source, string pattern, ILuaValue repl, int n = int.MaxValue) { - return Regex.Replace(source, pattern, new gsubHelper(repl).Match); + CheckNotNull("string.gsub", source); + CheckNotNull("string.gsub", pattern); + return Regex.Replace(source, pattern, new gsubHelper(repl, n).Match); } static int len(string str) { + CheckNotNull("string.len", str); return str.Length; } static string lower(string str) { + CheckNotNull("string.lower", str); return str.ToLower(CultureInfo.CurrentCulture); } [MultipleReturn] static IEnumerable match(string source, string pattern, int start = 1) { + CheckNotNull("string.match", source); + CheckNotNull("string.match", pattern); start = normalizeIndex_(source.Length, start); Regex reg = new Regex(pattern); - Match match = reg.Match(source, start); - if (match == null) + Match match = reg.Match(source, start - 1); + if (match == null || !match.Success) return new string[0]; - return match.Captures.Cast().Select(c => c.Value); + if (match.Groups.Count == 1) + return new[] { match.Value }; + else + return match.Groups.Cast().Skip(1).Select(c => c.Value); } static string rep(string str, int rep, string sep = null) { + CheckNotNull("string.rep", str); if (rep < 1) return ""; + sep = sep ?? ""; StringBuilder ret = new StringBuilder((str.Length + sep.Length) * rep); for (int i = 0; i < rep - 1; i++) { @@ -132,12 +156,19 @@ static string rep(string str, int rep, string sep = null) } static string reverse(string str) { - return new string(str.Reverse().ToArray()); + CheckNotNull("string.reverse", str); + // Ensure the reverse does not break surrogate pairs. + var matches = Regex.Matches(str, @"\p{IsHighSurrogates}\p{IsLowSurrogates}|."); + return matches.Cast().Select(c => c.Value).Reverse().Aggregate("", (a, b) => a + b); } - static string sub(string source, int i = 1, int? j = null) + static string sub(string source, int i, int j = -1) { + CheckNotNull("string.sub", source); + if (i > source.Length) + return ""; + int start = normalizeIndex_(source.Length, i); - int end = j != null ? normalizeIndex_(source.Length, j.Value) : start; + int end = normalizeIndex_(source.Length, j); if (start > end) return ""; @@ -145,6 +176,7 @@ static string sub(string source, int i = 1, int? j = null) } static string upper(string str) { + CheckNotNull("string.upper", str); return str.ToUpper(CultureInfo.CurrentCulture); } @@ -165,7 +197,10 @@ public string[] gmatch_iter(params object[] dummy) Match cur = matches[index]; index++; - return cur.Groups.Cast().Select(c => c.Value).ToArray(); + if (cur.Groups.Count == 1) + return new[] { cur.Groups[0].Value }; + else + return cur.Groups.Cast().Select(c => c.Value).Skip(1).ToArray(); } MatchCollection matches; @@ -173,8 +208,11 @@ public string[] gmatch_iter(params object[] dummy) } class gsubHelper { - public gsubHelper(ILuaValue value) + public gsubHelper(ILuaValue value, int max) { + count_ = 0; + max_ = max; + if (value.ValueType == LuaValueType.String) string_ = (string)value.GetValue(); else if (value.ValueType == LuaValueType.Table) @@ -187,6 +225,10 @@ public gsubHelper(ILuaValue value) public string Match(Match match) { + if (count_ >= max_) + return match.Value; + + count_++; if (string_ != null) { return Regex.Replace(string_, @"%[0-9%]", m => @@ -199,7 +241,8 @@ public string Match(Match match) } else if (table_ != null) { - ILuaValue value = table_.GetItemRaw(new LuaString(match.Value)); + string key = match.Groups.Count == 0 ? match.Value : match.Groups[1].Value; + ILuaValue value = table_.GetItemRaw(new LuaString(key)); if (value != null && value.IsTrue) return value.ToString(); } @@ -219,6 +262,8 @@ public string Match(Match match) return match.Value; } + int count_; + int max_; string string_; ILuaTable table_; ILuaValue method_; diff --git a/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Table.cs b/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Table.cs index 78ee9a7..cc3304b 100644 --- a/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Table.cs +++ b/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.Table.cs @@ -33,11 +33,13 @@ public void Initialize() string concat(ILuaTable table, string sep = null, int i = 1, int j = -1) { + CheckNotNull("table.concat", table); int len = (int)(table.Length().AsDouble() ?? 0); + if (i >= len) + return ""; + i = normalizeIndex_(len, i); j = normalizeIndex_(len, j); - if (j < i) - return ""; StringBuilder str = new StringBuilder(); for (; i <= j; i++) @@ -58,6 +60,8 @@ string concat(ILuaTable table, string sep = null, int i = 1, int j = -1) } void insert(ILuaTable table, ILuaValue pos, ILuaValue value = null) { + CheckNotNull("table.insert", table); + CheckNotNull("table.insert", pos); double i; double len = table.Length().AsDouble() ?? 0; if (value == null) @@ -66,10 +70,15 @@ void insert(ILuaTable table, ILuaValue pos, ILuaValue value = null) i = len + 1; } else + { i = pos.AsDouble() ?? 0; + } if (i > len + 1 || i < 1 || i % 1 != 0) - throw new ArgumentException("Position given to function 'table.insert' is outside valid range."); + { + throw new ArgumentException( + "Position given to function 'table.insert' is outside valid range."); + } for (double d = len; d >= i; d--) { @@ -90,6 +99,8 @@ ILuaValue pack(params ILuaValue[] args) } ILuaValue remove(ILuaTable table, int? pos = null) { + CheckNotNull("table.remove", table); + double len = table.Length().AsDouble() ?? 0; pos = pos ?? (int)len; if (pos > len + 1 || pos < 1) @@ -110,7 +121,7 @@ ILuaValue remove(ILuaTable table, int? pos = null) } void sort(ILuaTable table, ILuaValue comp = null) { - double len = table.Length().AsDouble() ?? 0; + CheckNotNull("table.sort", table); var comparer = new SortComparer(E, comp); ILuaValue[] elems = unpack(table).OrderBy(k => k, comparer).ToArray(); @@ -121,10 +132,11 @@ void sort(ILuaTable table, ILuaValue comp = null) } } [MultipleReturn] - IEnumerable unpack(ILuaTable table, int i = 1, int? j = null) + IEnumerable unpack(ILuaTable table, int i = 1, int? jOrNull = null) { - double len = table.Length().AsDouble() ?? 0; - j = j ?? (int)len; + CheckNotNull("table.unpack", table); + int len = (int)(table.Length().AsDouble() ?? 0); + int j = jOrNull ?? len; for (; i <= j; i++) { yield return table.GetItemRaw(E.Runtime.CreateValue(i)); @@ -149,7 +161,7 @@ public int Compare(ILuaValue x, ILuaValue y) if (method_ != null) { ILuaMultiValue ret = method_.Invoke( - null, false, -1, E_.Runtime.CreateMultiValueFromObj(x, y)); + LuaNil.Nil, false, -1, E_.Runtime.CreateMultiValueFromObj(x, y)); return ret.IsTrue ? -1 : 1; } diff --git a/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.cs b/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.cs index 858f571..9771b22 100644 --- a/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.cs +++ b/ModMaker.Lua.Net/Runtime/LuaLibraries/LuaLibraries.cs @@ -74,5 +74,13 @@ static int normalizeIndex_(int max, int i) return i; } + static void CheckNotNull(string name, object o) + { + if (o == null) + { + throw new ArgumentNullException( + "Argument to function '" + name + "' cannot be nil."); + } + } } } \ No newline at end of file diff --git a/ModMaker.Lua/Helpers.cs b/ModMaker.Lua/Helpers.cs index 412f251..c779273 100644 --- a/ModMaker.Lua/Helpers.cs +++ b/ModMaker.Lua/Helpers.cs @@ -831,7 +831,7 @@ public static object[] ConvertForArgs(ILuaMultiValue args, MethodBase method) for (int i = 0; i < min; i++) { // Skip params array since it's handled below. - if (i == min - 1 && hasParams) + if (i == param.Length - 1 && hasParams) continue; var paramType = param[i].ParameterType; diff --git a/ModMaker.Lua/ModMaker.Lua.csproj b/ModMaker.Lua/ModMaker.Lua.csproj index 3b01d95..4524309 100644 --- a/ModMaker.Lua/ModMaker.Lua.csproj +++ b/ModMaker.Lua/ModMaker.Lua.csproj @@ -80,6 +80,49 @@ TRACE prompt 4 + True + False + True + False + False + False + True + True + True + True + True + True + True + True + False + True + False + True + False + False + False + False + True + False + True + True + True + False + False + + + + + + + + True + False + False + True + None + %28none%29 + 0 diff --git a/ModMaker.Lua/Parser/PlainParser.cs b/ModMaker.Lua/Parser/PlainParser.cs index a9d0aa6..5fb90a1 100644 --- a/ModMaker.Lua/Parser/PlainParser.cs +++ b/ModMaker.Lua/Parser/PlainParser.cs @@ -910,7 +910,7 @@ protected virtual IParseExp ReadPrefixExp(ITokenizer input, ref Token token) Read(input, ref debug); try { - o = new LiteralItem(Convert.ToDouble(int.Parse(last.Value.Substring(1), + o = new LiteralItem(Convert.ToDouble(long.Parse(last.Value.Substring(1), NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture))) { Debug = last }; } catch (FormatException e) @@ -1097,7 +1097,7 @@ protected virtual IParseExp ReadPrefixExp(ITokenizer input, ref Token token) else if (input.Peek().Value == ")") break; else - throw new SyntaxException(string.Format(Resources.TokenInvalidExpecting2, last.Value, "function call", ",", ")"), input.Name, last); + throw new SyntaxException(string.Format(Resources.TokenInvalidExpecting2, input.Peek().Value, "function call", ",", ")"), input.Name, last); } if (input.Peek() == null) diff --git a/ModMaker.Lua/Runtime/LuaValues/LuaFunction.cs b/ModMaker.Lua/Runtime/LuaValues/LuaFunction.cs index b4ee14a..73261fd 100644 --- a/ModMaker.Lua/Runtime/LuaValues/LuaFunction.cs +++ b/ModMaker.Lua/Runtime/LuaValues/LuaFunction.cs @@ -147,7 +147,7 @@ public int CompareTo(ILuaValue other) /// A delegate that will call this function. public T As(ILuaEnvironment E) { - throw new NotImplementedException(); + return (T)(object)E.CodeCompiler.CreateDelegate(E, typeof(T), this); } /// diff --git a/Unit Tests/Net/Runtime/LuaLibraries/Bit32.cs b/Unit Tests/Net/Runtime/LuaLibraries/Bit32.cs new file mode 100644 index 0000000..2d7cb43 --- /dev/null +++ b/Unit Tests/Net/Runtime/LuaLibraries/Bit32.cs @@ -0,0 +1,458 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ModMaker.Lua.Runtime; +using System; + +namespace UnitTests.Net.Runtime.LuaLibraries +{ + [TestClass] + public class Bit32 : LibraryTestBase + { + #region arshift + [TestMethod] + public void arshift() + { + Lua.DoText(@" +-- Positive displacements. +assertEquals(0xf0, bit32.arshift(0xf00, 4), 'arshift: normal') +assertEquals(0xf0, bit32.arshift(0xf00, 4, 33), 'arshift: extra args') +assertEquals(0xf0, bit32.arshift(0xff00000f00, 4), 'arshift: larger than 32-bit') +assertEquals(0xff015600, bit32.arshift(0x80ab0000, 7), 'arshift: high-bit fill') +assertEquals(0xffffff00, bit32.arshift(0x80000000, 23), 'arshift: high-bit fill small value') +assertEquals(0x0, bit32.arshift(0x724624, 35), 'arshift: all shifted out') +assertEquals(0xffffffff, bit32.arshift(0x824624, 35), 'arshift: all shifted out (negative)') +assertEquals(0xfffffffc, bit32.arshift(-0xf, 2), 'arshift: negative source') + +-- Negative displacements. +assertEquals(0xf0, bit32.arshift(0xf, -4), 'arshift(left): normal') +assertEquals(0xf00000a0, bit32.arshift(0x0f00000a, -4), 'arshift(left): becomes negative') +assertEquals(0xf0, bit32.arshift(0xff0000000f, -4), 'arshift(left): larger than 32-bit') +assertEquals(0xff000000, bit32.arshift(0x1155ff, -24), 'arshift(left): drop high bits') +assertEquals(0x0, bit32.arshift(0x924624, -35), 'arshift(left): all shifted out') +assertEquals(0xffffff88, bit32.arshift(-0xf, -3), 'arshift(left): negative source') +"); + } + + [TestMethod] + public void arshift_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.arshift({0}, 2)"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.arshift(2, {0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void arshift_NotEnoughArgs() + { + Lua.DoText(@"bit32.arshift(0xf00)"); + } + #endregion + + #region band + [TestMethod] + public void band() + { + Lua.DoText(@" +assertEquals(0xea, bit32.band(0xff, 0xea), 'band: normal') +assertEquals(0xffffffff, bit32.band(), 'band: zero arguments') +assertEquals(0xea, bit32.band(0xea), 'band: one arguement') +assertEquals(0x22, bit32.band(0xff, 0xea, 0x7f, 0xa3), 'band: more than two') +assertEquals(0x00, bit32.band(0x42, 0xea, 0x7a, 0xa1), 'band: clears out') +assertEquals(0xaa000000, bit32.band(0xfa0000aa, 0xaf00aa00), 'band: large number') +assertEquals(0x0f, bit32.band(0xff0000000f, 0xff0000000f), 'band: larger than 32-bits') +"); + } + + [TestMethod] + public void band_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.band({0})"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.band(23, 4, {0}, 5)"); + } + #endregion + + #region bnot + [TestMethod] + public void bnot() + { + Lua.DoText(@" +assertEquals(0xff005533, bit32.bnot(0x00ffaacc), 'bnot: normal') +assertEquals(0xff005533, bit32.bnot(0x00ffaacc, 'cat'), 'bnot: extra args') +assertEquals(0x00ffaa66, bit32.bnot(0xff005599), 'bnot: high-bit set') +assertEquals(11, bit32.bnot(-12), 'bnot: negative') +assertEquals(0xffffffff, bit32.bnot(0), 'bnot: zero') +assertEquals(0xffffefdb, bit32.bnot(0x4500001024), 'bnot: larger than 32-bits') +"); + } + + [TestMethod] + public void bnot_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.bnot({0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void bnot_NotEnoughArgs() + { + Lua.DoText(@"bit32.bnot()"); + } + #endregion + + #region bor + [TestMethod] + public void bor() + { + Lua.DoText(@" +assertEquals(0xff, bit32.bor(0xaa, 0x55), 'bor: normal') +assertEquals(0x0, bit32.bor(), 'bor: zero arguments') +assertEquals(0xea, bit32.bor(0xea), 'bor: one arguement') +assertEquals(0xab, bit32.bor(0x01, 0x83, 0x21, 0x2a), 'bor: more than two') +assertEquals(0xff00aaaa, bit32.bor(0xfa0000aa, 0xaf00aa00), 'bor: large number') +assertEquals(0xff, bit32.bor(0xff000000f0, 0xff0000000f), 'bor: larger than 32-bits') +"); + } + + [TestMethod] + public void bor_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.bor({0})"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.bor(23, 4, {0}, 5)"); + } + #endregion + + #region btest + [TestMethod] + public void btest() + { + Lua.DoText(@" +assertEquals(false, bit32.btest(0xaa, 0x55), 'btest: normal') +assertEquals(true, bit32.btest(), 'btest: zero arguments') +assertEquals(true, bit32.btest(0xea), 'btest: one arguement') +assertEquals(false, bit32.btest(0x01, 0x83, 0x21, 0x2a), 'btest: more than two') +assertEquals(true, bit32.btest(0xfa0000aa, 0xaf00aa00), 'btest: large number') +assertEquals(false, bit32.btest(0xff000000f0, 0xff0000000f), 'btest: larger than 32-bits') +"); + } + + [TestMethod] + public void btest_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.btest({0})"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.btest(23, 4, {0}, 5)"); + } + #endregion + + #region bxor + [TestMethod] + public void bxor() + { + Lua.DoText(@" +assertEquals(0x82, bit32.bxor(0x24, 0xa6), 'bxor: normal') +assertEquals(0x0, bit32.bxor(), 'bxor: zero arguments') +assertEquals(0xea, bit32.bxor(0xea), 'bxor: one arguement') +assertEquals(0x89, bit32.bxor(0x01, 0x83, 0x21, 0x2a), 'bxor: more than two') +assertEquals(0x5500f848, bit32.bxor(0xfa005a1e, 0xaf00a256), 'bxor: large number') +assertEquals(0xff, bit32.bxor(0xff000000f0, 0xff0000000f), 'bxor: larger than 32-bits') +"); + } + + [TestMethod] + public void bxor_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.bxor({0})"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.bxor(23, 4, {0}, 5)"); + } + #endregion + + #region extract + [TestMethod] + public void extract() + { + Lua.DoText(@" +assertEquals(0x3a28, bit32.extract(0xa74e8a28, 6, 16), 'extract: normal') +assertEquals(0x0, bit32.extract(0xa74e8a28, 6), 'extract: defaults to 1 width') +assertEquals(0x4fe3a, bit32.extract(0x913f8ea, 2, 21, 'cat'), 'extract: extra args') +assertEquals(0x1e, bit32.extract(0xff0305d2f0, 3, 6), 'extract: larger than 32-bits') +"); + } + + [TestMethod] + public void extract_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.extract({0}, 0)"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.extract(4, {0})"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.extract(4, 3, {0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void extract_TooLargeIndex() + { + Lua.DoText(@"bit32.extract(0x3, 34)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void extract_TooLargeSize() + { + Lua.DoText(@"bit32.extract(0x3, 3, 42)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void extract_TooLargeIndexPlusSize() + { + Lua.DoText(@"bit32.extract(0x3, 21, 12)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void extract_NotEnoughArgs() + { + Lua.DoText(@"bit32.extract()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void extract_NotEnoughArgs2() + { + Lua.DoText(@"bit32.extract(0xf00)"); + } + #endregion + + #region replace + [TestMethod] + public void replace() + { + Lua.DoText(@" +assertEquals(0xa74a4a28, bit32.replace(0xa74e8a28, 0x452, 13, 6), 'replace: normal') +assertEquals(0xa74e8a20, bit32.replace(0xa74e8a28, 6, 3), 'replace: defaults to 1 width') +assertEquals(0xffff4c47, bit32.replace(-45625, 34, 5, 4), 'replace: negative source') +assertEquals(0xa74eca, bit32.replace(0xa74e8a, -42, 5, 4), 'replace: negative repl') +assertEquals(0x918aa, bit32.replace(0x918ea, 0x2462a, 2, 10, 'cat'), 'replace: extra args') +assertEquals(0xd0f0, bit32.replace(0xff0000d2f0, 0x23, 6, 4), 'replace: larger than 32-bits') +"); + } + + [TestMethod] + public void replace_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.replace({0}, 0)"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.replace(4, {0})"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.replace(4, 3, {0})"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.replace(4, 3, 4, {0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void replace_TooLargeIndex() + { + Lua.DoText(@"bit32.replace(0x3, 0x0, 34)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void replace_TooLargeSize() + { + Lua.DoText(@"bit32.replace(0x3, 0x0, 3, 42)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void replace_TooLargeIndexPlusSize() + { + Lua.DoText(@"bit32.replace(0x3, 0x0, 21, 12)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void replace_NotEnoughArgs() + { + Lua.DoText(@"bit32.replace()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void replace_NotEnoughArgs2() + { + Lua.DoText(@"bit32.replace(0xf00)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void replace_NotEnoughArgs3() + { + Lua.DoText(@"bit32.replace(0xf00, 0x00)"); + } + #endregion + + #region lrotate + [TestMethod] + public void lrotate() + { + Lua.DoText(@" +assertEquals(0xd3a28a29, bit32.lrotate(0xa74e8a28, 6), 'lrotate: normal') +assertEquals(0x4e9d1451, bit32.lrotate(0xa74e8a28, 65), 'lrotate: > 32') +assertEquals(0x4e9d1451, bit32.lrotate(0xa74e8a28, 65, 'cat'), 'lrotate: > 32') +assertEquals(0xc25a789e, bit32.lrotate(0x5a789ec2, -8), 'lrotate: negative') +assertEquals(0x27b0969e, bit32.lrotate(0x5a789ec2, -82), 'lrotate: negative < -32') +assertEquals(0xffff216f, bit32.lrotate(-3562, 4), 'lrotate: negative source') +assertEquals(0xd2f00, bit32.lrotate(0xff0000d2f0, 4), 'lrotate: larger than 32-bits') +"); + } + + [TestMethod] + public void lrotate_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.lrotate({0}, 0)"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.lrotate(4, {0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void lrotate_NotEnoughArgs() + { + Lua.DoText(@"bit32.lrotate()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void lrotate_NotEnoughArgs2() + { + Lua.DoText(@"bit32.lrotate(0xf00)"); + } + #endregion + + #region lshift + [TestMethod] + public void lshift() + { + Lua.DoText(@" +assertEquals(0xd3a28a00, bit32.lshift(0xa74e8a28, 6), 'lshift: normal') +assertEquals(0, bit32.lshift(0xa74e8a28, 65), 'lshift: > 32') +assertEquals(0, bit32.lshift(0xa74e8a28, 65, 'cat'), 'lshift: extra args') +assertEquals(0x5a789e, bit32.lshift(0x5a789ec2, -8), 'lshift: negative') +assertEquals(0, bit32.lshift(0x5a789ec2, -82), 'lshift: negative < -32') +assertEquals(0xffff2160, bit32.lshift(-3562, 4), 'lshift: negative source') +assertEquals(0xd2f00, bit32.lshift(0xff0000d2f0, 4), 'lshift: larger than 32-bits') +"); + } + + [TestMethod] + public void lshift_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.lshift({0}, 0)"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.lshift(4, {0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void lshift_NotEnoughArgs() + { + Lua.DoText(@"bit32.lshift()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void lshift_NotEnoughArgs2() + { + Lua.DoText(@"bit32.lshift(0xf00)"); + } + #endregion + + #region rrotate + [TestMethod] + public void rrotate() + { + Lua.DoText(@" +assertEquals(0xa29d3a28, bit32.rrotate(0xa74e8a28, 6), 'rrotate: normal') +assertEquals(0x53a74514, bit32.rrotate(0xa74e8a28, 65), 'rrotate: > 32') +assertEquals(0x53a74514, bit32.rrotate(0xa74e8a28, 65, 'cat'), 'rrotate: extra args') +assertEquals(0x789ec25a, bit32.rrotate(0x5a789ec2, -8), 'rrotate: negative') +assertEquals(0x7b0969e2, bit32.rrotate(0x5a789ec2, -82), 'rrotate: negative < -32') +assertEquals(0x6fffff21, bit32.rrotate(-3562, 4), 'rrotate: negative source') +assertEquals(0xd2f, bit32.rrotate(0xff0000d2f0, 4), 'rrotate: larger than 32-bits') +"); + } + + [TestMethod] + public void rrotate_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.rrotate({0}, 0)"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.rrotate(4, {0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void rrotate_NotEnoughArgs() + { + Lua.DoText(@"bit32.rrotate()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void rrotate_NotEnoughArgs2() + { + Lua.DoText(@"bit32.rrotate(0xf00)"); + } + #endregion + + #region rshift + [TestMethod] + public void rshift() + { + Lua.DoText(@" +assertEquals(0x29d3a28, bit32.rshift(0xa74e8a28, 6), 'rshift: normal') +assertEquals(0, bit32.rshift(0xa74e8a28, 65), 'rshift: > 32') +assertEquals(0, bit32.rshift(0xa74e8a28, 65, 'cat'), 'rshift: extra args') +assertEquals(0x789ec200, bit32.rshift(0x5a789ec2, -8), 'rshift: negative') +assertEquals(0, bit32.rshift(0x5a789ec2, -82), 'rshift: negative < -32') +assertEquals(0xfffff21, bit32.rshift(-3562, 4), 'rshift: negative source') +assertEquals(0xd2f, bit32.rshift(0xff0000d2f0, 4), 'rshift: larger than 32-bits') +"); + } + + [TestMethod] + public void rshift_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "bit32.rshift({0}, 0)"); + RunInvalidTypeTests(LuaValueType.Number, "bit32.rshift(4, {0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void rshift_NotEnoughArgs() + { + Lua.DoText(@"bit32.rshift()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void rshift_NotEnoughArgs2() + { + Lua.DoText(@"bit32.rshift(0xf00)"); + } + #endregion + + [Ignore] // TODO: Enable once coalesce is supported. + [TestMethod] + public void coalesce() + { + Lua.DoText(@" +assertEquals(10, bit32.arshift('43', 2), 'arshift: coalesce args') +assertEquals(0xaa, bit32.band('0xfa', 0xaf), 'band: coalesce args') +assertEquals(0xf0, bit32.bnot('0xffffff0f'), 'bnot: coalesce args') +assertEquals(0xff, bit32.bor('0xfa', 0xaf), 'bor: coalesce args') +assertEquals(true, bit32.btest('0xfa', 0x12), 'btest: coalesce args') +assertEquals(0x55, bit32.bxor('0xfa', 0xaf), 'bxor: coalesce args') +assertEquals(0x3, bit32.extract('0xfa', 4, 2), 'extract: coalesce args') +assertEquals(0xfe, bit32.replace('0xfa', 0x3, 2, 2), 'replace: coalesce args') +assertEquals(0x3e8, bit32.lrotate('0xfa', 2), 'lrotate: coalesce args') + +assertEquals(0x3e8, bit32.lshift('0xfa', 2), 'lshift: coalesce args') +assertEquals(0x3d, bit32.rrotate('0xf4', 2), 'rrotate: coalesce args') +assertEquals(0x3e, bit32.rshift('0xfa', 2), 'rshift: coalesce args') +"); + } + } +} \ No newline at end of file diff --git a/Unit Tests/Net/Runtime/LuaLibraries/LibraryTestBase.cs b/Unit Tests/Net/Runtime/LuaLibraries/LibraryTestBase.cs new file mode 100644 index 0000000..9c18a49 --- /dev/null +++ b/Unit Tests/Net/Runtime/LuaLibraries/LibraryTestBase.cs @@ -0,0 +1,97 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ModMaker.Lua; +using ModMaker.Lua.Runtime; +using ModMaker.Lua.Runtime.LuaValues; +using System; +using System.Linq; + +namespace UnitTests.Net.Runtime.LuaLibraries +{ + public class LibraryTestBase + { + /// + /// A custom type that is passed to Lua for testing. + /// + public class UserData + { + /// + /// Required explicit constructor for Lua. + /// + public UserData() { } + } + + public static void assertEqualsDelta(double expected, double actual, string message) + { + Assert.AreEqual(expected, actual, 0.0000001, message); + } + + protected LibraryTestBase() + { + Lua = new Lua(); + Lua.Register((Action)Assert.AreEqual, "assertEquals"); + Lua.Register((Action)Assert.IsTrue, "assertTrue"); + Lua.Register((Action)Assert.IsFalse, "assertFalse"); + Lua.Register((Action)assertEqualsDelta); + Lua.Register((Action)Assert.Fail, "fail"); + Lua.Register(typeof(UserData)); + } + + /// + /// Gets the current Lua instance. + /// + protected Lua Lua { get; private set; } + + /// + /// Runs a test that tests invalid arguments passed to a method. It will run the test + /// with all types except the given one. + /// + /// The valid argument type. + /// A format string for the code to test. + /// True to allow nil to be passed. + protected void RunInvalidTypeTests(LuaValueType validType, string format, bool allowNil = false) + { + foreach (var type in Enum.GetValues(typeof(LuaValueType)).Cast()) + { + if (type == validType || (type == LuaValueType.Nil && allowNil)) + continue; + + try + { + Lua.DoText(string.Format(format, GetValueForType(type))); + Assert.Fail("Expected ArgumentException to be thrown for type " + type); + } + catch (ArgumentException) { /* noop */ } + } + } + + /// + /// Gets a Lua expression that is of the given type. + /// + /// The type of expression. + /// A valid Lua expression of the given type. + static string GetValueForType(LuaValueType type) + { + switch (type) + { + case LuaValueType.Nil: + return "nil"; + case LuaValueType.String: + return "'foobar'"; + case LuaValueType.Bool: + return "true"; + case LuaValueType.Table: + return "{}"; + case LuaValueType.Function: + return "function() end"; + case LuaValueType.Number: + return "123"; + case LuaValueType.Thread: + return "coroutine.create(function() end)"; + case LuaValueType.UserData: + return "UserData()"; + default: + throw new NotImplementedException(); + } + } + } +} \ No newline at end of file diff --git a/Unit Tests/Net/Runtime/LuaLibraries/Math.cs b/Unit Tests/Net/Runtime/LuaLibraries/Math.cs new file mode 100644 index 0000000..ff682f4 --- /dev/null +++ b/Unit Tests/Net/Runtime/LuaLibraries/Math.cs @@ -0,0 +1,203 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ModMaker.Lua.Runtime; + +namespace UnitTests.Net.Runtime.LuaLibraries +{ + [TestClass] + public class Math : LibraryTestBase + { + [TestMethod] + public void General() + { + // Combine many of the methods together since they are just imported. + Lua.DoText(@" +assertEquals( 251245, math.abs(-251245), 'abs') +assertEqualsDelta(0.361416951927645, math.asin(0.3536), 'asin') +assertEqualsDelta(0.187568907875447, math.atan(0.1898), 'atan') +assertEqualsDelta(0.161967351986035, math.atan2(25, 153), 'atan2') +assertEquals( 2455, math.ceil(2454.5147), 'ceil') +assertEqualsDelta(0.658878051008508, math.cos(0.85147), 'cos') +assertEqualsDelta(1.38493787774095, math.cosh(0.85147), 'cosh') +assertEqualsDelta(48.7856373820042, math.deg(0.85147), 'deg') +assertEqualsDelta(69.6254387609039, math.exp(4.24313), 'exp') +assertEquals( 2454, math.floor(2454.5147), 'floor') +assertEqualsDelta(-1.195, math.fmod(24.54, 5.147), 'fmod') +assertEqualsDelta(70866960384, math.ldexp(528, 27), 'ldexp') +assertEqualsDelta(3.20030443928277, math.log(24.54), 'ln') +assertEqualsDelta(1.53902111462939, math.log(24.54, 8), 'log') +assertEqualsDelta(40872.6120526573, math.pow(3.678, 8.153), 'pow') +assertEqualsDelta(4.802797035638, math.rad(275.18), 'rad') +assertEqualsDelta(0.608415615200534, math.sin(2.48753), 'sin') +assertEqualsDelta(5.97420326157982, math.sinh(2.48753), 'sinh') +assertEqualsDelta(221.303904168002, math.sqrt(48975.418), 'sqrt') +assertEqualsDelta(-0.76663479914779, math.tan(2.48753), 'tan') +assertEqualsDelta(0.986278580120099, math.tanh(2.48753), 'tanh') +"); + } + + [TestMethod] + public void General_InvalidTypes() + { + // Combine many of the methods together since they are just imported. + var oneArgumentMethods = new[] { + "abs", "asin", "atan", "ceil", "cos", "cosh", "deg", "exp", + "frexp", "floor", "rad", "sin", "sinh", "sqrt", "tan", "tanh" + }; + var twoArgumentMethods = new[] { + "atan2", "fmod", "ldexp", "log", "pow" + }; + + foreach (var method in oneArgumentMethods) + { + RunInvalidTypeTests(LuaValueType.Number, "math." + method + "({0})"); + } + foreach (var method in twoArgumentMethods) + { + RunInvalidTypeTests(LuaValueType.Number, "math." + method + "({0}, 4)"); + RunInvalidTypeTests(LuaValueType.Number, "math." + method + "(73, {0})"); + } + } + + [TestMethod] + public void frexp() + { + Lua.DoText(@" +local a, b = math.frexp(245) +assertEqualsDelta(0.95703125, a, 'frexp: normal(1)') +assertEquals( 8, b, 'frexp: normal(2)') + +a, b = math.frexp(-24623) +assertEqualsDelta(-0.751434326171, a, 'frexp: negative(1)') +assertEquals( 15, b, 'frexp: negative(2)') +"); + } + + [TestMethod] + public void max() + { + Lua.DoText(@" +assertEquals(2672368, math.max(2, -566, 451, 2672368, 1), 'max: normal') +assertEquals(63, math.max(63, -566, -47, 0, -7), 'max: return is first argument') +assertEquals(8, math.max(8), 'max: one argument') +"); + } + + [TestMethod] + public void max_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "math.max(2, {0})"); + RunInvalidTypeTests(LuaValueType.Number, "math.max({0}, 4, 2)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void max_ZeroArgs() + { + Lua.DoText(@"math.max()"); + } + + [TestMethod] + public void min() + { + Lua.DoText(@" +assertEquals(-566, math.min(2, -566, 451, 2672368, 1), 'min: normal') +assertEquals(-63, math.min(-63, 566, 47, 0, -7), 'min: return is first argument') +assertEquals(8, math.min(8), 'min: one argument') +"); + } + + [TestMethod] + public void min_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "math.min(2, {0})"); + RunInvalidTypeTests(LuaValueType.Number, "math.min({0}, 4, 2)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void min_ZeroArgs() + { + Lua.DoText(@"math.min()"); + } + + [TestMethod] + public void modf() + { + Lua.DoText(@" +local a, b = math.modf(26825.2154672) +assertEquals(26825, a, 'modf: normal(1)') +assertEqualsDelta(0.2154672, b, 'modf: normal(2)') + +a, b = math.modf(-48675.287548) +assertEquals(-48675, a, 'modf: negative(1)') +assertEqualsDelta(-0.287548, b, 'modf: negative(2)') + +a, b = math.modf(8458) +assertEquals(8458, a, 'modf: integer(1)') +assertEqualsDelta(0, b, 'modf: integer(2)') + +a, b = math.modf(0.4856256) +assertEquals(0, a, 'modf: fraction(1)') +assertEqualsDelta(0.4856256, b, 'modf: fraction(2)') + +a, b = math.modf(0) +assertEquals(0, a, 'modf: zero(1)') +assertEqualsDelta(0, b, 'modf: zero(2)') +"); + } + + [TestMethod] + public void modf_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "math.modf({0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void modf_ZeroArgs() + { + Lua.DoText(@"math.modf()"); + } + + [TestMethod] + public void random() + { + // NOTE: Although the multiple runs will produce the same results, the actual value of + // the value is undefined; therefore we need to just check for 'randomness' and for + // consistency, not for specific values. + Lua.DoText(@" +math.randomseed(12345) +local a = math.random() +local b = math.random(45) +local c = math.random(24, 68) + +assertTrue(0 <= a and a <= 1, 'random: no-arg range') +assertTrue(1 <= b and b <= 45, 'random: one-arg range') +assertEquals(0, math.fmod(b, 1), 'random: one-arg is an integer') +assertTrue(24 <= c and c <= 68, 'random: two-arg range') +assertEquals(0, math.fmod(c, 1), 'random: two-arg is an integer') + +-- This is technically possible, but extremely unlikely. +math.randomseed(54321) +local x = math.random() +local y = math.random(45) +assertTrue(x ~= a and y ~= b, 'random: different seeds make different values') + +math.randomseed(12345) + +assertEquals(a, math.random(), 'random: zero-arg makes same values') +assertEquals(b, math.random(45), 'random: one-arg makes same values') +assertEquals(c, math.random(24, 68), 'random: two-arg makes same values') +"); + } + + [TestMethod] + public void randon_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "math.randomseed({0})"); + RunInvalidTypeTests(LuaValueType.Number, "math.random({0})", allowNil: true); + RunInvalidTypeTests(LuaValueType.Number, "math.random(12, {0})", allowNil: true); + } + } +} \ No newline at end of file diff --git a/Unit Tests/Net/Runtime/LuaLibraries/String.cs b/Unit Tests/Net/Runtime/LuaLibraries/String.cs new file mode 100644 index 0000000..0d521e5 --- /dev/null +++ b/Unit Tests/Net/Runtime/LuaLibraries/String.cs @@ -0,0 +1,446 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ModMaker.Lua.Runtime; + +namespace UnitTests.Net.Runtime.LuaLibraries +{ + [TestClass] + public class String : LibraryTestBase + { + #region byte + [TestMethod] + public void byte_() + { + Lua.DoText(@" +-- These use UTF-16 code points. +-- The escape sequences are done in C#, so they are passed as Unicode characters to Lua. +assertEquals(0x57, string.byte('CatWow', 4), 'byte: normal') +assertEquals(6, select('#', string.byte('Lorem Ipsum', 5, 10)), 'byte: returns multiple values') +assertEquals(0x20ac, string.byte('" + "\u20ac" + @"'), 'byte: handles UTF-16') + +local a, b = string.byte('" + "\ud801\udc37" + @"', 1, 2) +assertEquals(0xd801, a, 'byte: handles surrogate pairs(1)') +assertEquals(0xdc37, b, 'byte: handles surrogate pairs(2)') + +assertEquals(0x44, string.byte('ABCDEF', -3), 'byte: negative start') +assertEquals(3, select('#', string.byte('ABCDEF', 3, -2)), 'byte: negative end') +assertEquals(0, select('#', string.byte('ABCDEF', 10)), 'byte: start past end') +assertEquals(3, select('#', string.byte('ABCDEF', 4, 10)), 'byte: end past end') +"); + } + + [TestMethod] + public void byte_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.byte({0})"); + RunInvalidTypeTests(LuaValueType.Number, "string.byte('cat', {0})"); + RunInvalidTypeTests(LuaValueType.Number, "string.byte('cat', 3, {0})", allowNil: true); + } + #endregion + + #region char + [TestMethod] + public void char_() + { + Lua.DoText(@" +-- These use UTF-16 code points. +-- The escape sequences are done in C#, so they are passed as Unicode characters to Lua. +assertEquals('ABC', string.char(0x41, 0x42, 0x43), 'char: normal') +assertEquals('', string.char(), 'char: zero args') +assertEquals('" + "\uac20" + @"', string.char(0xac20), 'char: handles UTF-16') +assertEquals('" + "\ud801\udc37" + @"', string.char(0xd801, 0xdc37), 'char: handles surrotgate pairs split') +assertEquals('" + "\ud801\udc37" + @"', string.char(0x10437), 'char: handles high code points') +"); + } + + [TestMethod] + public void char_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Number, "string.char({0})"); + RunInvalidTypeTests(LuaValueType.Number, "string.char(3, 4, {0}, 1)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void char_BadArgument() + { + Lua.DoText(@"string.char(2, -1)"); + } + #endregion + + #region find + [TestMethod] + public void find() + { + Lua.DoText(@" +assertEquals(3, string.find('ABCABCABC', 'CA'), 'find: normal') +assertEquals(6, string.find('ABCABCABC', 'CA', 4), 'find: with start') +assertEquals(0, select('#', string.find('ABCABCABC', 'x')), 'find: not found') +assertEquals(0, select('#', string.find('aXabcd', 'X', 3)), 'find: not found, with start') + +local start, end, c1, c2 = string.find('xABcccDx', 'A(B)(.+)D') +assertEquals(2, start, 'find: returns start') +assertEquals(7, end, 'find: returns end') +assertEquals('B', c1, 'find: returns capture(1)') +assertEquals('ccc', c2, 'find: returns capture(2)') + +assertEquals(4, string.find('aXaXX+a', 'XX+', 1, true), 'find: plain') +"); + } + + [TestMethod] + public void find_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.find({0}, 'cat')"); + RunInvalidTypeTests(LuaValueType.String, "string.find('cat', {0})"); + RunInvalidTypeTests(LuaValueType.Number, "string.find('cat', 'cat', {0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void find_NotEnoughArgs() + { + Lua.DoText(@"string.find()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void find_NotEnoughArgs2() + { + Lua.DoText(@"string.find('foobar')"); + } + #endregion + + #region format + [TestMethod] + public void format() + { + Lua.DoText(@" +assertEquals('AB3CD', string.format('AB{0}CD', 3), 'format: normal') +assertEquals('ABCD', string.format('ABCD'), 'format: no format') +"); + } + + [TestMethod] + public void format_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.format({0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void format_NoArgs() + { + Lua.DoText(@"string.format()"); + } + #endregion + + #region gmatch + [TestMethod] + public void gmatch() + { + Lua.DoText(@" +local func = string.gmatch('hello world', '\\w+') +local x, y = func() +assertEquals('hello', x, 'gmatch: no captures (1)') +assertEquals(nil, y, 'gmatch: no captures (1e)') +assertEquals('world', func(), 'gmatch: no captures (2)') +assertEquals(0, select('#', func()), 'gmatch: no captures (3)') + +func = string.gmatch('key=value, foo=bar', '(\\w+)=(\\w+)') +x, y = func() +assertEquals('key', x, 'gmatch: captures (1a)') +assertEquals('value', y, 'gmatch: captures (1b)') +x, y = func() +assertEquals('foo', x, 'gmatch: captures (2a)') +assertEquals('bar', y, 'gmatch: captures (2b)') +assertEquals(0, select('#', func()), 'gmatch: captures (3)') +"); + } + + [TestMethod] + public void gmatch_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.gmatch({0}, 'cat')"); + RunInvalidTypeTests(LuaValueType.String, "string.gmatch('cat', {0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void gmatch_NotEnoughArgs() + { + Lua.DoText(@"string.gmatch()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void gmatch_NotEnoughArgs2() + { + Lua.DoText(@"string.gmatch('cat')"); + } + #endregion + + #region gsub + [TestMethod] + public void gsub() + { + Lua.DoText(@" +local function repl(s) + assertEquals('123', s, 'gsub: function arguments') + return 'abc' +end + +local t = {abc = 'code', xyz = 'lua'} +assertEquals('abab xyxy', string.gsub('ab xy', '\\w+', '%0%0'), 'gsub: normal') +assertEquals('abab xyxy 12', string.gsub('ab xy 12', '\\w+', '%0%0', 2), 'gsub: limit') +assertEquals('abc abc', string.gsub('123 abc', '(\\w+)', repl, 1), 'gsub: function') +assertEquals('makes lua code', string.gsub('makes #xyz #abc', '#(\\w+)', t), 'gsub: table') +"); + } + + [TestMethod] + public void gsub_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.gsub({0}, 'cat', '')"); + RunInvalidTypeTests(LuaValueType.String, "string.gsub('cat', {0}, '')"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void gsub_NotEnoughArgs() + { + Lua.DoText(@"string.gsub()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void gsub_NotEnoughArgs2() + { + Lua.DoText(@"string.gsub('cat')"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void gsub_NotEnoughArgs3() + { + Lua.DoText(@"string.gsub('cat', 'cat')"); + } + #endregion + + #region len + [TestMethod] + public void len() + { + Lua.DoText(@" +assertEquals(5, string.len('ab xy'), 'len: normal') +assertEquals(0, string.len(''), 'len: empty string') +assertEquals(5, string.len('a\000b\000c'), 'len: embedded nulls') +assertEquals(6, string.len('" + "a\u94ac\ud852xa\udf62" + @"'), 'len: Unicode') +"); + } + + [TestMethod] + public void len_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.len({0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void len_NotEnoughArgs() + { + Lua.DoText(@"string.len()"); + } + #endregion + + #region lower + [TestMethod] + public void lower() + { + Lua.DoText(@" +assertEquals('abcde', string.lower('aBCdE'), 'lower: normal') +assertEquals('', string.lower(''), 'lower: empty string') +assertEquals('τυφχψω', string.lower('ΤΥΦΧΨΩ'), 'lower: Unicode') +"); + } + + [TestMethod] + public void lower_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.lower({0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void lower_NotEnoughArgs() + { + Lua.DoText(@"string.lower()"); + } + #endregion + + #region match + [TestMethod] + public void match() + { + Lua.DoText(@" +local x, y = string.match('hello world', '\\w+') +assertEquals('hello', x, 'match: no captures (1)') +assertEquals(nil, y, 'match: no captures (2)') + +x, y = string.match('hello world', '(\\w+) (\\w+)') +assertEquals('hello', x, 'match: captures (1)') +assertEquals('world', y, 'match: captures (2)') + +x, y = string.match('hello world', '(\\w+)', 7) +assertEquals('world', x, 'match: with start (1)') +assertEquals(nil, y, 'match: with start (2)') +"); + } + + [TestMethod] + public void match_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.match({0}, 'cat')"); + RunInvalidTypeTests(LuaValueType.String, "string.match('cat', {0})"); + RunInvalidTypeTests(LuaValueType.Number, "string.match('cat', 'c', {0})", allowNil:true); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void match_NotEnoughArgs() + { + Lua.DoText(@"string.match()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void match_NotEnoughArgs2() + { + Lua.DoText(@"string.match('cat')"); + } + #endregion + + #region rep + [TestMethod] + public void rep() + { + Lua.DoText(@" +assertEquals('XaXaXaXa', string.rep('Xa', 4), 'rep: normal') +assertEquals('', string.rep('Xa', 0), 'rep: zero rep') +assertEquals('', string.rep('Xa', -3), 'rep: negative rep') +assertEquals('Xa,Xa,Xa', string.rep('Xa', 3, ','), 'rep: with sep') +"); + } + + [TestMethod] + public void rep_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.rep({0}, 8)"); + RunInvalidTypeTests(LuaValueType.Number, "string.rep('cat', {0})"); + RunInvalidTypeTests(LuaValueType.String, "string.rep('cat', 8, {0})", allowNil: true); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void rep_NotEnoughArgs() + { + Lua.DoText(@"string.rep()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void rep_NotEnoughArgs2() + { + Lua.DoText(@"string.rep('cat')"); + } + #endregion + + #region reverse + [TestMethod] + public void reverse() + { + Lua.DoText(@" +assertEquals('DCBA', string.reverse('ABCD'), 'reverse: normal') +assertEquals('', string.reverse(''), 'reverse: empty string') +assertEquals('" + "\u2678a\u4623" + @"', string.reverse('" + "\u4623a\u2678" + @"'), 'reverse: supports Unicode') +assertEquals('" + "\ud801\udc37a" + @"', string.reverse('" + "a\ud801\udc37" + @"'), 'reverse: supports UTF-16') +"); + } + + [TestMethod] + public void reverse_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.reverse({0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void reverse_NotEnoughArgs() + { + Lua.DoText(@"string.reverse()"); + } + #endregion + + #region sub + [TestMethod] + public void sub() + { + Lua.DoText(@" +assertEquals('CDE', string.sub('ABCDE', 3), 'sub: normal') +assertEquals('DE', string.sub('ABCDE', -2), 'sub: negative start') +assertEquals('BCD', string.sub('ABCDE', 2, 4), 'sub: with end') +assertEquals('B', string.sub('ABCDE', 2, 2), 'sub: start == end') +assertEquals('BCD', string.sub('ABCDE', 2, -2), 'sub: with negative end') +assertEquals('', string.sub('ABCDE', 4, 1), 'sub: start > end') +"); + } + + [TestMethod] + public void sub_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.sub({0}, 3)"); + RunInvalidTypeTests(LuaValueType.Number, "string.sub('cat', {0})"); + RunInvalidTypeTests(LuaValueType.Number, "string.sub('cat', 2, {0})", allowNil: true); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void sub_NotEnoughArgs() + { + Lua.DoText(@"string.sub()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void sub_NotEnoughArgs2() + { + Lua.DoText(@"string.sub('cat')"); + } + #endregion + + #region upper + [TestMethod] + public void upper() + { + Lua.DoText(@" +assertEquals('ABCDE', string.upper('aBCdE'), 'upper: normal') +assertEquals('', string.upper(''), 'upper: empty string') +assertEquals('ΤΥΦΧΨΩ', string.upper('τυφχψω'), 'upper: Unicode') +"); + } + + [TestMethod] + public void upper_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.String, "string.upper({0})"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void upper_NotEnoughArgs() + { + Lua.DoText(@"string.upper()"); + } + #endregion + } +} diff --git a/Unit Tests/Net/Runtime/LuaLibraries/Table.cs b/Unit Tests/Net/Runtime/LuaLibraries/Table.cs new file mode 100644 index 0000000..064c9a4 --- /dev/null +++ b/Unit Tests/Net/Runtime/LuaLibraries/Table.cs @@ -0,0 +1,254 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ModMaker.Lua; +using ModMaker.Lua.Runtime; +using System; + +namespace UnitTests.Net.Runtime.LuaLibraries +{ + [TestClass] + public class Table : LibraryTestBase + { + #region concat + [TestMethod] + public void concat() + { + Lua.DoText(@" +assertEquals('1c3', table.concat({1,'c',3}), 'concat: normal') +assertEquals('1c', table.concat({1,'c',nil,3}), 'concat: with nil') +assertEquals('1,c,3', table.concat({1,'c',3}, ','), 'concat: with sep') +assertEquals('c,3,4', table.concat({1,'c',3,4}, ',', 2), 'concat: with start') +assertEquals('c,3', table.concat({1,'c',3,4}, ',', 2, 3), 'concat: with start & end') +"); + } + + [TestMethod] + public void concat_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Table, "table.concat({0})"); + RunInvalidTypeTests(LuaValueType.String, "table.concat({{1,2,3}}, {0})", + allowNil: true); + RunInvalidTypeTests(LuaValueType.Number, "table.concat({{1,2,3}}, '1', {0})", + allowNil: true); + RunInvalidTypeTests(LuaValueType.Number, "table.concat({{1,2,3}}, 'cat', 2, {0})", + allowNil: true); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void concat_NotEnoughArgs() + { + Lua.DoText(@"table.concat()"); + } + #endregion + + #region insert + [TestMethod] + public void insert() + { + Lua.DoText(@" +local t = {1,2,3} +assertEquals(3, #t, 'insert: start length') + +table.insert(t, 4) +assertEquals(4, #t, 'insert: add length') +assertEquals(4, t[4], 'insert: add element') + +table.insert(t, 2, -1) +assertEquals(5, #t, 'insert: insert length') +assertEquals(1, t[1], 'insert: insert element(1)') +assertEquals(-1, t[2], 'insert: insert element(2)') +assertEquals(2, t[3], 'insert: insert element(3)') +"); + } + + [TestMethod] + public void insert_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Table, "table.insert({0}, 'c')"); + RunInvalidTypeTests(LuaValueType.Number, "table.insert({{1,2,3}}, {0}, 'c')"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void insert_InvalidArg() + { + Lua.DoText(@"table.insert({1,2}, -3, 'cat')"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void insert_InvalidArg2() + { + Lua.DoText(@"table.insert({1,2}, 10, 'cat')"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void insert_NotEnoughArgs() + { + Lua.DoText(@"table.insert()"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void insert_NotEnoughArgs2() + { + Lua.DoText(@"table.insert({1,2,3})"); + } + #endregion + + #region pack + [TestMethod] + public void pack() + { + Lua.DoText(@" +local t = table.pack(1, nil, 'cat') +assertEquals(3, t.n, 'pack: normal length') +assertEquals(1, t[1], 'pack: normal values(1)') +assertEquals(nil, t[2], 'pack: normal values(2)') +assertEquals('cat', t[3], 'pack: normal values(3)') + +t = table.pack() +assertEquals(0, #t, 'pack: empty length') +assertEquals(0, t.n, 'pack: empty n') +"); + } + #endregion + + #region remove + [TestMethod] + public void remove() + { + Lua.DoText(@" +local t = {1,2,3,4,5} +local x = table.remove(t) +assertEquals(4, #t, 'remove: end length') +assertEquals(nil, t[5], 'remove: end table') +assertEquals(5, x, 'remove: end return') + +x = table.remove(t, 2) +assertEquals(3, #t, 'remove: pos length') +assertEquals(nil, t[4], 'remove: pos table') +assertEquals(1, t[1], 'remove: pos table shifts(1)') +assertEquals(3, t[2], 'remove: pos table shifts(2)') +assertEquals(2, x, 'remove: pos return') +"); + } + + [TestMethod] + public void remove_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Table, "table.remove({0}, 0)"); + RunInvalidTypeTests(LuaValueType.Number, "table.remove({{0, 1}}, {0})", + allowNil: true); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void remove_InvalidArg() + { + Lua.DoText(@"table.remove({1,2}, -3)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void remove_InvalidArg2() + { + Lua.DoText(@"table.remove({1,2}, 10)"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void remove_NotEnoughArgs() + { + Lua.DoText(@"table.remove()"); + } + #endregion + + #region sort + [TestMethod] + public void sort() + { + Lua.DoText(@" +local t = {5,2,8,1,6} +table.sort(t) +assertEquals(1, t[1], 'sort: normal(1)') +assertEquals(2, t[2], 'sort: normal(2)') +assertEquals(5, t[3], 'sort: normal(3)') +assertEquals(6, t[4], 'sort: normal(4)') +assertEquals(8, t[5], 'sort: normal(5)') + +local function comp(a, b) + return b < a +end +table.sort(t, comp) +assertEquals(8, t[1], 'sort: comp(5)') +assertEquals(6, t[2], 'sort: comp(4)') +assertEquals(5, t[3], 'sort: comp(3)') +assertEquals(2, t[4], 'sort: comp(2)') +assertEquals(1, t[5], 'sort: comp(1)') +"); + } + + [TestMethod] + public void sort_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Table, "table.sort({0}, 0)"); + RunInvalidTypeTests(LuaValueType.Function, "table.sort({{0, 1}}, {0})", + allowNil: true); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void sort_NotEnoughArgs() + { + Lua.DoText(@"table.sort()"); + } + #endregion + + #region unpack + [TestMethod] + public void unpack() + { + Lua.DoText(@" +local t = {1,2,3} +local x, y, z = table.unpack(t) +assertEquals(1, x, 'unpack: values(1)') +assertEquals(2, y, 'unpack: values(2)') +assertEquals(3, z, 'unpack: values(3)') + +x, y, z = table.unpack(t, -1, 1) +assertEquals(nil, x, 'unpack: neg values(1)') +assertEquals(nil, y, 'unpack: neg values(2)') +assertEquals(1, z, 'unpack: neg values(3)') + +assertEquals(3, select('#', table.unpack(t)), 'unpack: normal') +assertEquals(2, select('#', table.unpack(t, 2)), 'unpack: start') +assertEquals(1, select('#', table.unpack(t, 2, 2)), 'unpack: start & end') +assertEquals(0, select('#', table.unpack(t, 2, 0)), 'unpack: start > end') +assertEquals(3, select('#', table.unpack(t, -2, 0)), 'unpack: start < 0') +assertEquals(4, select('#', table.unpack(t, -4, -1)), 'unpack: end < 0') +assertEquals(6, select('#', table.unpack(t, 1, 6)), 'unpack: end > #t') +assertEquals(2, select('#', table.unpack(t, 5, 6)), 'unpack: start > #t') +"); + } + + [TestMethod] + public void unpack_InvalidTypes() + { + RunInvalidTypeTests(LuaValueType.Table, "table.unpack({0})"); + RunInvalidTypeTests( + LuaValueType.Number, "table.unpack({{1,2,3}}, {0})", allowNil: true); + RunInvalidTypeTests( + LuaValueType.Number, "table.unpack({{1,2,3}}, 2, {0})", allowNil: true); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void unpack_NotEnoughArgs() + { + Lua.DoText(@"table.unpack()"); + } + #endregion + } +} \ No newline at end of file diff --git a/Unit Tests/Unit Tests.csproj b/Unit Tests/Unit Tests.csproj index f096633..89124a9 100644 --- a/Unit Tests/Unit Tests.csproj +++ b/Unit Tests/Unit Tests.csproj @@ -52,6 +52,11 @@ + + + + +