From 7aac2ed48fff094a1a764c0f381d05aa2872a8a5 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 09:24:03 +0700 Subject: [PATCH 01/23] change test project target framework to net8.0 from netstandard2.0 --- tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj | 2 +- .../SIL.LCModel.FixData.Tests/SIL.LCModel.FixData.Tests.csproj | 2 +- tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj | 2 +- tests/SIL.LCModel.Utils.Tests/SIL.LCModel.Utils.Tests.csproj | 2 +- tests/TestHelper/TestHelper.csproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj b/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj index c980c133..493335a9 100644 --- a/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj +++ b/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj @@ -1,7 +1,7 @@ - net461;netstandard2.0 + net461;net8.0 SIL.LCModel.Core The liblcm library is the core FieldWorks model for linguistic analyses of languages. Tools in this library provide the ability to store and interact with language and culture data, including anthropological, text corpus, and linguistics data. This package provides unit tests for SIL.LCModel.Core. diff --git a/tests/SIL.LCModel.FixData.Tests/SIL.LCModel.FixData.Tests.csproj b/tests/SIL.LCModel.FixData.Tests/SIL.LCModel.FixData.Tests.csproj index b1cdf48c..98038ba9 100644 --- a/tests/SIL.LCModel.FixData.Tests/SIL.LCModel.FixData.Tests.csproj +++ b/tests/SIL.LCModel.FixData.Tests/SIL.LCModel.FixData.Tests.csproj @@ -1,7 +1,7 @@ - net461;netstandard2.0 + net461;net8.0 SIL.LCModel.FixData false diff --git a/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj b/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj index 0ef7b2bb..f7045720 100644 --- a/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj +++ b/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj @@ -1,7 +1,7 @@ - net461;netstandard2.0 + net461;net8.0 SIL.LCModel The liblcm library is the core FieldWorks model for linguistic analyses of languages. Tools in this library provide the ability to store and interact with language and culture data, including anthropological, text corpus, and linguistics data. This package provides unit tests for SIL.LCModel. diff --git a/tests/SIL.LCModel.Utils.Tests/SIL.LCModel.Utils.Tests.csproj b/tests/SIL.LCModel.Utils.Tests/SIL.LCModel.Utils.Tests.csproj index a675da34..ca1b2a25 100644 --- a/tests/SIL.LCModel.Utils.Tests/SIL.LCModel.Utils.Tests.csproj +++ b/tests/SIL.LCModel.Utils.Tests/SIL.LCModel.Utils.Tests.csproj @@ -1,7 +1,7 @@ - net461 + net461;net8.0 SIL.LCModel.Utils The liblcm library is the core FieldWorks model for linguistic analyses of languages. Tools in this library provide the ability to store and interact with language and culture data, including anthropological, text corpus, and linguistics data. This package provides unit tests for SIL.LCModel.Utils and test utility classes diff --git a/tests/TestHelper/TestHelper.csproj b/tests/TestHelper/TestHelper.csproj index 77b819bd..92dd601a 100644 --- a/tests/TestHelper/TestHelper.csproj +++ b/tests/TestHelper/TestHelper.csproj @@ -1,7 +1,7 @@ - net461;netstandard2.0 + net461;net8.0 Icu.Tests TestHelper false From 834526aca8e9aba7783c8fc902806af1cdd7022e Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 09:24:47 +0700 Subject: [PATCH 02/23] update System.Configuration.ConfigurationManager dependency to 6 to match a conflict with System.Diagnostics.PerformanceCounter --- src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj b/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj index fa567fbb..06eb44bb 100644 --- a/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj +++ b/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj @@ -14,7 +14,7 @@ SIL.LCModel.Utils provides utility classes. - + From deee6f43a096a3e3e275f39b3b27fc841272d121 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 09:26:04 +0700 Subject: [PATCH 03/23] reworked path validation to use Path.GetInvalidPathChars and Path.GetInvalidFileNameChars --- src/SIL.LCModel.Utils/FileUtils.cs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/SIL.LCModel.Utils/FileUtils.cs b/src/SIL.LCModel.Utils/FileUtils.cs index 3a5ceba5..c33e588c 100644 --- a/src/SIL.LCModel.Utils/FileUtils.cs +++ b/src/SIL.LCModel.Utils/FileUtils.cs @@ -473,16 +473,8 @@ public static string ActualFilePath(string sPathname) /// ------------------------------------------------------------------------------------ public static void AssertValidFilePath(string filename) { - try - { - new FileInfo(filename); - } - catch (SecurityException) - { - } - catch (UnauthorizedAccessException) - { - } + if (!IsFilePathValid(filename)) + throw new ArgumentException("Illegal characters in path."); } /// ------------------------------------------------------------------------------------ @@ -492,11 +484,19 @@ public static void AssertValidFilePath(string filename) /// ------------------------------------------------------------------------------------ public static bool IsFilePathValid(string filename) { + if (filename == null) + return false; try { - AssertValidFilePath(filename); + // will throw in .net framework, but not newer versions + var name = Path.GetFileName(filename); + if (name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + return false; + var directoryPath = filename.Substring(0, filename.Length - name.Length); + if (directoryPath.IndexOfAny(Path.GetInvalidPathChars()) >= 0) + return false; } - catch (Exception) + catch { return false; } @@ -936,7 +936,7 @@ public static string ChangeWindowsPathIfLinux(string windowsPath) public static string ChangeWindowsPathIfLinuxPreservingPrefix(string windowsPath, string prefix) { - if (windowsPath == null || prefix == null || !windowsPath.StartsWith(prefix)) + if (windowsPath == null || prefix == null || !windowsPath.StartsWith(prefix, StringComparison.Ordinal)) return ChangeWindowsPathIfLinux(windowsPath); // Preserve prefix windowsPath = windowsPath.Substring(prefix.Length); @@ -975,7 +975,7 @@ public static string ChangeLinuxPathIfWindows(string linuxPath) public static string ChangeLinuxPathIfWindowsPreservingPrefix(string linuxPath, string prefix) { - if (linuxPath == null || prefix == null || !linuxPath.StartsWith(prefix)) + if (linuxPath == null || prefix == null || !linuxPath.StartsWith(prefix, StringComparison.Ordinal)) return ChangeLinuxPathIfWindows(linuxPath); // Preserve prefix linuxPath = linuxPath.Substring(prefix.Length); From 52aa8a6238e2e26996b01df5e442f410a7b0e6bf Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 09:30:24 +0700 Subject: [PATCH 04/23] explicitly specify the hash algorithm to use when comparing files as dotnet does not define a default algorithm --- src/SIL.LCModel.Utils/FileUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SIL.LCModel.Utils/FileUtils.cs b/src/SIL.LCModel.Utils/FileUtils.cs index c33e588c..90d4028b 100644 --- a/src/SIL.LCModel.Utils/FileUtils.cs +++ b/src/SIL.LCModel.Utils/FileUtils.cs @@ -319,7 +319,7 @@ public static bool AreFilesIdentical(string file1, string file2) byte[] fileHash2; // compute the hashes for comparison - using(var hash = HashAlgorithm.Create()) + using(var hash = HashAlgorithm.Create("SHA1")) using(Stream fileStream1 = OpenStreamForRead(file1), fileStream2 = OpenStreamForRead(file2)) { From 8622d325c06477b07c74fae1ff7e95dcc23f2bbc Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 10:26:30 +0700 Subject: [PATCH 05/23] Core tests, add reference to TestHelper project so that it gets copied to the output directory --- tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj b/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj index 493335a9..87c36aa7 100644 --- a/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj +++ b/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj @@ -22,6 +22,7 @@ This package provides unit tests for SIL.LCModel.Core. + From 80d13970e68d8268bdb219db16b6fca56a853942 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 10:27:21 +0700 Subject: [PATCH 06/23] remove redundant `/` in output path. Ensure there's always a configuration set so the output path is consistent. --- Directory.Build.props | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 6d8c5c73..c2c63465 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,7 +11,8 @@ https://github.com/sillsdev/liblcm false Any CPU - $(MSBuildThisFileDirectory)/artifacts/$(Configuration)/$(TargetFramework) + Debug + $(MSBuildThisFileDirectory)artifacts/$(Configuration)/$(TargetFramework) false $(MSBuildThisFileDirectory)/artifacts true From 78b4dc649d073a5d829c03389e7c3f01abb1f970 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 10:28:28 +0700 Subject: [PATCH 07/23] add a net8.0 target framework to SIL.LCModel.Core.csproj, this ensures that IcuData will be copied to the output directory for dotnet 8 projects that depend on SIL.LCModel.Core --- src/SIL.LCModel.Core/SIL.LCModel.Core.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj index df9b1c1a..34a8321d 100644 --- a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj +++ b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj @@ -1,7 +1,7 @@  - netstandard2.0;net461 + netstandard2.0;net461;net8.0 SIL.LCModel.Core The liblcm library is the core FieldWorks model for linguistic analyses of languages. Tools in this library provide the ability to store and interact with language and culture data, including anthropological, text corpus, and linguistics data. SIL.LCModel.Core provides a base library with core functionality. @@ -163,12 +163,12 @@ SIL.LCModel.Core provides a base library with core functionality. - + - + From 2cbb4099c437ecfb3c26316472a92b2d1f2844b1 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 11:17:51 +0700 Subject: [PATCH 08/23] ensure all TestHelper.exe files are copied to the output directory, including Newtonsoft.Json.dll. Use TestContext.Out instead of Console.WriteLine. Fix assertion in CustomIcuFallbackTests.InitIcuDataDir_NoIcuLibrary due to how the error is different between .NET Framework and .NET Core. --- .../Text/CustomIcuFallbackTests.cs | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/tests/SIL.LCModel.Core.Tests/Text/CustomIcuFallbackTests.cs b/tests/SIL.LCModel.Core.Tests/Text/CustomIcuFallbackTests.cs index f29956e4..7e32b7ca 100644 --- a/tests/SIL.LCModel.Core.Tests/Text/CustomIcuFallbackTests.cs +++ b/tests/SIL.LCModel.Core.Tests/Text/CustomIcuFallbackTests.cs @@ -90,8 +90,8 @@ private string RunTestHelper(string workDir, out string stdErr, bool expectFailu if (process.ExitCode != 0) { var expected = expectFailure ? "expected" : "unexpected"; - Console.WriteLine($"TestHelper.exe failed ({expected}):"); - Console.WriteLine(stdErr); + TestContext.Out.WriteLine($"TestHelper.exe failed ({expected}):"); + TestContext.Out.WriteLine(stdErr); } return output.TrimEnd('\r', '\n'); } @@ -111,10 +111,10 @@ private static void CopyIcuFiles(string targetDir, string icuVersion) private static void CopyTestFiles(string sourceDir, string targetDir) { - var testHelper = Path.Combine(sourceDir, "TestHelper.exe"); - if (!File.Exists(testHelper)) - testHelper = Path.Combine(sourceDir, "TestHelper.dll"); - CopyFile(testHelper, targetDir); + foreach (var file in Directory.EnumerateFiles(sourceDir, "TestHelper.*")) + { + CopyFile(file, targetDir); + } var targetIcuDataDir = Path.Combine(targetDir, "IcuData"); Directory.CreateDirectory(targetIcuDataDir); DirectoryHelper.Copy(Path.Combine(sourceDir, "IcuData"), targetIcuDataDir); @@ -126,7 +126,8 @@ private static void CopyTestFiles(string sourceDir, string targetDir) "SIL.LCModel.Utils", "icu.net", "SIL.Core", - "Microsoft.Extensions.DependencyModel" + "Microsoft.Extensions.DependencyModel", + "Newtonsoft.Json" }) { var sourceFile = Path.Combine(sourceDir, $"{file}.dll"); @@ -222,21 +223,31 @@ private static void PrintIcuDllsOnPath() } catch (Exception e) { - Console.WriteLine($"Error enumerating: {e.GetType()}: {e.Message}"); + TestContext.Out.WriteLine($"Error enumerating: {e.GetType()}: {e.Message}"); } } if (files.Any()) { - Console.WriteLine($"Found the following ICU DLL's lurking around:\r\n{string.Join("\r\n", files)}"); - Console.WriteLine("(note: DLL's without a version number in the name should not be a problem)"); + TestContext.Out.WriteLine($"Found the following ICU DLL's lurking around:\r\n{string.Join("\r\n", files)}"); + TestContext.Out.WriteLine("(note: DLL's without a version number in the name should not be a problem)"); + } + else + { + TestContext.Out.WriteLine("No ICU DLL's found"); } } [Test] public void InitIcuDataDir_NoIcuLibrary() { - Assert.That(RunTestHelper(_tmpDir, out var stdErr, true), Is.Empty); - Assert.That(stdErr, Does.Contain("Unhandled Exception: System.IO.FileLoadException: Can't load ICU library (version 0)")); + var result = RunTestHelper(_tmpDir, out var stdErr, true); + if (!string.IsNullOrEmpty(result)) + { + PrintIcuDllsOnPath(); + } + + Assert.That(result, Is.Empty); + Assert.That(stdErr, Does.Contain("System.IO.FileLoadException: Can't load ICU library (version 0)")); } } } \ No newline at end of file From c03afbd4fbabb26d7720785b9bc4e24a69c66d99 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 11:39:33 +0700 Subject: [PATCH 09/23] replace RhinoMocks with Moq using RhinoMocksToMoq migration package. --- .../DomainServices/DataStoreInitializationServicesTests.cs | 4 +++- tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/SIL.LCModel.Tests/DomainServices/DataStoreInitializationServicesTests.cs b/tests/SIL.LCModel.Tests/DomainServices/DataStoreInitializationServicesTests.cs index 47e31455..bf6f85a8 100644 --- a/tests/SIL.LCModel.Tests/DomainServices/DataStoreInitializationServicesTests.cs +++ b/tests/SIL.LCModel.Tests/DomainServices/DataStoreInitializationServicesTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Moq; using NUnit.Framework; using Rhino.Mocks; using SIL.LCModel.Core.KernelInterfaces; @@ -12,6 +13,7 @@ using SIL.LCModel.Core.Text; using SIL.LCModel.DomainImpl; using SIL.LCModel.Utils; +using MockRepository = Rhino.Mocks.MockRepository; namespace SIL.LCModel.DomainServices { @@ -320,7 +322,7 @@ public override void TestSetup() GetBtDelegate getBtDelegate = () => m_para.TranslationsOC.FirstOrDefault(trans => trans.TypeRA != null && trans.TypeRA.Guid == CmPossibilityTags.kguidTranBackTranslation); - m_para.Stub(p => p.GetBT()).Do(getBtDelegate); + Mock.Get(m_para).Setup(p => p.GetBT()).Returns(getBtDelegate); } #endregion diff --git a/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj b/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj index f7045720..f2d09b43 100644 --- a/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj +++ b/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj @@ -14,7 +14,7 @@ This package provides unit tests for SIL.LCModel. - + From c02e0d1725411f27bd6f5a4ba77ecc5ef2c03fd6 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 11:54:26 +0700 Subject: [PATCH 10/23] use dotnet 8 in CI --- .github/workflows/ci-cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index a850e741..77212fc8 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -28,9 +28,9 @@ jobs: # Install the .NET Core workload - name: Install .NET Core - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x + dotnet-version: 8.0.x - name: Install mono-devel package run: sudo apt-get install mono-devel From d1437c44796f8d1cb5a816973f4630fd4667665d Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 13:25:22 +0700 Subject: [PATCH 11/23] remove RhinoMocksToMoq dependency and inline the code from the repo copying the LICENSE file, this avoids the strong name issues with the NuGet package. --- .../DataStoreInitializationServicesTests.cs | 4 +- .../RhinoMocksToMoq/Expect.cs | 78 +++++++++++++++++++ .../SIL.LCModel.Tests/RhinoMocksToMoq/LICENSE | 21 +++++ .../RhinoMocksToMoq/MockExtensions.cs | 39 ++++++++++ .../RhinoMocksToMoq/MockRepository.cs | 23 ++++++ .../RhinoMocksToMoq/MoqAdapter.cs | 61 +++++++++++++++ .../RhinoMocksToMoq/MoqExtensions.cs | 21 +++++ .../SIL.LCModel.Tests.csproj | 2 +- 8 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 tests/SIL.LCModel.Tests/RhinoMocksToMoq/Expect.cs create mode 100644 tests/SIL.LCModel.Tests/RhinoMocksToMoq/LICENSE create mode 100644 tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockExtensions.cs create mode 100644 tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockRepository.cs create mode 100644 tests/SIL.LCModel.Tests/RhinoMocksToMoq/MoqAdapter.cs create mode 100644 tests/SIL.LCModel.Tests/RhinoMocksToMoq/MoqExtensions.cs diff --git a/tests/SIL.LCModel.Tests/DomainServices/DataStoreInitializationServicesTests.cs b/tests/SIL.LCModel.Tests/DomainServices/DataStoreInitializationServicesTests.cs index bf6f85a8..9198f471 100644 --- a/tests/SIL.LCModel.Tests/DomainServices/DataStoreInitializationServicesTests.cs +++ b/tests/SIL.LCModel.Tests/DomainServices/DataStoreInitializationServicesTests.cs @@ -319,10 +319,10 @@ public override void TestSetup() m_para.Stub(p => p.Id).Return(paraId); m_para.Contents = TsStringUtils.EmptyString(Cache.DefaultVernWs); - GetBtDelegate getBtDelegate = () => + Func getBtDelegate = () => m_para.TranslationsOC.FirstOrDefault(trans => trans.TypeRA != null && trans.TypeRA.Guid == CmPossibilityTags.kguidTranBackTranslation); - Mock.Get(m_para).Setup(p => p.GetBT()).Returns(getBtDelegate); + m_para.Stub(p => p.GetBT()).Do(getBtDelegate); } #endregion diff --git a/tests/SIL.LCModel.Tests/RhinoMocksToMoq/Expect.cs b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/Expect.cs new file mode 100644 index 00000000..a8b8e5c3 --- /dev/null +++ b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/Expect.cs @@ -0,0 +1,78 @@ +using System; +using System.Linq.Expressions; +using Moq; + +namespace Rhino.Mocks +{ + public interface IExpect where T : class + { + void Throw(Exception exception); + } + + public interface IExpect where T : class + { + IExpect Do(Func action); + + IExpect Return(TR result); + + void ReturnInOrder(params TR[] results); + + void Throw(Exception exception); + } + + public class Expect : IExpect where T : class + { + private readonly MoqAdapter _moqAdapter; + + private bool _isResultAssigned; + + public Expect(Mock mock, Expression> expression) + { + _moqAdapter = new MoqAdapter(mock, expression); + } + + public IExpect Do(Func action) + { + _moqAdapter.Setup(action); + return this; + } + + public IExpect Return(TR result) + { + if (_isResultAssigned) + { + throw new InvalidOperationException("Return should be setup only once"); + } + + _isResultAssigned = true; + + _moqAdapter.Setup(result); + return this; + } + + public void Throw(Exception exception) + { + _moqAdapter.Throws(exception); + } + + public void ReturnInOrder(params TR[] results) + { + _moqAdapter.SetupReturnInOrder(results); + } + } + + public class Expect : IExpect where T : class + { + private readonly MoqAdapter _mockAdapter; + + public Expect(Mock mock, Expression> expression) + { + _mockAdapter = new MoqAdapter(mock, expression); + } + + public void Throw(Exception exception) + { + _mockAdapter.Throws(exception); + } + } +} \ No newline at end of file diff --git a/tests/SIL.LCModel.Tests/RhinoMocksToMoq/LICENSE b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/LICENSE new file mode 100644 index 00000000..a4e99b46 --- /dev/null +++ b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Minh Nguyen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockExtensions.cs b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockExtensions.cs new file mode 100644 index 00000000..a5a1ff8b --- /dev/null +++ b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockExtensions.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq.Expressions; +using Moq; + +namespace Rhino.Mocks +{ + public static class MockExtensions + { + public static IExpect Expect(this T obj, Expression> expression) where T : class + { + return Stub(obj, expression); + } + + public static IExpect Expect(this T obj, Expression> expression) where T : class + { + return Stub(obj, expression); + } + + public static IExpect Stub(this T obj, Expression> expression) where T : class + { + return new Expect(Mock.Get(obj), expression); + } + + public static IExpect Stub(this T obj, Expression> expression) where T : class + { + return new Expect(Mock.Get(obj), expression); + } + + public static void AssertWasNotCalled(this T obj, Expression> expression) where T : class + { + Mock.Get(obj).Verify(expression, Times.Never); + } + + public static void VerifyAllExpectations(this T obj) where T : class + { + Mock.Get(obj).Verify(); + } + } +} \ No newline at end of file diff --git a/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockRepository.cs b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockRepository.cs new file mode 100644 index 00000000..cc7d424a --- /dev/null +++ b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockRepository.cs @@ -0,0 +1,23 @@ +using Moq; + +namespace Rhino.Mocks +{ + public static class MockRepository + { + public static T GenerateStub() where T : class + { + return GenerateMock(); + } + + public static T GenerateStrictMock() where T : class + { + return GenerateMock(MockBehavior.Strict); + } + + public static T GenerateMock(MockBehavior behavior = MockBehavior.Default) where T : class + { + var mock = new Mock(behavior); + return mock.Object; + } + } +} \ No newline at end of file diff --git a/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MoqAdapter.cs b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MoqAdapter.cs new file mode 100644 index 00000000..9e432e8c --- /dev/null +++ b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MoqAdapter.cs @@ -0,0 +1,61 @@ +using System; +using System.Linq.Expressions; +using Moq; + +namespace Rhino.Mocks +{ + class MoqAdapter where T : class + { + private readonly Mock _mock; + private readonly Expression> _expression; + + public MoqAdapter(Mock mock, Expression> expression) + { + _mock = mock; + _expression = expression; + } + + public void Setup(TR result) + { + if (result != null) + { + _mock.Setup(_expression).Returns(result); + } + } + + public void Setup(Func result) + { + if (result != null) + { + _mock.Setup(_expression).Returns(result); + } + } + + public void SetupReturnInOrder(params TR[] results) + { + _mock.Setup(_expression).ReturnsInOrder(results); + } + + public void Throws(Exception exception) + { + _mock.Setup(_expression).Throws(exception); + } + } + + class MoqAdapter where T : class + { + private readonly Mock _mock; + private readonly Expression> _expression; + + public MoqAdapter(Mock mock, Expression> expression) + { + _mock = mock; + _expression = expression; + } + + public void Throws(Exception exception) + { + _mock.Setup(_expression).Throws(exception); + } + } +} \ No newline at end of file diff --git a/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MoqExtensions.cs b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MoqExtensions.cs new file mode 100644 index 00000000..a8f10899 --- /dev/null +++ b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MoqExtensions.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Moq.Language.Flow; + +namespace Rhino.Mocks +{ + public static class MoqExtensions + { + public static IReturnsResult ReturnsInOrder(this ISetup setup, params Func[] valueFunctions) where TMock : class + { + var functionQueue = new Queue>(valueFunctions); + return setup.Returns(() => functionQueue.Dequeue()()); + } + + public static void ReturnsInOrder(this ISetup setup, TResult[] results) where TMock : class + { + ReturnsInOrder(setup, results.Select(result => new Func(() => result)).ToArray()); + } + } +} \ No newline at end of file diff --git a/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj b/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj index f2d09b43..9353e879 100644 --- a/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj +++ b/tests/SIL.LCModel.Tests/SIL.LCModel.Tests.csproj @@ -12,9 +12,9 @@ This package provides unit tests for SIL.LCModel. + - From c32fe13be3dee54f7fb68b27023f5b4006a8d739 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 13:56:37 +0700 Subject: [PATCH 12/23] change FindOrCreateFile to use the path validation helper as dotnet 8 tests were failing because '<' is valid in a path but not a file name --- src/SIL.LCModel/DomainServices/DomainObjectServices.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/SIL.LCModel/DomainServices/DomainObjectServices.cs b/src/SIL.LCModel/DomainServices/DomainObjectServices.cs index 09319a1f..2564e4bc 100644 --- a/src/SIL.LCModel/DomainServices/DomainObjectServices.cs +++ b/src/SIL.LCModel/DomainServices/DomainObjectServices.cs @@ -2298,9 +2298,7 @@ public static ICmFile FindOrCreateFile(ICmFolder folder, string srcFile) if (String.IsNullOrEmpty(srcFile)) throw new ArgumentException("File path not specified.", "srcFile"); - char[] bad = Path.GetInvalidPathChars(); - int idx = srcFile.IndexOfAny(bad); - if (idx >= 0) + if (!FileUtils.IsFilePathValid(srcFile)) throw new ArgumentException("File path (" + srcFile + ") contains at least one invalid character.", "srcFile"); foreach (ICmFile file in folder.FilesOC) From 63b284133719e6a0afb4285186b210bcaf1b7626 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 13:57:14 +0700 Subject: [PATCH 13/23] make all mocks support normal properties to match RhinoMocks --- tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockRepository.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockRepository.cs b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockRepository.cs index cc7d424a..e3057a82 100644 --- a/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockRepository.cs +++ b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockRepository.cs @@ -17,6 +17,8 @@ public static T GenerateStrictMock() where T : class public static T GenerateMock(MockBehavior behavior = MockBehavior.Default) where T : class { var mock = new Mock(behavior); + mock.DefaultValueProvider = DefaultValueProvider.Empty; + mock.SetupAllProperties(); return mock.Object; } } From 04bd789e5579430d8e054962059b683eea9d15be Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 13:57:34 +0700 Subject: [PATCH 14/23] make argument exception message matching less strict --- tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs b/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs index 6320aac3..0cd353f8 100644 --- a/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs +++ b/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs @@ -252,7 +252,7 @@ public void CmPictureConstructor_FromTextRep_MissingCmPictureToken() public void CmPictureConstructor_FromTextRep_MissingFilename() { Assert.That(() => m_pictureFactory.Create("CmPicture||||||This is a caption||", - CmFolderTags.LocalPictures), Throws.ArgumentException.With.Property("Message").Matches("File path not specified.(\\r)?\\nParameter( )?name: srcFile")); + CmFolderTags.LocalPictures), Throws.ArgumentException.With.Message.Contains("File path not specified")); } /// ------------------------------------------------------------------------------------- From 8813c1c3f4aaabfc7f4aac4b495b3a210b18dfb4 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 14:50:47 +0700 Subject: [PATCH 15/23] add special case for paths with : in the folder name --- src/SIL.LCModel.Utils/FileUtils.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/SIL.LCModel.Utils/FileUtils.cs b/src/SIL.LCModel.Utils/FileUtils.cs index 90d4028b..f22dbb4e 100644 --- a/src/SIL.LCModel.Utils/FileUtils.cs +++ b/src/SIL.LCModel.Utils/FileUtils.cs @@ -489,12 +489,26 @@ public static bool IsFilePathValid(string filename) try { // will throw in .net framework, but not newer versions + _ = Path.GetFullPath(filename); var name = Path.GetFileName(filename); - if (name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + var invalidFileNameChars = Path.GetInvalidFileNameChars(); + if (name.IndexOfAny(invalidFileNameChars) >= 0) return false; var directoryPath = filename.Substring(0, filename.Length - name.Length); if (directoryPath.IndexOfAny(Path.GetInvalidPathChars()) >= 0) return false; + if (Platform.IsWindows) + { + // some paths like "C:\bla" are valid, but not C\:bla, we want to catch those by excluding the drive letter and checking for invalid file name chars in each directory + + //trim off the drive letter if it exists + directoryPath = directoryPath.Substring(Path.GetPathRoot(directoryPath).Length); + //each directory must be a valid file name. Using both / and \ because it could be a mixed path which usually works fine + if (directoryPath.Split('\\', '/').Any(dir => dir.IndexOfAny(invalidFileNameChars) >= 0)) + { + return false; + } + } } catch { From a114e3fdd3f4ac0e2e08a7391e772c38cdab8a15 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 30 Jul 2024 16:40:52 +0700 Subject: [PATCH 16/23] fix an edge case in FileUtils.IsFilePathValid where there's no directory --- src/SIL.LCModel.Utils/FileUtils.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SIL.LCModel.Utils/FileUtils.cs b/src/SIL.LCModel.Utils/FileUtils.cs index f22dbb4e..7b5f1b40 100644 --- a/src/SIL.LCModel.Utils/FileUtils.cs +++ b/src/SIL.LCModel.Utils/FileUtils.cs @@ -494,6 +494,7 @@ public static bool IsFilePathValid(string filename) var invalidFileNameChars = Path.GetInvalidFileNameChars(); if (name.IndexOfAny(invalidFileNameChars) >= 0) return false; + if (name == filename) return true; var directoryPath = filename.Substring(0, filename.Length - name.Length); if (directoryPath.IndexOfAny(Path.GetInvalidPathChars()) >= 0) return false; From 49a13a143703f24db846994b619a443deb7e3b9b Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 31 Jul 2024 13:34:32 +0700 Subject: [PATCH 17/23] ensure test results are disambiguated by framework version --- .github/workflows/ci-cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 77212fc8..92a0efa9 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -67,11 +67,11 @@ jobs: - name: Test on Linux run: | . environ - dotnet test --no-restore --no-build -p:ParallelizeAssembly=false --configuration Release --logger:"trx" --results-directory ./test-results + dotnet test --no-restore --no-build -p:ParallelizeAssembly=false --configuration Release --logger:"trx;LogFilePrefix=results" --results-directory ./test-results if: matrix.os == 'ubuntu-latest' - name: Test on Windows - run: dotnet test --no-restore --no-build -p:ParallelizeAssembly=false --configuration Release --logger:"trx" --results-directory ./test-results + run: dotnet test --no-restore --no-build -p:ParallelizeAssembly=false --configuration Release --logger:"trx;LogFilePrefix=results" --results-directory ./test-results if: matrix.os != 'ubuntu-latest' - name: Upload test results if: always() From 5d279deee35e7699b4b01d862f8c7c9621813765 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Thu, 15 Aug 2024 15:59:58 +0700 Subject: [PATCH 18/23] upgrade to SIL.WritingSystems 14.2.0 beta 21 to fix issue with CollatorSort_DoesNotThrow failing on dotnet 8 --- src/SIL.LCModel.Core/SIL.LCModel.Core.csproj | 6 +++--- src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj | 2 +- src/SIL.LCModel/SIL.LCModel.csproj | 4 ++-- tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj index 34a8321d..f658a157 100644 --- a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj +++ b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj @@ -9,16 +9,16 @@ SIL.LCModel.Core provides a base library with core functionality. - + - + - + diff --git a/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj b/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj index 06eb44bb..fa7a22e6 100644 --- a/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj +++ b/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj @@ -10,7 +10,7 @@ SIL.LCModel.Utils provides utility classes. - + diff --git a/src/SIL.LCModel/SIL.LCModel.csproj b/src/SIL.LCModel/SIL.LCModel.csproj index fafcfa45..ed40366b 100644 --- a/src/SIL.LCModel/SIL.LCModel.csproj +++ b/src/SIL.LCModel/SIL.LCModel.csproj @@ -22,9 +22,9 @@ - + - + diff --git a/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj b/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj index 87c36aa7..c8cf5f4c 100644 --- a/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj +++ b/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj @@ -16,7 +16,7 @@ This package provides unit tests for SIL.LCModel.Core. - + From 63c18ee922fcb6d6e51c531c9265c369328992bd Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Thu, 15 Aug 2024 16:20:16 +0700 Subject: [PATCH 19/23] fix test failures where `rootOfPath` was unexpectedly null --- src/SIL.LCModel/LinkedFilesRelativePathHelper.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/SIL.LCModel/LinkedFilesRelativePathHelper.cs b/src/SIL.LCModel/LinkedFilesRelativePathHelper.cs index db997587..92681132 100644 --- a/src/SIL.LCModel/LinkedFilesRelativePathHelper.cs +++ b/src/SIL.LCModel/LinkedFilesRelativePathHelper.cs @@ -274,6 +274,8 @@ private static string GetPathWithLowercaseRoot(string path) try { var rootOfPath = Path.GetPathRoot(path); + // dotnet 8 on linux, when path contains invalid characters rootOfPath will be null + if (rootOfPath is null) return path.ToLowerInvariant(); return rootOfPath.ToLowerInvariant() + path.Substring(rootOfPath.Length, path.Length - rootOfPath.Length); } From f4232a846d90564fff0e42d4df9cca7fbfbcc53b Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Thu, 15 Aug 2024 16:27:51 +0700 Subject: [PATCH 20/23] disable spelling tests when running dotnet 8 on linux --- .../SpellChecking/SpellingHelperTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/SIL.LCModel.Core.Tests/SpellChecking/SpellingHelperTests.cs b/tests/SIL.LCModel.Core.Tests/SpellChecking/SpellingHelperTests.cs index a3cec6b9..296d7d49 100644 --- a/tests/SIL.LCModel.Core.Tests/SpellChecking/SpellingHelperTests.cs +++ b/tests/SIL.LCModel.Core.Tests/SpellChecking/SpellingHelperTests.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using NUnit.Framework; using SIL.IO; @@ -22,6 +23,17 @@ public class SpellingHelperTests { // TODO-Linux: need slightly modified hunspell package installed! + [OneTimeSetUp] + public void FixtureSetUp() + { + #if NET8_0 + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Ignore("NHunspell does not work on dotnet 8 on linux"); + } + #endif + } + /// /// Check how spelling status is set and cleared. /// From 36685bd550747e48295d7afcedd8ec0c4492208f Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Fri, 16 Aug 2024 09:39:50 +0700 Subject: [PATCH 21/23] revert back to using wildcards for SIL package references --- src/SIL.LCModel.Core/SIL.LCModel.Core.csproj | 6 +++--- src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj | 2 +- src/SIL.LCModel/SIL.LCModel.csproj | 4 ++-- tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj index f658a157..c448c981 100644 --- a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj +++ b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj @@ -9,16 +9,16 @@ SIL.LCModel.Core provides a base library with core functionality. - + - + - + diff --git a/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj b/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj index fa7a22e6..59686716 100644 --- a/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj +++ b/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj @@ -10,7 +10,7 @@ SIL.LCModel.Utils provides utility classes. - + diff --git a/src/SIL.LCModel/SIL.LCModel.csproj b/src/SIL.LCModel/SIL.LCModel.csproj index ed40366b..3e74509d 100644 --- a/src/SIL.LCModel/SIL.LCModel.csproj +++ b/src/SIL.LCModel/SIL.LCModel.csproj @@ -22,9 +22,9 @@ - + - + diff --git a/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj b/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj index c8cf5f4c..93be06eb 100644 --- a/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj +++ b/tests/SIL.LCModel.Core.Tests/SIL.LCModel.Core.Tests.csproj @@ -16,7 +16,7 @@ This package provides unit tests for SIL.LCModel.Core. - + From 7a67c5ab213852dec2065b35b803873d23b9e836 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Fri, 16 Aug 2024 09:40:43 +0700 Subject: [PATCH 22/23] fix build issue in Rider due to TestHelper not being where GitVersion expects it to be, just disabled GitVersion for TestHelper since it's not published. --- tests/TestHelper/TestHelper.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/TestHelper/TestHelper.csproj b/tests/TestHelper/TestHelper.csproj index 92dd601a..2d9aa817 100644 --- a/tests/TestHelper/TestHelper.csproj +++ b/tests/TestHelper/TestHelper.csproj @@ -6,6 +6,7 @@ TestHelper false Exe + false From a08ee4ae2557d8d845456a27fdfd07cd55f2974e Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 27 Aug 2024 10:27:24 +0700 Subject: [PATCH 23/23] make artifact name unique per OS --- .github/workflows/ci-cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 92a0efa9..53e0c221 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -94,7 +94,7 @@ jobs: - name: Publish Artifacts uses: actions/upload-artifact@v4 with: - name: NugetPackages + name: NugetPackages (${{ matrix.os }}) path: artifacts/*.nupkg if: github.event_name == 'pull_request' publish-test-results: