From patchwork Tue Jan 23 17:54:38 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: rshaffer@tunein.com X-Patchwork-Id: 7401 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.156.27 with SMTP id q27csp4591814jak; Tue, 23 Jan 2018 10:00:09 -0800 (PST) X-Google-Smtp-Source: AH8x2251JNW8tXd8QwpyJgt3/3C0LXV4+pTP06+s6jB4nvmDqtGRpbpS5y3Epn7DX6usYfSEBM0L X-Received: by 10.223.175.220 with SMTP id y28mr2855107wrd.263.1516730409490; Tue, 23 Jan 2018 10:00:09 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516730409; cv=none; d=google.com; s=arc-20160816; b=JPrDBGsq3BV+rOBc1joh2NzsvaaBdyoOH1QSvUZU+ZSQaEsf1sI6N6cBQvctOy92tq ZLtGjs9Od0mQ+zeC3vgKeDzarv69L5AhuVZurmUAwJNOXuBeYbzg6/c5OCEiQQQIrwWZ JOCtrEhBwSDizq196TiEP8Nmf5NT9vOtNb00gOjVI3eMV5t15DTQIMiQhjY1/xAzVaoF 5Bv/BudkELnF4FXy3gy6+SNVUb5DBX/SyHacGOGSmX9efUNjf5aP1ZpYarMH9X3wLkD9 Id+bMh5XhY7OPWma5EuIG1oRmA8upX35ekvzWbrfnVW1tCx/p7wosohm7D5ofS6jZqfG +YRQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:message-id:date:to:from:dkim-signature :delivered-to:arc-authentication-results; bh=jC8QtuTPLaJB3A6VyQ25wQ5WGd13nMIU3JRRUeolgLI=; b=XPIej1zDhIiSZOlKWRmmakHuCegffMub0VCuZrUCTl20tFj0pV3SHoW6OSsIgqFyDQ B2GFMxk2Ei1HtnfOkHDdW5zlYERKDgE17G90WqaqSLGsgHJktOGw5KUAbKRqx9w1zjsr 95zMpDAOVb7mCtXBnaxxbnUq0hyb+ljrYbwaMOjAuTX5bEvEn8rscaOxjk2qBLefSgl+ hpc79rhiHkuOlVqyRtNiUqAF5Rr0sruaUi5QxqBMFnXdUmIzuVdtmOsNc9alBzXt1uUi Z4oEWmTH2QGun4eECGvruUBvGtVleHZ4uyg5qNhDY9MVA/Y9FhLtMiTi+cCeW7pc0WP1 EIvA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@tunein-com.20150623.gappssmtp.com header.s=20150623 header.b=hg9kzilq; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 11si613673wra.274.2018.01.23.10.00.08; Tue, 23 Jan 2018 10:00:09 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@tunein-com.20150623.gappssmtp.com header.s=20150623 header.b=hg9kzilq; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 53E65689D39; Tue, 23 Jan 2018 20:00:05 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-io0-f172.google.com (mail-io0-f172.google.com [209.85.223.172]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 81066689C45 for ; Tue, 23 Jan 2018 19:59:58 +0200 (EET) Received: by mail-io0-f172.google.com with SMTP id f34so1840552ioi.13 for ; Tue, 23 Jan 2018 10:00:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tunein-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id; bh=Ceue88XCKTnacFfxPjUwnlZ5vQBTDuFJfy3fcaq9yK8=; b=hg9kzilqaMEfrEUDbNZ1DY5RRLX28n4db3rdrxGTVR/Mptt8Z438Di7o/qBUfGHg/G GaZz4bC7rhGiIcptEtQjKg8RSMnYW8GuYd7zEU5yh0ctkVMamFrzJp2xy8bZkJ8SoVVN KPxIUOsSB8+PvVNYWgPaj3DSamo8AThfhlW9arZ/LMoI9joz+vVLzUxhX2C1JiciwjDO q3Lplg5Ql2pbJ3G9XQq9fulW/14ib4cxPHRDYLNlLh9Ac4IlWJIEaJAwg2S6v89Q4oA3 vWYpJKAfeGtUJBUFFK/r2W2BrySZC5VYcSFKUmgn3hF22pHbV6Hwgwlc7ZnovkV4UFq9 s9/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=Ceue88XCKTnacFfxPjUwnlZ5vQBTDuFJfy3fcaq9yK8=; b=hvP7cGEAQ71vIIiP0xA0zfPd2kgWiLTZBQGgdjvNDDsuPoau7x2wcrypFjMdEZImsX liLALyviMXqBWm/Gy1lDPNSdbSAOrssl6JMYulJDdsopNWSh6iizWF0/HpG9tjBsMVTd yN88Gm78ZEsmFCukRscZ65r8HV5TGaXBh8iGFoTLeM1DTCqvw3G6ZO15eQMHevbbIAKy wuIY2xWZci/8y/eE8GRRrWpUAfUwTe0Ub0vOKIzB6GeHRTjUeyzFc6L/cS0FWspgCAId b5rkzYEtbkz6jeLU/N7SPMi4kiCwwl14PGUmzW+dW6y/wf7xBjRNi+dny96f12BY7h3q wfMA== X-Gm-Message-State: AKwxytegn3vqh0dpPoW4N3bJGMQaCQ9nCT9Wh35430NJeu//Plo+juPj hWQK9KopyBTDPeub7342FfC0RYZXwzA= X-Received: by 10.107.198.86 with SMTP id w83mr4573588iof.75.1516730082063; Tue, 23 Jan 2018 09:54:42 -0800 (PST) Received: from 000984.attlocal.net (104-58-207-63.lightspeed.sntcca.sbcglobal.net. [104.58.207.63]) by smtp.gmail.com with ESMTPSA id m71sm3909917iom.70.2018.01.23.09.54.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 23 Jan 2018 09:54:41 -0800 (PST) From: rshaffer@tunein.com To: ffmpeg-devel@ffmpeg.org Date: Tue, 23 Jan 2018 09:54:38 -0800 Message-Id: <20180123175438.998-1-rshaffer@tunein.com> X-Mailer: git-send-email 2.14.3 (Apple Git-98) Subject: [FFmpeg-devel] [PATCH] fate: add id3v2 tests X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Richard Shaffer MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Richard Shaffer Adds basic unit tests for parsing and writing ID3v2 tags. --- This requires the patch to "add option to parse/store ID3 PRIV tags in metadata." libavformat/Makefile | 3 +- libavformat/tests/.gitignore | 1 + libavformat/tests/id3v2.c | 229 +++++++++++++++++++++++++++++++++++++++++++ tests/fate/libavformat.mak | 4 + tests/ref/fate/id3v2 | 4 + 5 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 libavformat/tests/id3v2.c create mode 100644 tests/ref/fate/id3v2 diff --git a/libavformat/Makefile b/libavformat/Makefile index de0de921c2..753edd9cd5 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -609,7 +609,8 @@ SLIBOBJS-$(HAVE_GNU_WINDRES) += avformatres.o SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h SKIPHEADERS-$(CONFIG_NETWORK) += network.h rtsp.h -TESTPROGS = seek \ +TESTPROGS = id3v2 \ + seek \ url \ # async \ diff --git a/libavformat/tests/.gitignore b/libavformat/tests/.gitignore index 7ceb7a356b..5a06704afd 100644 --- a/libavformat/tests/.gitignore +++ b/libavformat/tests/.gitignore @@ -1,4 +1,5 @@ /fifo_muxer +/id3v2 /movenc /noproxy /rtmpdh diff --git a/libavformat/tests/id3v2.c b/libavformat/tests/id3v2.c new file mode 100644 index 0000000000..9efd0f9011 --- /dev/null +++ b/libavformat/tests/id3v2.c @@ -0,0 +1,229 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "libavformat/avio.h" +#include "libavformat/avio_internal.h" +#include "libavformat/id3v2.h" +#include "libavutil/dict.h" + +static int tag_equal(AVDictionary *meta, const char * tag, const char * value) +{ + int failures = 0; + AVDictionaryEntry *entry = av_dict_get(meta, tag, NULL, 0); + if (!entry) { + failures++; + fprintf(stderr, "expected metadata tag '%s' not found\n", tag); + } + if (strcmp(entry->value, value)) { + failures++; + fprintf(stderr, "metadata tag '%s' has value '%s'; expected '%s'\n", + tag, entry->value, value); + } + return failures; +} + +static int test_id3v2_read() +{ + uint8_t buf[] = { + 'I', 'D', '3', /*ver*/ 4, 0, /*flags*/ 0, /*size*/ 0, 0, 0, 53, + + 'T', 'I', 'T', '2', /*size*/ 0, 0, 0, 12, /*flags*/ 0, 0, /*utf8*/ 3, + 'T', 'e', 's', 't', ' ', 'T', 'i', 't', 'l', 'e', 0, + + 'P', 'R', 'I', 'V', /* size */ 0, 0, 0, 21, /*flags*/ 0, 0, + 't', 'e', 's', 't', 'o', 'w', 'n', 'e', 'r', 0, + 't', 'e', 's', 't', 'd', 'a', 't', 'a', /*some extra data*/ 0, 1, 2, + }; + int failures = 0; + AVIOContext io; + AVDictionary *meta = NULL; + ID3v2ExtraMeta *extra_meta = NULL; + + ffio_init_context(&io, buf, sizeof(buf), 0, NULL, NULL, NULL, NULL); + + ff_id3v2_read_dict(&io, &meta, ID3v2_DEFAULT_MAGIC, &extra_meta); + ff_id3v2_parse_priv_dict(&meta, &extra_meta); + + failures += tag_equal(meta, "title", "Test Title"); + failures += tag_equal(meta, ID3v2_PRIV_METADATA_PREFIX "testowner", + "testdata\\x00\\x01\\x02"); + + av_dict_free(&meta); + ff_id3v2_free_extra_meta(&extra_meta); + return failures; +} + +static int check_header(const uint8_t *buf, size_t buflen, const char * magic, int version, int size) +{ + int failures = 0; + int i, s; + if (buflen < 10) { + fprintf(stderr, "id3v2 wrote short header\n"); + return 1; + } + if (memcmp(buf, magic, 3)) { + failures++; + fprintf(stderr, "id3v2 wrote magic '%.3s'; expected '%s'\n", magic, buf); + } + if (buf[3] != version || buf[4] != 0) { + failures++; + fprintf(stderr, "id3v2 wrote version %d.%d; expected 4.0\n", buf[3], buf[4]); + } + if (buf[5]) { + failures++; + fprintf(stderr, "id3v2 wrote flags %#x; expected 0x0\n", buf[5]); + } + for (i = 6, s = 0; i < 10; i++) { + if (buf[i] & 0x80) { + failures++; + fprintf(stderr, "id3v2 wrote invalid synchsafe integer in header size\n"); + } + s |= (buf[i] & 0x7f) << ((9 - i) * 8); + } + if (s != size) { + failures++; + fprintf(stderr, "id3v2 wrote tag size %d; expected %d\n", s, size); + } + return failures; +} + +static int check_frame(const char *id, const uint8_t *buf, int buflen, const uint8_t *expected, int expectedlen) +{ + int failures = 0, s, i; + if (buflen < 10) { + fprintf(stderr, "id3v2 wrote short frame header\n"); + return 1; + } + if (strncmp(id, buf, 4)) { + failures++; + fprintf(stderr, "id3v2 wrote frame id %.4s; expected %s\n", buf, id); + } + for (i = 4, s = 0; i < 8; i++) { + if (buf[i] & 0x80) { + failures++; + fprintf(stderr, "id3v2 wrote invalid synchsafe integer in header size\n"); + } + s |= (buf[i] & 0x7f) << ((7 - i) * 8); + } + if (buf[8] || buf[9]) { + failures++; + fprintf(stderr, "id3v2 wrote frame flags %#x; expected 0x0\n", (buf[8] << 8) | buf[9]); + } + if (s != expectedlen) { + failures++; + fprintf(stderr, "id3v2 wrote frame size %d; expected %d\n", s, expectedlen); + } + if (buflen - 10 < expectedlen) { + failures++; + fprintf(stderr, "id3v2 wrote short frame\n"); + } else if (memcmp(&buf[10], expected, expectedlen)) { + failures++; + fprintf(stderr, "id3v2 wrote unexpected frame data\n"); + } + return failures; +} + +static int test_id3v2_write() +{ + int failures = 0, ret, len; + uint8_t *buf; + const uint8_t tit2_data[] = { + 3, 'T', 'e', 's', 't', ' ', 'T', 'i', 't', 'l', 'e', 0}; + const uint8_t priv_data[] = { + 't', 'e', 's', 't', 'o', 'w', 'n', 'e', 'r', 0, + 't', 'e', 's', 't', 'd', 'a', 't', 'a', 0, 1, 2 + }; + AVFormatContext *s = avformat_alloc_context(); + + avio_open_dyn_buf(&s->pb); + av_dict_set(&s->metadata, "title", "Test Title", 0); + av_dict_set(&s->metadata, ID3v2_PRIV_METADATA_PREFIX "testowner", + "testdata\\x00\\x01\\x02", 0); + + if ((ret = ff_id3v2_write_simple(s, 4, ID3v2_DEFAULT_MAGIC))) { + failures++; + fprintf(stderr, "writing id3v2 tag return unexpected error %d\n", ret); + } + + len = avio_close_dyn_buf(s->pb, &buf); + avformat_free_context(s); + + if (len < 63) { + failures++; + fprintf(stderr, "id3v2 wrote %d bytes; expected 63\n", ret); + } + + failures += check_header(buf, len, ID3v2_DEFAULT_MAGIC, 4, len - 10); + failures += check_frame("TIT2", &buf[10], len - 10, tit2_data, sizeof(tit2_data)); + failures += check_frame("PRIV", &buf[32], len - 32, priv_data, sizeof(priv_data)); + + return failures; +} + +static int test_id3v2_write_bad_priv() +{ + const char * values[] = { + "test\\xXXdata", "testdata\\xXX", "testdata\\xX", "testdata\\x", + "testdata\\x1", NULL + }; + int i, failures = 0, ret; + AVFormatContext *s = avformat_alloc_context(); + + for (i = 0; values[i]; i++) { + avio_open_dyn_buf(&s->pb); + av_dict_set(&s->metadata, ID3v2_PRIV_METADATA_PREFIX "testowner", values[i], 0); + ret = ff_id3v2_write_simple(s, 4, ID3v2_DEFAULT_MAGIC); + if (ret != AVERROR(EINVAL)) { + failures++; + fprintf(stderr, "writing id3v2 data returned %d; expected AVERROR(EINVAL)\n", ret); + } + ffio_free_dyn_buf(&s->pb); + av_dict_free(&s->metadata); + } + + avformat_free_context(s); + + return failures; +} + +int main(int argc, char **argv) +{ + int failures = 0, i; + + #define ID3v2_TEST(x) {x, #x} + + struct { int(*test)(void); const char * name; } tests[] = { + ID3v2_TEST(test_id3v2_read), + ID3v2_TEST(test_id3v2_write), + ID3v2_TEST(test_id3v2_write_bad_priv), + {NULL, NULL}, + }; + + for (i = 0; tests[i].test; i++) { + int f = tests[i].test(); + printf("%s %s\n", tests[i].name, f ? "failed" : "passed"); + failures += f; + } + + printf("Ran %d tests; %d failed\n", i, failures); + + return failures; +} \ No newline at end of file diff --git a/tests/fate/libavformat.mak b/tests/fate/libavformat.mak index cf1ba189dd..de59ab5ee5 100644 --- a/tests/fate/libavformat.mak +++ b/tests/fate/libavformat.mak @@ -2,6 +2,10 @@ #fate-async: libavformat/tests/async$(EXESUF) #fate-async: CMD = run libavformat/tests/async +FATE_LIBAVFORMAT-yes += fate-id3v2 +fate-id3v2: libavformat/tests/id3v2$(EXESUF) +fate-id3v2: CMD = run libavformat/tests/id3v2 + FATE_LIBAVFORMAT-$(CONFIG_NETWORK) += fate-noproxy fate-noproxy: libavformat/tests/noproxy$(EXESUF) fate-noproxy: CMD = run libavformat/tests/noproxy diff --git a/tests/ref/fate/id3v2 b/tests/ref/fate/id3v2 new file mode 100644 index 0000000000..594e09cfbd --- /dev/null +++ b/tests/ref/fate/id3v2 @@ -0,0 +1,4 @@ +test_id3v2_read passed +test_id3v2_write passed +test_id3v2_write_bad_priv passed +Ran 3 tests; 0 failed