[tracker-miners/sam/cuesheet-testcase: 2/2] functional-tests: Add a test for the cuesheet extractor



commit b09116a751d8220054f6e47df1dcbb0ed669acca
Author: Sam Thursfield <sam afuera me uk>
Date:   Mon Apr 1 13:40:03 2019 +0200

    functional-tests: Add a test for the cuesheet extractor
    
    We recently discovered a long standing regression in this feature:
    https://gitlab.gnome.org/GNOME/tracker-miners/issues/60
    
    As the saying goes: "if it's not tested, it doesn't work"[1].
    
    Depends on https://gitlab.gnome.org/GNOME/tracker/merge_requests/85
    
    1. https://yakking.branchable.com/posts/truism-4-if-it-is-not-tested/

 .../401-extractor-flac-cuesheet.py                 | 101 +++++++++++++++++++++
 tests/functional-tests/common/utils/extractor.py   |  53 ++++++++++-
 tests/functional-tests/meson.build                 |   1 +
 .../test-extraction-data/audio/cuesheet-test.cue   |  17 ++++
 4 files changed, 171 insertions(+), 1 deletion(-)
---
diff --git a/tests/functional-tests/401-extractor-flac-cuesheet.py 
b/tests/functional-tests/401-extractor-flac-cuesheet.py
new file mode 100755
index 000000000..fc2df05a4
--- /dev/null
+++ b/tests/functional-tests/401-extractor-flac-cuesheet.py
@@ -0,0 +1,101 @@
+#!/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()
diff --git a/tests/functional-tests/common/utils/extractor.py 
b/tests/functional-tests/common/utils/extractor.py
index 85a1df940..17148b948 100644
--- a/tests/functional-tests/common/utils/extractor.py
+++ b/tests/functional-tests/common/utils/extractor.py
@@ -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)
diff --git a/tests/functional-tests/meson.build b/tests/functional-tests/meson.build
index 355d4bd66..a7e49b3cd 100644
--- a/tests/functional-tests/meson.build
+++ b/tests/functional-tests/meson.build
@@ -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',
diff --git a/tests/functional-tests/test-extraction-data/audio/cuesheet-test.cue 
b/tests/functional-tests/test-extraction-data/audio/cuesheet-test.cue
new file mode 100644
index 000000000..ac4cbb823
--- /dev/null
+++ b/tests/functional-tests/test-extraction-data/audio/cuesheet-test.cue
@@ -0,0 +1,17 @@
+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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]