From 5dc957ec74a4467f31464c01a370241b3a3a4af4 Mon Sep 17 00:00:00 2001 From: Victor Elias Date: Tue, 31 Jan 2023 18:56:27 -0300 Subject: [PATCH] vodtester: Add E2E tests for playback (#252) * go.mod: add go-api-client with playback info * vodtester: Create helper function to check playback reusing existing stuff * vodtester: Grab duration from API So good to avoid having to ffprobe or joy4 locally lol * vodtester: checkPlayback from all the right places --- go.mod | 2 +- go.sum | 4 +- internal/app/vodtester/vodtester_app.go | 66 +++++++++++++++++++++++-- m3u8/tester.go | 19 +++++-- 4 files changed, 80 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 0c9eed77..91a2a2d8 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/glog v1.0.0 github.com/gosuri/uilive v0.0.3 // indirect github.com/gosuri/uiprogress v0.0.1 - github.com/livepeer/go-api-client v0.4.2 + github.com/livepeer/go-api-client v0.4.2-0.20230124192858-4ae2f6d037f3 github.com/livepeer/go-livepeer v0.5.31 github.com/livepeer/joy4 v0.1.2-0.20220210094601-95e4d28f5f07 github.com/livepeer/leaderboard-serverless v1.0.0 diff --git a/go.sum b/go.sum index 15472e6f..aa24e7bb 100644 --- a/go.sum +++ b/go.sum @@ -738,8 +738,8 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/livepeer/go-api-client v0.4.2 h1:jfYY6lIpB6XOXSqJOKOc6eQ1VGlYJNh5W+7HFV0v6VE= -github.com/livepeer/go-api-client v0.4.2/go.mod h1:Jdb+RI7JyzEZOHd1GUuKofwFDKMO/btTa80SdpUpYQw= +github.com/livepeer/go-api-client v0.4.2-0.20230124192858-4ae2f6d037f3 h1:qmX+PwBhzvDiybrePyvDmgUK4+n1Ny53UtR0xohNMfs= +github.com/livepeer/go-api-client v0.4.2-0.20230124192858-4ae2f6d037f3/go.mod h1:Jdb+RI7JyzEZOHd1GUuKofwFDKMO/btTa80SdpUpYQw= github.com/livepeer/go-livepeer v0.5.31 h1:LcN+qDnqWRws7fdVYc4ucZPVcLQRs2tehUYCQVnlnRw= github.com/livepeer/go-livepeer v0.5.31/go.mod h1:cpBikcGWApkx0cyR0Ht+uAym7j3uAwXGpPbvaOA8XUU= github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= diff --git a/internal/app/vodtester/vodtester_app.go b/internal/app/vodtester/vodtester_app.go index a007aabe..942b2bb3 100644 --- a/internal/app/vodtester/vodtester_app.go +++ b/internal/app/vodtester/vodtester_app.go @@ -10,10 +10,14 @@ import ( "github.com/golang/glog" "github.com/livepeer/go-api-client" + "github.com/livepeer/stream-tester/internal/app/common" + "github.com/livepeer/stream-tester/m3u8" "golang.org/x/sync/errgroup" ) +const maxTimeToWaitForManifest = 20 * time.Second + type ( // IVodTester ... IVodTester interface { @@ -136,8 +140,15 @@ func (vt *vodTester) uploadViaUrlTester(vodImportUrl string, taskPollDuration ti if err != nil { glog.Errorf("Error processing asset assetId=%s taskId=%s", importAsset.ID, importTask.ID) + return nil, fmt.Errorf("error waiting for asset processing: %w", err) + } + + if err := vt.checkPlayback(importAsset.ID); err != nil { + glog.Errorf("Error checking playback assetId=%s err=%v", importAsset.ID, err) + return nil, fmt.Errorf("error checking playback: %w", err) } - return importAsset, err + + return importAsset, nil } func (vt *vodTester) directUploadTester(fileName string, taskPollDuration time.Duration) error { @@ -175,8 +186,15 @@ func (vt *vodTester) directUploadTester(fileName string, taskPollDuration time.D err = vt.CheckTaskProcessing(taskPollDuration, uploadTask) if err != nil { glog.Errorf("Error processing asset assetId=%s taskId=%s", uploadAsset.ID, uploadTask.ID) + return fmt.Errorf("error waiting for asset processing: %w", err) + } + + if err := vt.checkPlayback(uploadAsset.ID); err != nil { + glog.Errorf("Error checking playback assetId=%s err=%v", uploadAsset.ID, err) + return fmt.Errorf("error checking playback: %w", err) } - return err + + return nil } func (vt *vodTester) resumableUploadTester(fileName string, taskPollDuration time.Duration) error { @@ -216,9 +234,51 @@ func (vt *vodTester) resumableUploadTester(fileName string, taskPollDuration tim if err != nil { glog.Errorf("Error processing asset assetId=%s taskId=%s", uploadAsset.ID, uploadTask.ID) + return fmt.Errorf("error waiting for asset processing: %w", err) + } + + if err := vt.checkPlayback(uploadAsset.ID); err != nil { + glog.Errorf("Error checking playback assetId=%s err=%v", uploadAsset.ID, err) + return fmt.Errorf("error checking playback: %w", err) + } + + return nil +} + +func (vt *vodTester) checkPlayback(assetID string) error { + asset, err := vt.Lapi.GetAsset(assetID, false) + if err != nil { + return fmt.Errorf("error getting asset: %w", err) + } else if asset.VideoSpec.DurationSec <= 0 { + return fmt.Errorf("missing asset duration (%f)", asset.VideoSpec.DurationSec) + } + duration := time.Duration(asset.VideoSpec.DurationSec * float64(time.Second)) + + pinfo, err := vt.Lapi.GetPlaybackInfo(asset.PlaybackID) + if err != nil { + return fmt.Errorf("error getting playback info: %w", err) } - return err + var url string + for _, src := range pinfo.Meta.Source { + if src.Type == api.PlaybackInfoSourceTypeHLS { + url = src.Url + break + } + } + if url == "" { + return fmt.Errorf("no HLS source found in playback info") + } + + stats, err := m3u8.CheckStats(vt.Ctx, url, duration, maxTimeToWaitForManifest) + if err != nil { + return err + } + if numRenditions := len(stats.SegmentsNum); numRenditions <= 1 { + return fmt.Errorf("no transcoded renditions found in playlist") + } + + return nil } // Patches the target URL with the source URL host, only if the latter is not diff --git a/m3u8/tester.go b/m3u8/tester.go index 266256c4..187e89e5 100644 --- a/m3u8/tester.go +++ b/m3u8/tester.go @@ -9,6 +9,7 @@ import ( "github.com/livepeer/go-api-client" "github.com/livepeer/stream-tester/internal/metrics" "github.com/livepeer/stream-tester/internal/testers" + "github.com/livepeer/stream-tester/model" ) func InitCensus(service, version string) { @@ -17,14 +18,22 @@ func InitCensus(service, version string) { } func Check(ctx context.Context, url string, profiles []api.Profile, expectedDuration time.Duration, timeout time.Duration) error { - downloader := testers.NewM3utester2(ctx, url, false, false, false, false, timeout, nil, false) - <-downloader.Done() - stats := downloader.VODStats() - if ok, ers := stats.IsOk(expectedDuration, false); !ok { - return fmt.Errorf("playlist not ok: %s", ers) + stats, err := CheckStats(ctx, url, expectedDuration, timeout) + if err != nil { + return err } if len(stats.SegmentsNum) != len(profiles)+1 { return fmt.Errorf("number of renditions doesn't match (has %d should %d)", len(stats.SegmentsNum), len(profiles)+1) } return nil } + +func CheckStats(ctx context.Context, url string, expectedDuration time.Duration, timeout time.Duration) (model.VODStats, error) { + downloader := testers.NewM3utester2(ctx, url, false, false, false, false, timeout, nil, false) + <-downloader.Done() + stats := downloader.VODStats() + if ok, ers := stats.IsOk(expectedDuration, false); !ok { + return model.VODStats{}, fmt.Errorf("playlist not ok: %s", ers) + } + return stats, nil +}