Tag Parser 12.1.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
Loading...
Searching...
No Matches
testfilecheck.cpp
Go to the documentation of this file.
1#include "../caseinsensitivecomparer.h"
2
3#include <c++utilities/conversion/stringbuilder.h>
4#include <c++utilities/tests/testutils.h>
5
6#include <cppunit/TestFixture.h>
7#include <cppunit/extensions/HelperMacros.h>
8
9#include <openssl/evp.h>
10#include <openssl/sha.h>
11
12#include <fstream>
13
14using namespace std;
15using namespace CppUtilities;
16using namespace TagParser;
17using namespace CPPUNIT_NS;
18
23 char checksum[65];
24 bool operator==(const Sha256Checksum &other) const;
25};
26
31{
32 for (const char *i1 = checksum, *i2 = other.checksum, *end = checksum + sizeof(checksum); i1 != end; ++i1, ++i2) {
33 if (CaseInsensitiveCharComparer::toLower(static_cast<unsigned char>(*i1))
34 != CaseInsensitiveCharComparer::toLower(static_cast<unsigned char>(*i2))) {
35 return false;
36 }
37 }
38 return true;
39}
40
41ostream &operator<<(ostream &os, const Sha256Checksum &checksum)
42{
43 os << checksum.checksum;
44 return os;
45}
46
51struct TestFile {
52 const char *path;
55 void verifyChecksum() const;
56} const testFiles[] = {
57 { "matroska_wave1/logo3_256x256.png", { "810b9172607e281d9a3969018c7d6521de240cc3688fecf598444e666aa6b4dc" } },
58 { "matroska_wave1/test1.mkv", { "0996a309ff2095910b9d30d5253b044d637154297ddf7d0bda7f3adedf5addc1" } },
59 { "matroska_wave1/test2.mkv", { "5b53d306e56f9bda6e80c3fbd9f3ccd20cc885770449d1fc0b5bec35c71d61e2" } },
60 { "matroska_wave1/test3.mkv", { "1722b0d93a6ef1a14dd513bd031cd5901c233b45aa3e3c87be0b0d7348d7d1b5" } },
61 { "matroska_wave1/test4.mkv", { "43df750a2a01a37949791b717051b41522081a266b71d113be4b713063843699" } },
62 { "matroska_wave1/test5.mkv", { "92acdc33bb0b5d7a4d9b0d6ca792230a78c786a30179dc9999cee41c28642842" } },
63 { "matroska_wave1/test6.mkv", { "7cad84b434116e023d340dd584ac833b93f03fb1bd7ea2727fa45de50af0abb9" } },
64 { "matroska_wave1/test7.mkv", { "95b21c92ad5a4fe00914ff5009e2a64f12fd4c5fb9cb1c3c888ab50bf0ffe483" } },
65 { "matroska_wave1/test8.mkv", { "9dddcd1550b814dae44d62e2b9f27c0eca31d5e190df2220cbf7492e3d6c63da" } },
66 { "mtx-test-data/mkv/handbrake-chapters-2.mkv", { "eccc55f3b59a77086b3ffb914525d312c7886eae34e3933352dea2f6f6a1974c" } },
67 { "mtx-test-data/mkv/tags.mkv", { "4330019afc3d846600c1ded38158fcac081297f4e56c749251c236f4871e0287" } },
68 { "mkv/nested-tags.xml", { "85cfcc94920f114e52fd1aa3df24706cd2710626e065a2c8c55dd209ec8dc8ce" } },
69 { "mp4/test1.m4a", { "4f16e0a22525bd13ba859431406d7f5991e0b4f155c51e10e5f32b0c97034b36" } },
70 { "mp4/android-8.1-camera-recoding.mp4", { "e7c5624872de1c9c2fb52dd954cef53adc337a6ba88342ff516dde5d4ef374dc" } },
71 { "mtx-test-data/aac/he-aacv2-ps.m4a", { "be54be0ae45b0184583ced8a84a881a1652a449feb7f6a917e11f60efabb68ac" } },
72 { "mtx-test-data/alac/othertest-itunes.m4a", { "5e9c64cde00902211533fbe38aaa67ef5f79a945e1d717951b78b4bbf9ff84e8" } },
73 { "mtx-test-data/mp3/id3-tag-and-xing-header.mp3", { "4a9187b05dc74d32e5a3de53494fde9db8c6c25d46082f86de6f424ad28daacf" } },
74 { "mtx-test-data/mp4/10-DanseMacabreOp.40.m4a", { "30c915d5656de049d66fd70b0966a33faf038af42365a2bb973e5c2fc0ba2038" } },
75 { "mtx-test-data/mp4/1080p-DTS-HD-7.1.mp4", { "fbf929bf8300fc6e53c5c5b7fde4ed2a427fef2d4fd093511c672083039abbf1" } },
76 { "mtx-test-data/mp4/dash/dragon-age-inquisition-H1LkM6IVlm4-video.mp4", { "864891f4510f3fa9c49c19e671171cec08ceb331362cf7161419b957be090d47" } },
77 { "mtx-test-data/ogg/qt4dance_medium.ogg", { "0b5429da9713be171c6ae0da69621261e8d5ddc9db3da872e5ade1a1c883decd" } },
78 { "mtx-test-data/opus/v-opus.ogg", { "e12adece4dbcccf2471b61c3ebd7c6576dee351d85809ab6f01d6f324d65b417" } },
79 { "misc/multiple_id3v2_4_values.mp3", { "da012a41213cdc49b2afe1457625d8baced1a64e2351f17b520bf82c6bfe4e03" } },
80 { "ogg/noise-without-cover.opus", { "ff578894c0c47aed4cc41ae94dee2886fe2c556593e44f731135f47bca870464" } },
81 { "ogg/noise-broken-segment-termination.opus", { "12835cf12b5b9fa70c239ae05e9d5bb768e715a2d61ef6301ed4af673088de45" } },
82 { "ogg/example-cover.png", { "897e1a2d0cfb79c1fe5068108bb34610c3758bd0b9a7e90c1702c4e6972e0801" } },
83};
84
85struct EvpMdCtx {
87 : handle(EVP_MD_CTX_new())
88 {
89 }
91 {
92 if (handle) {
93 EVP_MD_CTX_free(handle);
94 }
95 }
96 EVP_MD_CTX *handle;
97};
98
103{
104 // init sha256 hashing
105 const auto mdctx = EvpMdCtx();
106 if (!mdctx.handle) {
107 throw std::runtime_error("Unable to create EVP context.");
108 }
109 if (EVP_DigestInit_ex(mdctx.handle, EVP_sha256(), nullptr) != 1) {
110 throw std::runtime_error("Unable to init SHA256-EVP context.");
111 }
112
113 // read and hash file
114 {
115 ifstream file;
116 file.exceptions(ios_base::eofbit | ios_base::failbit | ios_base::badbit);
117 file.open(testFilePath(path), ios_base::in | ios_base::binary);
118
119 char readBuffer[4096];
120 try {
121 for (;;) {
122 file.read(readBuffer, sizeof(readBuffer));
123 if (EVP_DigestUpdate(mdctx.handle, readBuffer, static_cast<std::size_t>(file.gcount())) != 1) {
124 throw std::runtime_error("Unable to update SHA256-EVP.");
125 }
126 }
127 } catch (const std::ios_base::failure &) {
128 if (file.eof() && !file.bad()) {
129 if (EVP_DigestUpdate(mdctx.handle, readBuffer, static_cast<std::size_t>(file.gcount())) != 1) {
130 throw std::runtime_error("Unable to update SHA256-EVP.");
131 }
132 } else {
133 throw;
134 }
135 }
136 }
137
138 // compute final hash
139 unsigned char hash[SHA256_DIGEST_LENGTH];
140 auto length = static_cast<unsigned int>(SHA256_DIGEST_LENGTH);
141 if (EVP_DigestFinal_ex(mdctx.handle, hash, &length) != 1) {
142 throw std::runtime_error("Unable to finalize SHA256-EVP.");
143 }
144
145 // convert to "hex string"
146 Sha256Checksum hexString;
147 char *hexStringIterator = hexString.checksum;
148 for (unsigned char hashNumber : hash) {
149 const string digits = numberToString(hashNumber, static_cast<unsigned char>(16));
150 *(hexStringIterator++) = digits.size() < 2 ? '0' : digits.front();
151 *(hexStringIterator++) = digits.back();
152 }
153 *hexStringIterator = '\0';
154 return hexString;
155}
156
161{
162 CPPUNIT_ASSERT_EQUAL_MESSAGE(argsToString("integrity of testfile \"", path, '\"'), expectedSha256sum, computeSha256Sum());
163}
164
169class TestFileCheck : public TestFixture {
170 CPPUNIT_TEST_SUITE(TestFileCheck);
171 CPPUNIT_TEST(verifyChecksums);
172 CPPUNIT_TEST_SUITE_END();
173
174private:
175 void verifyChecksums();
176};
177
179
180void TestFileCheck::verifyChecksums()
181{
182 for (const TestFile &testFile : testFiles) {
183 testFile.verifyChecksum();
184 }
185}
The TestFileCheck class verifies integrity of all testfiles used in the testsuite of tagparser or tag...
std::ostream & operator<<(std::ostream &os, const TagParser::TagTextEncoding &encoding)
Prints a TagTextEncoding to enable CPPUNIT_ASSERT_EQUAL for tag values.
Definition helper.cpp:8
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10
EVP_MD_CTX * handle
The Sha256Checksum struct holds the "hex string representation" of a SHA-256 checksum.
bool operator==(const Sha256Checksum &other) const
Returns whether the current instance equals other ignoring the case.
static constexpr unsigned char toLower(const unsigned char c)
The TestFile struct holds the path (relative to testfile dir) and checksum of a test file.
Sha256Checksum computeSha256Sum() const
Computes the SHA-256 checksums for the file using OpenSSL.
const char * path
void verifyChecksum() const
Checks whether the expected SHA-256 checksum matches the actual checksum.
Sha256Checksum expectedSha256sum
struct TestFile testFiles[]
CPPUNIT_TEST_SUITE_REGISTRATION(TestFileCheck)