[InlineData(5, 0)] // default out of valid range
        public async void GetImage_DefaultVideoStreams_ReturnsCorrectStreamImage(int defaultIndex, int targetIndex)
        {
            var input = new Movie {
                DefaultVideoStreamIndex = defaultIndex
            };

            string targetPath   = "path.jpg";
            var    mediaStreams = new List <MediaStream>();
            var    mediaEncoder = new Mock <IMediaEncoder>(MockBehavior.Strict);

            for (int i = 0; i <= targetIndex; i++)
            {
                var mediaStream = new MediaStream {
                    Type = MediaStreamType.Video, Index = i
                };
                mediaStreams.Add(mediaStream);

                var path = i == targetIndex ? targetPath : "wrong stream called!";
                mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <MediaSourceInfo>(), mediaStream, It.IsAny <Video3DFormat?>(), It.IsAny <TimeSpan?>(), It.IsAny <CancellationToken>()))
                .Returns(Task.FromResult(path));
            }

            var defaultStream      = defaultIndex < mediaStreams.Count ? mediaStreams[targetIndex] : null;
            var mediaSourceManager = GetMediaSourceManager(input, defaultStream, mediaStreams);

            var videoImageProvider = new VideoImageProvider(mediaSourceManager, mediaEncoder.Object, new NullLogger <VideoImageProvider>());

            var actual = await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);

            Assert.NotNull(actual);
            Assert.True(actual.HasImage);
            Assert.Equal(targetPath, actual.Path);
            Assert.Equal(ImageFormat.Jpg, actual.Format);
        }
        [InlineData(500, 50)]  // calculated time
        public async void GetImage_TimeSpan_SelectsCorrectTime(int?runTimeSeconds, long expectedSeconds)
        {
            MediaStream targetStream = new() { Type = MediaStreamType.Video, Index = 0 };
            var         input        = new Movie
            {
                DefaultVideoStreamIndex = 0,
                RunTimeTicks            = runTimeSeconds * TimeSpan.TicksPerSecond
            };

            var mediaSourceManager = GetMediaSourceManager(input, targetStream, new List <MediaStream> {
                targetStream
            });

            // use a callback to catch the actual value
            // provides more information on failure than verifying a specific input was called on the mock
            TimeSpan?actualTimeSpan = null;
            var      mediaEncoder   = new Mock <IMediaEncoder>(MockBehavior.Strict);

            mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <MediaSourceInfo>(), It.IsAny <MediaStream>(), It.IsAny <Video3DFormat?>(), It.IsAny <TimeSpan?>(), CancellationToken.None))
            .Callback <string, string, MediaSourceInfo, MediaStream, Video3DFormat?, TimeSpan?, CancellationToken>((_, _, _, _, _, timeSpan, _) => actualTimeSpan = timeSpan)
            .Returns(Task.FromResult("path"));

            var videoImageProvider = new VideoImageProvider(mediaSourceManager, mediaEncoder.Object, new NullLogger <VideoImageProvider>());

            // not testing return, just verifying what gets requested for time span
            await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);

            Assert.Equal(TimeSpan.FromSeconds(expectedSeconds), actualTimeSpan);
        }
        public async void GetImage_UnsupportedInput_ReturnsNoImage(Video input)
        {
            var mediaSourceManager = GetMediaSourceManager(input, null, new List <MediaStream>());
            var videoImageProvider = new VideoImageProvider(mediaSourceManager, Mock.Of <IMediaEncoder>(), new NullLogger <VideoImageProvider>());

            var actual = await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);

            Assert.NotNull(actual);
            Assert.False(actual.HasImage);
        }