Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved unit tests for FFmpegRunner #1364

Merged
merged 27 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1642a26
Improved unit tests for FFmpegRunner to not take over display, be dep…
tombogle Nov 8, 2024
dba4714
Merge branch 'master' into improve-tests
tombogle Nov 14, 2024
cf3c597
Added CI build step to install ffprobe and ffmpeg before running tests.
tombogle Nov 16, 2024
43cbcd6
add a line to debug ffmpeg path
hahn-kev Nov 18, 2024
6d7fb90
search the path for ffmpeg
hahn-kev Nov 18, 2024
c82cea2
Removed inaccurate comments
tombogle Nov 18, 2024
6baf806
Merge branch 'master' into improve-tests
tombogle Nov 19, 2024
4fabb1a
+semver:minor Added FFmpegRunner.FfmpegMinimumVersion static property
tombogle Nov 19, 2024
590ffc1
Merge branch 'master' into improve-tests
tombogle Nov 19, 2024
02b8bfb
Added not in CHANGELOG.md about enhanced FFmpegRunner functionality: …
tombogle Nov 19, 2024
4901250
Allow (the only non-ignored test in) AudioPlayerTests to run on CI build
tombogle Nov 19, 2024
568c08b
Attempt to enable tests that use audio devices on CI build
tombogle Nov 19, 2024
b7e5120
Upgraded irrKlang DLLs to version 1.6
tombogle Nov 19, 2024
77ea045
Added specific NUnit cataegories for RequiresAudioOutputDevice and Re…
tombogle Nov 21, 2024
c0e0ce0
Added FFmpeg to path in build.yml
tombogle Nov 21, 2024
99e9bc4
removed `add-path` step from build.yml since that command is disabled…
tombogle Nov 21, 2024
10d0165
+semver:major Made MediaInfo look for the FFprobe exe in the same loc…
tombogle Nov 22, 2024
a77c5d9
Added installation of ffmpeg to Appveyor build and excluded tests tha…
tombogle Nov 22, 2024
2e5e58c
Try installing ab-audio-cable to see if that simulates presence of au…
tombogle Nov 22, 2024
269b695
Merge branch 'master' into improve-tests
tombogle Dec 3, 2024
c1e1801
Since vb-audio-cable doesn't have a choco installer, try downloading …
tombogle Dec 3, 2024
9b9df3a
Removed silent switch from VB Cable installer so I see if it is maybe…
tombogle Dec 5, 2024
f36c07d
Run VB Cable installer with elevated privileges and verify installation
tombogle Dec 5, 2024
bd14972
Gave up on installing a virtual audio input driver on Github build ag…
tombogle Dec 6, 2024
7ec02ef
See if explicitly creating SLDR cache folder will enable test setup t…
tombogle Dec 6, 2024
9b61be8
Revert "See if explicitly creating SLDR cache folder will enable test…
tombogle Dec 9, 2024
a045012
Merge branch 'master' into improve-tests
tombogle Dec 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,39 @@ jobs:
- 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

# Several steps to set up FFmpeg and Scream and start Audio Service so that audio tests can run
- name: Install FFmpeg
uses: FedericoCarboni/setup-ffmpeg@v3
id: setup-ffmpeg
with:
ffmpeg-version: release
- run: echo ffmpeg path ${{ steps.setup-ffmpeg.outputs.ffmpeg-path }}

- name: Install Scream on Windows
shell: powershell
run: |
Invoke-WebRequest https://github.com/duncanthrax/scream/releases/download/4.0/Scream4.0.zip -OutFile Scream4.0.zip
Expand-Archive -Path Scream4.0.zip -DestinationPath Scream
openssl req -batch -verbose -x509 -newkey rsa -keyout ScreamCertificate.pvk -out ScreamCertificate.cer -nodes -extensions v3_req
openssl pkcs12 -export -nodes -in ScreamCertificate.cer -inkey ScreamCertificate.pvk -out ScreamCertificate.pfx -passout pass:

- name: Setup MSVC Dev Cmd
uses: ilammy/msvc-dev-cmd@v1

