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

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like it is still worth a comment explaining why we are skipping these tests in CI.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this file never explained its dependency. Turns out that with that one test ignored, the only remaining test in the fixture runs just fine without any sound device enabled. So I'll remove the SkipOnTeamCity category. I'm now thinkingnthat doing something like this is the real answer:https://github.com/NathanCheshire/CyderUtils/actions/runs/9733272570/workflow. Then all these tests could run in the CI build.

[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