Commit b09116a7 authored by Sam Thursfield's avatar Sam Thursfield

functional-tests: Add a test for the cuesheet extractor

We recently discovered a long standing regression in this feature:
#60

As the saying goes: "if it's not tested, it doesn't work"[1].

Depends on tracker!85

1. https://yakking.branchable.com/posts/truism-4-if-it-is-not-tested/
parent fc281ca8
Pipeline #79577 passed with stage
in 1 minute and 13 seconds
#!/usr/bin/env python3
# Copyright (C) 2019, Sam Thursfield (sam@afuera.me.uk)
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
"""
Tests the FLAC+cuesheet extraction feature.
"""
import os
import shutil
import tempfile
import unittest as ut
import common.utils.configuration as cfg
from common.utils.helpers import log
from common.utils.extractor import get_tracker_extract_jsonld_output, create_test_flac, TrackerExtractTestCase
class FlacCuesheetTest(TrackerExtractTestCase):
def spec(self, audio_path):
audio_uri = 'file://' + audio_path
return {
'@type': ['nfo:Audio'],
'nie:url': audio_uri,
'nfo:duration': 360,
'nfo:sampleRate': 44100,
'nie:hasLogicalPart': [
{
'@type': ['nmm:MusicPiece', 'nfo:Audio'],
'nfo:audioOffset': 0.0,
'nfo:duration': 257,
'nie:isLogicalPartOf': audio_uri,
'nie:isStoredAs': audio_uri,
'nie:title': 'Only Shallow',
'nmm:trackNumber': 1,
'nmm:musicAlbum': {
'@id': 'urn:album:Loveless:My%20Bloody%20Valentine',
'@type': 'nmm:MusicAlbum',
'nmm:albumTrackCount': 2,
'nmm:albumArtist': ['urn:artist:My%20Bloody%20Valentine'],
'nie:title': 'Loveless',
},
'nmm:musicAlbumDisc': {
'@id': 'urn:album-disc:Loveless:My%20Bloody%20Valentine:Disc1',
'@type': 'nmm:MusicAlbumDisc',
'nmm:setNumber': 1,
'nmm:albumDiscAlbum': 'urn:album:Loveless:My%20Bloody%20Valentine',
},
'nmm:performer': {
'@id': 'urn:artist:My%20Bloody%20Valentine',
'@type': 'nmm:Artist',
'nmm:artistName': 'My Bloody Valentine',
},
},
{
'@type': ['nmm:MusicPiece', 'nfo:Audio'],
'nfo:audioOffset': 257.6933333333333,
'nfo:duration': 102,
'nie:isLogicalPartOf': audio_uri,
'nie:isStoredAs': audio_uri,
'nmm:musicAlbum': 'urn:album:Loveless:My%20Bloody%20Valentine',
'nmm:musicAlbumDisc': 'urn:album-disc:Loveless:My%20Bloody%20Valentine:Disc1',
'nmm:performer': 'urn:artist:My%20Bloody%20Valentine',
'nie:title': 'Loomer',
'nmm:trackNumber': 2,
}
],
}
def test_external_cue_sheet(self):
with tempfile.TemporaryDirectory() as tmpdir:
datadir = os.path.join(os.getcwd() + "/test-extraction-data")
shutil.copy(os.path.join(datadir, 'audio', 'cuesheet-test.cue'), tmpdir)
audio_path = os.path.join(tmpdir, 'cuesheet-test.flac')
create_test_flac(audio_path, duration=6*60)
result = get_tracker_extract_jsonld_output(audio_path)
self.assert_extract_result_matches_spec(
self.spec(audio_path), result, audio_path, __file__)
if __name__ == '__main__':
ut.main()
......@@ -23,11 +23,16 @@ from common.utils import configuration as cfg
from common.utils.helpers import log
import errno
import json
import math
import os
import re
import subprocess
import unittest as ut
import gi
gi.require_version('Gst', '1.0')
from gi.repository import GLib, Gst
def get_tracker_extract_jsonld_output(filename, mime_type=None):
"""
......@@ -139,7 +144,11 @@ class TrackerExtractTestCase(ut.TestCase):
error_wrong_length % (prop, filename, spec_filename))
for i in range(0, len(expected_value)):
self.assert_extract_result_matches_spec(spec[prop][i], result[prop][i], filename, spec_filename)
if isinstance(expected_value[i], dict):
self.assert_extract_result_matches_spec(expected_value[i], result[prop][i], filename, spec_filename)
else:
self.assertEqual(str(expected_value[i]), str(result[prop][i]),
error_wrong_value % (prop, filename, spec_filename))
elif isinstance(expected_value, dict):
self.assert_extract_result_matches_spec(expected_value, result[prop], filename, spec_filename)
else:
......@@ -162,3 +171,45 @@ class TrackerExtractTestCase(ut.TestCase):
for prop in expected_keys:
self.assertDictHasKey(result, prop,
error_missing_prop % (prop, filename, spec_filename))
def create_test_flac(path, duration, timeout=10):
"""
Create a .flac audio file for testing purposes.
FLAC audio doesn't compress test data particularly efficiently, so
committing an audio file more than a few seconds long to Git is not
practical. This function creates a .flac file containing a test tone.
The 'duration' parameter sets the length in seconds of the time.
The function is guaranteed to return or raise an exception within the
number of seconds given in the 'timeout' parameter.
"""
Gst.init([])
num_buffers = math.ceil(duration * 44100 / 1024.0)
pipeline_src = ' ! '.join([
'audiotestsrc num-buffers=%s samplesperbuffer=1024' % num_buffers,
'capsfilter caps="audio/x-raw,rate=44100"',
'flacenc',
'filesink location=%s' % path,
])
log("Running pipeline: %s" % pipeline_src)
pipeline = Gst.parse_launch(pipeline_src)
ret = pipeline.set_state(Gst.State.PLAYING)
msg = pipeline.get_bus().poll(Gst.MessageType.ERROR | Gst.MessageType.EOS,
timeout * Gst.SECOND)
if msg and msg.type == Gst.MessageType.EOS:
pass
elif msg and msg.type == Gst.MessageType.ERROR:
raise RuntimeError(msg.parse_error())
elif msg:
raise RuntimeError("Got unexpected GStreamer message %s" % msg.type)
else:
raise RuntimeError("Timeout generating test audio file after %i seconds" % timeout)
pipeline.set_state(Gst.State.NULL)
......@@ -30,6 +30,7 @@ functional_tests = [
'310-fts-basic',
'311-fts-file-operations',
'312-fts-stopwords',
'401-extractor-flac-cuesheet',
'410-extractor-decorator',
'500-writeback',
'501-writeback-details',
......
REM Example CUE sheet adapted from http://wiki.hydrogenaud.io/index.php?title=Cue_sheet#Examples
REM GENRE Alternative
REM DATE 1991
REM DISCID 860B640B
REM COMMENT "ExactAudioCopy v0.95b4"
PERFORMER "My Bloody Valentine"
TITLE "Loveless"
FILE "cuesheet-test.cue" WAVE
TRACK 01 AUDIO
TITLE "Only Shallow"
PERFORMER "My Bloody Valentine"
INDEX 01 00:00:00
TRACK 02 AUDIO
TITLE "Loomer"
PERFORMER "My Bloody Valentine"
INDEX 01 04:17:52
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment