Skip to content

Commit

Permalink
Merge branch 'master' into improve-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tombogle committed Nov 14, 2024
2 parents 1642a26 + 1c95083 commit dba4714
Show file tree
Hide file tree
Showing 23 changed files with 272 additions and 82 deletions.
56 changes: 42 additions & 14 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,21 @@ on:
pull_request:
workflow_dispatch:

defaults:
run:
shell: bash

jobs:
build:
timeout-minutes: 60
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
framework: [net462, net48, net8.0]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.framework }}
cancel-in-progress: true

steps:
- name: Checkout code
Expand All @@ -19,28 +30,45 @@ jobs:
fetch-depth: '0'

- name: Build project
run: dotnet build -bl:build.binlog --configuration Release
run: dotnet build -bl:build.binlog -c Release

- name: Get Path to Tests
run: echo "TEST_PATH=$(dotnet msbuild SIL.Core.Tests/ --getProperty:OutputPath -p:TargetFramework=${{ matrix.framework }} -p:Configuration=Release)" >> $GITHUB_ENV

# there are cases where this will fail and we want to know about it
# so we don't use continue-on-error, but we still want to publish the results
- name: Test project
id: test
run: dotnet test -bl:test.binlog --filter "TestCategory != SkipOnTeamCity" --blame-hang-timeout 5m --logger:"trx;LogFilePrefix=results" --results-directory ./test-results
run: dotnet test "$TEST_PATH"/SIL*Tests.dll --filter "TestCategory != SkipOnTeamCity" --blame-hang-timeout 5m --logger:"trx;LogFilePrefix=results" --results-directory ./test-results

- name: Publish test results
if: ${{ !cancelled() && steps.test.outcome != 'skipped' }}
uses: EnricoMi/publish-unit-test-result-action/windows@v2
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
check_name: LibPalaso Tests
files: ./test-results/**/*.trx
action_fail: true
action_fail_on_inconclusive: true
name: Test results (${{ matrix.framework }})
path: ./test-results

- name: Publish logs on failure
if: failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: binary-logs
path: |
build.binlog
test.binlog
name: binary-logs (${{ matrix.framework }})
path: build.binlog

publish-test-results:
runs-on: ubuntu-latest
needs: build
if: always()
steps:
- name: Download test results
uses: actions/download-artifact@v4
with:
path: artifacts
pattern: Test results *
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@8885e273a4343cd7b48eaa72428dea0c3067ea98 # v2.14.0
with:
check_name: Palaso Tests
files: artifacts/**/*.trx
action_fail: true
action_fail_on_inconclusive: true
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]

### Added
- [SIL.Core] Added optional parameter, preserveNamespaces, to XmlUtils.WriteNode
- [SIL.Core] Added optional parameter, includeSystemLibraries, to AcknowledgementsProvider.CollectAcknowledgements
- [SIL.Windows.Forms] Added ability to select which SIL logo(s) to use in SILAboutBox.
- [SIL.Windows.Forms] Added public enum Widgets.SilLogoVariant
Expand Down Expand Up @@ -75,10 +76,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- [SIL.Archiving] Made MetaTranscript.WriteCorpusImdiFile asynchronous, changing its signature to return Task<bool>.
- [SIL.Archiving] Changed the name of the third parameter in ArchivingDlgViewModel.AddFileGroup from progressMessage to addingToArchiveProgressMessage.
- [SIL.Windows.Forms.Archiving] Changed Cancel Button to say Close instead in IMDIArchivingDlg.
- [SIL.Core.Desktop] Renamed GetFromRegistryProgramThatOpensFileType to GetDefaultProgramForFileType.

### Fixed
- [SIL.Archiving] Fixed typo in RampArchivingDlgViewModel for Ethnomusicology performance collection.
- [SIL.Archiving] Changed URLs that used http: to https: in resource EmptyMets.xml.
- [SIL.Core.Desktop] Implemented GetDefaultProgramForFileType (as trenamed) in a way that works on Windows 11, Mono (probably) and MacOS (untested).

### Removed

