diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index a850e741..53e0c221 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 @@ -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() @@ -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: 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 diff --git a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj index df9b1c1a..c448c981 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. @@ -9,16 +9,16 @@ SIL.LCModel.Core provides a base library with core functionality. - + - + - + @@ -163,12 +163,12 @@ SIL.LCModel.Core provides a base library with core functionality. - + - + diff --git a/src/SIL.LCModel.Utils/FileUtils.cs b/src/SIL.LCModel.Utils/FileUtils.cs index 3a5ceba5..7b5f1b40 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)) { @@ -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,34 @@ 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 + _ = Path.GetFullPath(filename); + var name = Path.GetFileName(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; + 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 (Exception) + catch { return false; } @@ -936,7 +951,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 +990,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); diff --git a/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj b/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj index fa567fbb..59686716 100644 --- a/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj +++ b/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj @@ -10,11 +10,11 @@ SIL.LCModel.Utils provides utility classes. - + - + 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) 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); } diff --git a/src/SIL.LCModel/SIL.LCModel.csproj b/src/SIL.LCModel/SIL.LCModel.csproj index fafcfa45..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 c980c133..93be06eb 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. @@ -16,12 +16,13 @@ This package provides unit tests for SIL.LCModel.Core. - + + 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. /// 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 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/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")); } /// ------------------------------------------------------------------------------------- diff --git a/tests/SIL.LCModel.Tests/DomainServices/DataStoreInitializationServicesTests.cs b/tests/SIL.LCModel.Tests/DomainServices/DataStoreInitializationServicesTests.cs index 47e31455..9198f471 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 { @@ -317,7 +319,7 @@ 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); m_para.Stub(p => p.GetBT()).Do(getBtDelegate); 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..e3057a82 --- /dev/null +++ b/tests/SIL.LCModel.Tests/RhinoMocksToMoq/MockRepository.cs @@ -0,0 +1,25 @@ +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); + mock.DefaultValueProvider = DefaultValueProvider.Empty; + mock.SetupAllProperties(); + 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 0ef7b2bb..9353e879 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. @@ -12,9 +12,9 @@ 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..2d9aa817 100644 --- a/tests/TestHelper/TestHelper.csproj +++ b/tests/TestHelper/TestHelper.csproj @@ -1,11 +1,12 @@ - net461;netstandard2.0 + net461;net8.0 Icu.Tests TestHelper false Exe + false