- name: Sign and Install Scream Driver on Windows
if: matrix.os == 'windows-latest'
shell: powershell
run: |
signtool sign /v /fd SHA256 /f ScreamCertificate.pfx Scream\Install\driver\x64\Scream.cat
Import-Certificate -FilePath ScreamCertificate.cer -CertStoreLocation Cert:\LocalMachine\root
Import-Certificate -FilePath ScreamCertificate.cer -CertStoreLocation Cert:\LocalMachine\TrustedPublisher
Scream\Install\helpers\devcon-x64.exe install Scream\Install\driver\x64\Scream.inf *Scream
timeout-minutes: 5

- name: Start Windows Audio Service
run: net start audiosrv
shell: powershell

# 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
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- [SIL.Archiving] Added public property isValid to IMDIPackage.
- [SIL.Archiving] Added public event InitializationFailed to IMDIArchivingDlgViewModel.
- [SIL.Archiving] Added the following properties to ArchivingDlgViewModel as an alternative way to customize the initial summary displayed: GetOverriddenPreArchivingMessages, InitialFileGroupDisplayMessageType, OverrideGetFileGroupDisplayMessage
- [SIL.Media] Added FFmpegRunner.FfmpegMinimumVersion property.

### Changed

Expand Down Expand Up @@ -78,6 +79,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- [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.
- [SIL.Media] Made FFmpegRunner able to use version of FFmpeg found on the path.
- [SIL.Media] Upgraded irrKlang to v. 1.6.

### Fixed
- [SIL.Archiving] Fixed typo in RampArchivingDlgViewModel for Ethnomusicology performance collection.
Expand Down
3 changes: 3 additions & 0 deletions Palaso.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Abbysinica/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Abridgement/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=acknowledgements/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=acodec/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=adaptor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ALSA/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=analytics/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -34,6 +35,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Arbil/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Argb/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=autosize/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=avconv/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=bbbcccvvv/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=BBCCCVVV/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=bidi/@EntryIndexedValue">True</s:Boolean>
Expand Down Expand Up @@ -118,6 +120,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Subtags/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=taskbar/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=triglot/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=vcodec/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=versifications/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=vshost/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Wasta/@EntryIndexedValue">True</s:Boolean>
Expand Down
1 change: 0 additions & 1 deletion SIL.Media.Tests/AudioFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ namespace SIL.Media.Tests
{
// These will not work if a speaker is not available.
[TestFixture]
[Category("SkipOnTeamCity")]
[Category("AudioTests")]
public class AudioFactoryTests
{
Expand Down
13 changes: 6 additions & 7 deletions SIL.Media.Tests/AudioPlayerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
namespace SIL.Media.Tests
{
/// <summary>
/// All these tests are skipped on TeamCity (even if you remove this category) because SIL.Media.Tests compiles to an exe,
/// and the project that builds libpalaso on TeamCity (build/Palaso.proj, task Test) invokes RunNUnitTC which
/// selects the test assemblies using Include="$(RootDir)/output/$(Configuration)/*.Tests.dll" which excludes exes.
/// I have not tried to verify that all of these tests would actually have problems on TeamCity, but it seemed
/// helpful to document in the usual way that they are not, in fact, run there.
/// Tests for the AudioPlayer class.
/// </summary>
andrew-polk marked this conversation as resolved.
Show resolved Hide resolved
[Category("SkipOnTeamCity")]
/// <remarks>This fixture used to be skipped during the CI build, perhaps because of the test
/// that actually tries to do playback (since that would require an audio output device).
/// However, that test is now ignored and the only non-ignored test works fine without an
/// actual playback device.</remarks>
[TestFixture]
public class AudioPlayerTests
{
Expand All @@ -33,7 +32,7 @@ public void LoadFile_ThenDispose_FileCanBeDeleted()
}

/// <summary>
/// This test shows what caused hearthis to abandon the naudio; previous to a change to this class in 2/2012, it is believed that all was ok.
/// This test shows what caused HearThis to abandon NAudio; previous to a change to this class in 2/2012, it is believed that all was ok.
/// </summary>
[Test, Ignore("Known to Fail (hangs forever")]
public void PlayFile_ThenDispose_FileCanBeDeleted()
Expand Down
1 change: 0 additions & 1 deletion SIL.Media.Tests/AudioRecorderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ namespace SIL.Media.Tests
// Some of these tests require a speaker. Others require a microphone.
// None of them will work if neither a speaker nor a microphone is available.
[TestFixture]
[Category("SkipOnTeamCity")]
[Category("AudioTests")]
public class AudioRecorderTests
{
Expand Down
10 changes: 3 additions & 7 deletions SIL.Media.Tests/AudioSessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ namespace SIL.Media.Tests
// Some of these tests require a speaker. Others require a microphone.
// None of them will work if neither a speaker nor a microphone is available.
[TestFixture]
[NUnit.Framework.Category("SkipOnTeamCity")]
[NUnit.Framework.Category("AudioTests")]
public class AudioSessionTests
{
Expand Down Expand Up @@ -265,10 +264,7 @@ public RecordingSession(int millisecondsToRecordBeforeStopping)
_recorder.StopRecordingAndSaveAsWav();
}

public ISimpleAudioSession Recorder
{
get { return _recorder; }
}
public ISimpleAudioSession Recorder => _recorder;

public void Dispose()
{
Expand Down Expand Up @@ -451,8 +447,8 @@ public void Record_LongRecording()
{
using (var folder = new TemporaryFolder("Record_LongRecording"))
{
string fpath = Path.Combine(folder.Path, "long.wav");
using (var x = AudioFactory.CreateAudioSession(fpath))
string fPath = Path.Combine(folder.Path, "long.wav");
using (var x = AudioFactory.CreateAudioSession(fPath))
{
SystemSounds.Beep.Play();
Assert.DoesNotThrow(() => x.StartRecording());
Expand Down
100 changes: 82 additions & 18 deletions SIL.Media.Tests/FFmpegRunnerTests.cs
hahn-kev marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,38 +1,67 @@
using System;
using System.IO;
using FFMpegCore;
using NUnit.Framework;
using SIL.IO;
using SIL.Media.Tests.Properties;
using SIL.Progress;

namespace SIL.Media.Tests
{
/// <summary>
/// All these tests are skipped on TeamCity (even if you remove this category) because SIL.Media.Tests compiles to an exe,
/// and the project that builds libpalaso on TeamCity (build/Palaso.proj, task Test) invokes RunNUnitTC which
/// selects the test assemblies using Include="$(RootDir)/output/$(Configuration)/*.Tests.dll" which excludes exes.
/// I have not tried to verify that all of these tests would actually have problems on TeamCity, but it seemed
/// helpful to document in the usual way that they are not, in fact, run there.
/// </summary>
[Category("SkipOnTeamCity")]
[Category("RequiresFfmpeg")]
[TestFixture]
public class FFmpegRunnerTests
{
[OneTimeSetUp]
public void CheckRequirements()
{
if (!FFmpegRunner.HaveNecessaryComponents)
Assert.Ignore("These tests require ffmpeg to be installed.");
{
if (Environment.GetEnvironmentVariable("CI") == null)
Assert.Ignore("These tests require ffmpeg to be installed.");
else
Assert.Fail("On CI build using GHA, FFMpeg should have been installed before running tests.");
hahn-kev marked this conversation as resolved.
Show resolved Hide resolved
}
}

[Test]
[Category("RequiresFfmpeg")]
public void HaveNecessaryComponents_ReturnsTrue()
public void HaveNecessaryComponents_NoExplicitMinVersion_ReturnsTrue()
{
Assert.IsTrue(FFmpegRunner.HaveNecessaryComponents);
}

[TestCase(5, 1)]
[TestCase(4, 9)]
public void HaveNecessaryComponents_TwoDigitMinVersion_ReturnsTrue(int major, int minor)
{
FFmpegRunner.FfmpegMinimumVersion = new Version(major, minor);
Assert.IsTrue(FFmpegRunner.HaveNecessaryComponents);
}

[TestCase(5, 1, 1)]
[TestCase(5, 0, 0)]
public void HaveNecessaryComponents_ThreeDigitMinVersion_ReturnsTrue(int major, int minor, int build)
{
FFmpegRunner.FfmpegMinimumVersion = new Version(major, minor, build);
Assert.IsTrue(FFmpegRunner.HaveNecessaryComponents);
}

[TestCase(5, 1, 1, 0)]
[TestCase(5, 0, 0, 9)]
public void HaveNecessaryComponents_FourDigitMinVersion_ReturnsTrue(int major, int minor, int build, int revision)
{
FFmpegRunner.FfmpegMinimumVersion = new Version(major, minor, build, revision);
Assert.IsTrue(FFmpegRunner.HaveNecessaryComponents);
}

[Test]
public void HaveNecessaryComponents_ReallyHighVersionThatDoesNotExist_ReturnsFalse()
{
FFmpegRunner.FfmpegMinimumVersion = new Version(int.MaxValue, int.MaxValue);
Assert.IsFalse(FFmpegRunner.HaveNecessaryComponents);
}

[Test]
[Category("RequiresFfmpeg")]
public void ExtractMp3Audio_CreatesFile()
{
using (var file = TempFile.FromResource(Resources.tiny, ".wmv"))
Expand All @@ -44,7 +73,6 @@ public void ExtractMp3Audio_CreatesFile()
}

[Test]
[Category("RequiresFfmpeg")]
public void ExtractOggAudio_CreatesFile()
{
using (var file = TempFile.FromResource(Resources.tiny, ".wmv"))
Expand All @@ -56,7 +84,6 @@ public void ExtractOggAudio_CreatesFile()
}

[Test]
[Category("RequiresFfmpeg")]
public void ChangeNumberOfAudioChannels_CreatesFile()
{
using (var file = TempFile.FromResource(Resources._2Channel, ".wav"))
Expand All @@ -68,7 +95,6 @@ public void ChangeNumberOfAudioChannels_CreatesFile()
}

[Test]
[Category("RequiresFfmpeg")]
public void MakeLowQualityCompressedAudio_CreatesFile()
{
using (var file = TempFile.FromResource(Resources.tiny, ".wmv"))
Expand All @@ -79,20 +105,58 @@ public void MakeLowQualityCompressedAudio_CreatesFile()
var outputPath = originalAudioPath.Replace("mp3", "low.mp3");
FFmpegRunner.MakeLowQualityCompressedAudio(originalAudioPath, outputPath, new ConsoleProgress());
Assert.IsTrue(File.Exists(outputPath));
System.Diagnostics.Process.Start(outputPath);

var mediaInfoOrig = FFProbe.Analyse(file.Path);
var mediaInfo = FFProbe.Analyse(outputPath);

// Validate resolution and bit rate
Assert.That(mediaInfo.PrimaryVideoStream, Is.Null);
Assert.That(mediaInfo.PrimaryAudioStream, Is.Not.Null);
Assert.That(mediaInfo.AudioStreams.Count, Is.EqualTo(1));
Assert.That(mediaInfo.PrimaryAudioStream.Channels, Is.EqualTo(1));
Assert.That(mediaInfo.Format.BitRate, Is.LessThan(mediaInfoOrig.Format.BitRate));
Assert.That(mediaInfo.PrimaryAudioStream.SampleRateHz, Is.EqualTo(8000));
try
{
RobustFile.Delete(outputPath);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}

[Test]
[Category("RequiresFfmpeg")]
public void MakeLowQualitySmallVideo_CreatesFile()
{
using (var file = TempFile.FromResource(Resources.tiny, ".wmv"))
{
var outputPath = file.Path.Replace("wmv", "low.wmv");
FFmpegRunner.MakeLowQualitySmallVideo(file.Path, outputPath, 0, new ConsoleProgress());
Assert.IsTrue(File.Exists(outputPath));
System.Diagnostics.Process.Start(outputPath);

var mediaInfoOrig = FFProbe.Analyse(file.Path);
var mediaInfo = FFProbe.Analyse(outputPath);

// Validate resolution and bit rate
Assert.That(mediaInfo.PrimaryVideoStream, Is.Not.Null);
Assert.That(mediaInfo.VideoStreams.Count, Is.EqualTo(1));
Assert.That(mediaInfo.PrimaryAudioStream, Is.Not.Null);
Assert.That(mediaInfo.AudioStreams.Count, Is.EqualTo(1));
Assert.That(mediaInfo.PrimaryVideoStream.Width, Is.EqualTo(160));
Assert.That(mediaInfo.PrimaryVideoStream.Height, Is.EqualTo(120));
Assert.That(mediaInfo.Format.BitRate, Is.LessThan(mediaInfoOrig.Format.BitRate));
try
{
// When running the by-hand test, the default media player might leave this
// locked, so this cleanup will fail.
RobustFile.Delete(outputPath);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
Expand Down
Loading
Loading