Expand Down
1 change: 1 addition & 0 deletions SIL.Archiving.Tests/SIL.Archiving.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<TargetFrameworks>$(TargetFrameworks);net8.0</TargetFrameworks>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion SIL.Archiving/ArchivingPrograms.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static string GetRampExeFileLocation()
if (ArchivingDlgViewModel.IsMono)
exeFile = FileLocationUtilities.LocateInProgramFiles("ramp", true);
else
exeFile = FileLocator.GetFromRegistryProgramThatOpensFileType(rampFileExtension) ??
exeFile = FileLocator.GetDefaultProgramForFileType(rampFileExtension) ??
FileLocationUtilities.LocateInProgramFiles("ramp.exe", true, "ramp");

// make sure the file exists
Expand Down
18 changes: 6 additions & 12 deletions SIL.Core.Desktop.Tests/IO/FileLocatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,21 @@ public void LocateFile_FileNoteFound_ReturnsEmptyString()
}

[Test]
[Platform(Exclude="Unix")]
[Category("KnownMonoIssue")]
public void GetFromRegistryProgramThatOpensFileType_SendInvalidType_ReturnsNull()
public void GetDefaultProgramForFileType_SendInvalidType_ReturnsNull()
{
Assert.IsNull(FileLocator.GetFromRegistryProgramThatOpensFileType(".blah"));
Assert.IsNull(FileLocator.GetDefaultProgramForFileType(".blah"));
}

[Test]
[Platform(Exclude="Unix")]
[Category("KnownMonoIssue")]
public void GetFromRegistryProgramThatOpensFileType_SendValidType_ReturnsProgramPath()
public void GetDefaultProgramForFileType_SendValidType_ReturnsProgramPath()
{
Assert.IsNotNull(FileLocator.GetFromRegistryProgramThatOpensFileType(".txt"));
Assert.IsNotNull(FileLocator.GetDefaultProgramForFileType(".txt"));
}

[Test]
[Platform(Exclude="Unix")]
[Category("KnownMonoIssue")]
public void GetFromRegistryProgramThatOpensFileType_SendExtensionWithoutPeriod_ReturnsProgramPath()
public void GetDefaultProgramForFileType_SendExtensionWithoutPeriod_ReturnsProgramPath()
{
Assert.IsNotNull(FileLocator.GetFromRegistryProgramThatOpensFileType("txt"));
Assert.IsNotNull(FileLocator.GetDefaultProgramForFileType("txt"));
}
}
}
1 change: 1 addition & 0 deletions SIL.Core.Desktop.Tests/SIL.Core.Desktop.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<Description>Unit tests for SIL.Core.Desktop</Description>
<IsPackable>false</IsPackable>
<TargetFrameworks>$(TargetFrameworks);net8.0</TargetFrameworks>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
Expand Down
159 changes: 113 additions & 46 deletions SIL.Core.Desktop/IO/FileLocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using JetBrains.Annotations;
using Microsoft.Win32;
using SIL.PlatformUtilities;
using SIL.Reporting;

Expand Down Expand Up @@ -150,69 +152,134 @@ public virtual IFileLocator CloneAndCustomize(IEnumerable<string> addedSearchPat
return new FileLocator(new List<string>(SearchPaths.Concat(addedSearchPaths)));
}

#region Methods for locating file in program files folders
#region Methods for locating program file associated with a file
/// ------------------------------------------------------------------------------------
/// <summary>
/// Searches the registry and returns the full path to the application program used to
/// open files having the specified extension. The fileExtension can be with or without
/// the preceding period. If the command cannot be found in the registry, then null is
/// returned. If a command in the registry is found, but it refers to a program file
/// that does not exist, null is returned.
/// returns the full path to the application program used to open files having the
/// specified extension/type. The fileExtension can be with or without
/// the preceding period. If no associated application can be found or the associated
/// program does not actually exist, null is returned.
/// </summary>
/// ------------------------------------------------------------------------------------
public static string GetFromRegistryProgramThatOpensFileType(string fileExtension)
public static string GetDefaultProgramForFileType(string fileExtension)
{
if (!Platform.IsWindows)
{
//------------------------------------------------------------------------------------
// The following command will output the mime type of an existing file, Phil.html:
// file -b --mime-type ~/Phil.html
//
// This command will tell you the default application to open the file Phil.html:
// ext=$(grep "$(file -b --mime-type ~/Phil.html)" /etc/mime.types
// | awk '{print $1}') && xdg-mime query default $ext
//
// This command will open the file Phil.html using the default application:
// xdg-open ~/Page.html
//------------------------------------------------------------------------------------

throw new NotImplementedException(
"GetFromRegistryProgramThatOpensFileType not implemented on Mono yet.");
}
if (!fileExtension.StartsWith("."))
fileExtension = "." + fileExtension;

var ext = fileExtension.Trim();
if (!ext.StartsWith("."))
ext = "." + ext;
if (Platform.IsWindows)
return GetDefaultWindowsProgramForFileType(fileExtension);

var key = Registry.ClassesRoot.OpenSubKey(ext);
if (key == null)
return null;
if (Platform.IsMac)
return GetDefaultMacProgramForFileType(fileExtension);

if (Platform.IsLinux)
return GetDefaultLinuxProgramForFileType(fileExtension);

throw new PlatformNotSupportedException("This operating system is not supported.");
}

[DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern uint AssocQueryString(
uint flags,
int str,
string pszAssoc,
string pszExtra,
[Out] StringBuilder pszOut,
ref uint pcchOut);

private static string GetDefaultWindowsProgramForFileType(string fileExtension)
{
const int assocStrExecutable = 2;
uint length = 260;
var sb = new StringBuilder((int)length);

var value = key.GetValue(string.Empty) as string;
key.Dispose();
var result = AssocQueryString(0, assocStrExecutable, fileExtension, null, sb, ref length);

if (value == null)
if (result != 0 || sb.Length == 0)
return null;

key = Registry.ClassesRoot.OpenSubKey($"{value}\\shell\\open\\command");
var path = sb.ToString();
return Path.GetFileName(path) != "OpenWith.exe" && File.Exists(path) ? path : null;
}

if (key == null && value.ToLower() == "ramp.package")
private static string GetDefaultMacProgramForFileType(string fileExtension)
{
try
{
key = Registry.ClassesRoot.OpenSubKey("ramp\\shell\\open\\command");
if (key == null)
return null;
}
string filePath = $"/tmp/dummy{fileExtension}";
Process.Start("touch", filePath)?.WaitForExit();

value = key?.GetValue(string.Empty) as string;
key?.Dispose();
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "open",
Arguments = "-Ra " + filePath,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};

if (value == null)
return null;
process.Start();
var output = process.StandardOutput.ReadToEnd().Trim();
process.WaitForExit();

value = value.Trim('\"', '%', '1', ' ');
return (!File.Exists(value) ? null : value);
return string.IsNullOrEmpty(output) ? null : output;
}
catch
{
return null;
}
}

private static string GetDefaultLinuxProgramForFileType(string fileExtension)
{
try
{
var mimeProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "xdg-mime",
Arguments = "query default " + fileExtension,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};

mimeProcess.Start();
var desktopEntry = mimeProcess.StandardOutput.ReadToEnd().Trim();
mimeProcess.WaitForExit();

if (string.IsNullOrEmpty(desktopEntry))
return null;

// Check if the executable associated with the desktop entry exists
var whichProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "which",
Arguments = desktopEntry,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};

whichProcess.Start();
string executablePath = whichProcess.StandardOutput.ReadToEnd().Trim();
whichProcess.WaitForExit();

return string.IsNullOrEmpty(executablePath) ? null : executablePath;
}
catch
{
return null;
}
}
#endregion

public virtual void AddPath(string path)
Expand Down
1 change: 1 addition & 0 deletions SIL.Core.Tests/SIL.Core.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<Description>Unit tests for SIL.Core</Description>
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies>
<TargetFrameworks>$(TargetFrameworks);net8.0</TargetFrameworks>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
Expand Down
Loading

0 comments on commit dba4714

Please sign in to comment.