From 515a94f0fff5f6452fed4bd0a79ec6bfac07fc9e Mon Sep 17 00:00:00 2001 From: Chris Needham Date: Sat, 3 Aug 2019 13:08:29 +0100 Subject: [PATCH] Fixed processing files to/from stdio - Duration calculation - More consistent status messages - Error reporting if unable to perform requested conversion - Test cases for all combinations of file and stdio input and output - Prevent illegal seek error writing WAV output - Remove code duplication in WaveformBuffer --- CMakeLists.txt | 2 +- src/AudioProcessor.h | 4 +- src/DurationCalculator.cpp | 14 +- src/DurationCalculator.h | 4 +- src/FileUtil.cpp | 14 ++ src/FileUtil.h | 2 + src/GdImageRenderer.cpp | 13 +- src/Mp3AudioFileReader.cpp | 23 +- src/OptionHandler.cpp | 15 +- src/SndFileAudioFileReader.cpp | 27 ++- src/WavFileWriter.cpp | 26 ++- src/WavFileWriter.h | 4 +- src/WaveformBuffer.cpp | 141 +++++------- src/WaveformGenerator.cpp | 9 +- src/WaveformGenerator.h | 4 +- test/GdImageRendererTest.cpp | 8 +- test/Mp3AudioFileReaderTest.cpp | 10 + test/OptionHandlerTest.cpp | 326 ++++++++++++++++++++-------- test/SndFileAudioFileReaderTest.cpp | 4 + test/WaveformBufferTest.cpp | 10 +- test/mocks/MockAudioProcessor.h | 3 +- 21 files changed, 438 insertions(+), 225 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dcae86a..c9b386c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ #------------------------------------------------------------------------------- # -# Copyright 2013-2018 BBC Research and Development +# Copyright 2013-2019 BBC Research and Development # # This file is part of Audio Waveform Image Generator. # diff --git a/src/AudioProcessor.h b/src/AudioProcessor.h index ae10486..fd7d31c 100644 --- a/src/AudioProcessor.h +++ b/src/AudioProcessor.h @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// Copyright 2013-2018 BBC Research and Development +// Copyright 2013-2019 BBC Research and Development // // Author: Chris Needham // @@ -38,6 +38,8 @@ class AudioProcessor int buffer_size ) = 0; + virtual bool shouldContinue() const = 0; + virtual bool process( const short* input_buffer, int input_frame_count diff --git a/src/DurationCalculator.cpp b/src/DurationCalculator.cpp index cd46823..e4bff6b 100644 --- a/src/DurationCalculator.cpp +++ b/src/DurationCalculator.cpp @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// Copyright 2018 BBC Research and Development +// Copyright 2019 BBC Research and Development // // Author: Chris Needham // @@ -38,8 +38,16 @@ bool DurationCalculator::init(int sample_rate, int /* channels */, long frame_co sample_rate_ = sample_rate; frame_count_ = frame_count; - // Only continue processing if we don't now know the length - return frame_count == 0; + return true; +} + +//------------------------------------------------------------------------------ + +bool DurationCalculator::shouldContinue() const +{ + // Only continue processing if we don't now know the length from the + // information passed to init() + return frame_count_ == 0; } //------------------------------------------------------------------------------ diff --git a/src/DurationCalculator.h b/src/DurationCalculator.h index aba7a67..8406494 100644 --- a/src/DurationCalculator.h +++ b/src/DurationCalculator.h @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// Copyright 2018 BBC Research and Development +// Copyright 2019 BBC Research and Development // // Author: Chris Needham // @@ -42,6 +42,8 @@ class DurationCalculator : public AudioProcessor int buffer_size ); + virtual bool shouldContinue() const; + virtual bool process( const short* input_buffer, int input_frame_count diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp index 4bb74ab..5e8ed7c 100644 --- a/src/FileUtil.cpp +++ b/src/FileUtil.cpp @@ -43,6 +43,20 @@ bool isStdioFilename(const char* filename) //------------------------------------------------------------------------------ +const char* getInputFilename(const char* filename) +{ + return FileUtil::isStdioFilename(filename) ? "(stdin)" : filename; +} + +//------------------------------------------------------------------------------ + +const char* getOutputFilename(const char* filename) +{ + return FileUtil::isStdioFilename(filename) ? "(stdout)" : filename; +} + +//------------------------------------------------------------------------------ + } // namespace FileUtil //------------------------------------------------------------------------------ diff --git a/src/FileUtil.h b/src/FileUtil.h index f379770..44ea532 100644 --- a/src/FileUtil.h +++ b/src/FileUtil.h @@ -28,6 +28,8 @@ namespace FileUtil { bool isStdioFilename(const char* filename); + const char* getInputFilename(const char* filename); + const char* getOutputFilename(const char* filename); } //------------------------------------------------------------------------------ diff --git a/src/GdImageRenderer.cpp b/src/GdImageRenderer.cpp index da54264..4fe098c 100644 --- a/src/GdImageRenderer.cpp +++ b/src/GdImageRenderer.cpp @@ -470,16 +470,17 @@ bool GdImageRenderer::saveAsPng( else { output_file = fopen(filename, "wb"); - if (output_file != nullptr) { - error_stream << "Writing PNG file: " << filename << '\n'; - } - else { - error_stream << "Failed to write PNG file: " << filename << '\n' - << strerror(errno) << '\n'; + if (output_file == nullptr) { + error_stream << "Failed to write PNG file: " + << filename << '\n' + << strerror(errno) << '\n'; return false; } } + error_stream << "Output file: " + << FileUtil::getOutputFilename(filename) << '\n'; + gdImagePngEx(image_, output_file, compression_level); if (output_file != stdout) { diff --git a/src/Mp3AudioFileReader.cpp b/src/Mp3AudioFileReader.cpp index be63b7e..9ded93c 100644 --- a/src/Mp3AudioFileReader.cpp +++ b/src/Mp3AudioFileReader.cpp @@ -360,15 +360,17 @@ bool Mp3AudioFileReader::open(const char* filename, bool show_info) if (!getFileSize()) { error_stream << "Failed to determine file size: " << filename << '\n' - << strerror(errno); + << strerror(errno) << '\n'; } - error_stream << "Input file: " << filename << '\n'; } + error_stream << "Input file: " + << FileUtil::getInputFilename(filename) << '\n'; + if (!skipId3Tags()) { - error_stream << "Failed to read file: " << filename << '\n' - << strerror(errno); + error_stream << "Failed to read file: " + << strerror(errno) << '\n'; return false; } @@ -709,6 +711,11 @@ bool Mp3AudioFileReader::run(AudioProcessor& processor) break; } + if (!processor.shouldContinue()) { + status = STATUS_PROCESS_ERROR; + break; + } + showProgress(0, file_size_); started = true; @@ -761,9 +768,7 @@ bool Mp3AudioFileReader::run(AudioProcessor& processor) const int frames = OUTPUT_BUFFER_SIZE / channels; - bool success = processor.process(output_buffer, frames); - - if (!success) { + if (!processor.process(output_buffer, frames)) { status = STATUS_PROCESS_ERROR; break; } @@ -779,9 +784,7 @@ bool Mp3AudioFileReader::run(AudioProcessor& processor) if (output_ptr != output_buffer && status != STATUS_PROCESS_ERROR) { int buffer_size = static_cast(output_ptr - output_buffer); - bool success = processor.process(output_buffer, buffer_size / channels); - - if (!success) { + if (!processor.process(output_buffer, buffer_size / channels)) { status = STATUS_PROCESS_ERROR; } } diff --git a/src/OptionHandler.cpp b/src/OptionHandler.cpp index e30d311..899a2f8 100644 --- a/src/OptionHandler.cpp +++ b/src/OptionHandler.cpp @@ -27,6 +27,7 @@ #include "DurationCalculator.h" #include "Error.h" #include "FileFormat.h" +#include "FileUtil.h" #include "GdImageRenderer.h" #include "Mp3AudioFileReader.h" #include "Options.h" @@ -182,6 +183,12 @@ static std::pair getDuration( error_stream << "Duration: " << duration << " seconds\n"; + if (FileUtil::isStdioFilename(input_filename.string().c_str())) { + if (fseek(stdin, 0, SEEK_SET) != 0) { + return std::make_pair(false, 0); + } + } + return std::make_pair(true, duration); } @@ -362,6 +369,7 @@ bool OptionHandler::renderWaveformImage( auto result = getDuration(input_filename, input_format); if (!result.first) { + error_stream << "Failed to get audio duration\n"; return false; } @@ -513,8 +521,11 @@ bool OptionHandler::run(const Options& options) ); } else { - error_stream << "Can't generate " << output_filename - << " from " << input_filename << '\n'; + error_stream << "Can't generate " + << FileFormat::toString(output_format) + << " format output from " + << FileFormat::toString(input_format) + << " format input\n"; success = false; } } diff --git a/src/SndFileAudioFileReader.cpp b/src/SndFileAudioFileReader.cpp index 72a2433..cb10775 100644 --- a/src/SndFileAudioFileReader.cpp +++ b/src/SndFileAudioFileReader.cpp @@ -69,14 +69,11 @@ bool SndFileAudioFileReader::open(const char* input_filename, bool show_info) if (FileUtil::isStdioFilename(input_filename)) { input_file_ = sf_open_fd(fileno(stdin), SFM_READ, &info_, 0); - if (input_file_ != nullptr) { - if (show_info) { - showInfo(error_stream, info_); - } - } - else { + if (input_file_ == nullptr) { error_stream << "Failed to read input: " << sf_strerror(nullptr) << '\n'; + + return false; } } else { @@ -85,17 +82,19 @@ bool SndFileAudioFileReader::open(const char* input_filename, bool show_info) if (input_file_ == nullptr) { error_stream << "Failed to read file: " << input_filename << '\n' << sf_strerror(nullptr) << '\n'; - } - else { - error_stream << "Input file: " << input_filename << '\n'; - if (show_info) { - showInfo(error_stream, info_); - } + return false; } } - return input_file_ != nullptr; + error_stream << "Input file: " + << FileUtil::getInputFilename(input_filename) << '\n'; + + if (show_info) { + showInfo(error_stream, info_); + } + + return true; } //------------------------------------------------------------------------------ @@ -135,7 +134,7 @@ bool SndFileAudioFileReader::run(AudioProcessor& processor) success = processor.init(info_.samplerate, info_.channels, info_.frames, BUFFER_SIZE); - if (success) { + if (success && processor.shouldContinue()) { showProgress(0, info_.frames); while (success && frames_read == frames_to_read) { diff --git a/src/WavFileWriter.cpp b/src/WavFileWriter.cpp index 90393bb..f39fded 100644 --- a/src/WavFileWriter.cpp +++ b/src/WavFileWriter.cpp @@ -22,11 +22,13 @@ //------------------------------------------------------------------------------ #include "WavFileWriter.h" +#include "FileUtil.h" #include "Streams.h" #include #include #include +#include //------------------------------------------------------------------------------ @@ -53,7 +55,8 @@ bool WavFileWriter::init( const long /* frame_count */, const int buffer_size) { - error_stream << "Output file: " << output_filename_ << '\n'; + error_stream << "Output file: " + << FileUtil::getOutputFilename(output_filename_.c_str()) << '\n'; channels_ = channels; buffer_size_ = buffer_size; @@ -67,7 +70,19 @@ bool WavFileWriter::init( info.channels = channels; info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - output_file_ = sf_open(output_filename_.c_str(), SFM_WRITE, &info); + if (FileUtil::isStdioFilename(output_filename_.c_str())) { + // Prevent illegal seek error + if (isatty(fileno(stdout))) { + error_stream << "Cannot write WAV audio to the terminal\n"; + return false; + } + else { + output_file_ = sf_open_fd(fileno(stdout), SFM_WRITE, &info, 0); + } + } + else { + output_file_ = sf_open(output_filename_.c_str(), SFM_WRITE, &info); + } if (output_file_ == nullptr) { error_stream << sf_strerror(output_file_) << '\n'; @@ -78,6 +93,13 @@ bool WavFileWriter::init( //------------------------------------------------------------------------------ +bool WavFileWriter::shouldContinue() const +{ + return true; +} + +//------------------------------------------------------------------------------ + void WavFileWriter::close() { if (output_file_ != nullptr) { diff --git a/src/WavFileWriter.h b/src/WavFileWriter.h index 4b06d78..3ca2846 100644 --- a/src/WavFileWriter.h +++ b/src/WavFileWriter.h @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// Copyright 2013-2018 BBC Research and Development +// Copyright 2013-2019 BBC Research and Development // // Author: Chris Needham // @@ -52,6 +52,8 @@ class WavFileWriter : public AudioProcessor int buffer_size ); + virtual bool shouldContinue() const; + virtual bool process( const short* input_buffer, int input_frame_count diff --git a/src/WaveformBuffer.cpp b/src/WaveformBuffer.cpp index 43c4133..22d1ac8 100644 --- a/src/WaveformBuffer.cpp +++ b/src/WaveformBuffer.cpp @@ -146,16 +146,28 @@ bool WaveformBuffer::load(const char* filename) bool success = true; std::ifstream file; - file.exceptions(std::ios::badbit | std::ios::failbit); + std::istream* input; uint32_t size = 0; try { - file.open(filename, std::ios::in | std::ios::binary); + if (FileUtil::isStdioFilename(filename)) { + input = &std::cin; + } + else { + input = &file; + } + + input->exceptions(std::ios::badbit | std::ios::failbit); - error_stream << "Reading waveform data file: " << filename << '\n'; + if (!FileUtil::isStdioFilename(filename)) { + file.open(filename, std::ios::in | std::ios::binary); + } + + error_stream << "Input file: " + << FileUtil::getInputFilename(filename) << '\n'; - const int32_t version = readInt32(file); + const int32_t version = readInt32(*input); if (version != 1 && version != 2) { reportReadError( @@ -166,15 +178,15 @@ bool WaveformBuffer::load(const char* filename) return false; } - const uint32_t flags = readUInt32(file); + const uint32_t flags = readUInt32(*input); - sample_rate_ = readInt32(file); - samples_per_pixel_ = readInt32(file); + sample_rate_ = readInt32(*input); + samples_per_pixel_ = readInt32(*input); - size = readUInt32(file); + size = readUInt32(*input); if (version == 2) { - channels_ = readInt32(file); + channels_ = readInt32(*input); } else { channels_ = 1; @@ -193,10 +205,10 @@ bool WaveformBuffer::load(const char* filename) bits_ = 8; for (uint32_t i = 0; i < size * channels_; ++i) { - int8_t min_value = readInt8(file); + int8_t min_value = readInt8(*input); data_.push_back(static_cast(min_value * 256)); - int8_t max_value = readInt8(file); + int8_t max_value = readInt8(*input); data_.push_back(static_cast(max_value * 256)); } } @@ -204,10 +216,10 @@ bool WaveformBuffer::load(const char* filename) bits_ = 16; for (uint32_t i = 0; i < size * channels_; ++i) { - int16_t min_value = readInt16(file); + int16_t min_value = readInt16(*input); data_.push_back(min_value); - int16_t max_value = readInt16(file); + int16_t max_value = readInt16(*input); data_.push_back(max_value); } } @@ -267,13 +279,9 @@ bool WaveformBuffer::load(const char* filename) //------------------------------------------------------------------------------ -bool WaveformBuffer::save(const char* filename, const int bits) const +template +static bool openOutputStream(const char* filename, bool binary, Writer writer) { - if (bits != 8 && bits != 16) { - error_stream << "Invalid bits: must be either 8 or 16\n"; - return false; - } - bool success = true; std::ofstream file; @@ -290,15 +298,19 @@ bool WaveformBuffer::save(const char* filename, const int bits) const output->exceptions(std::ios::badbit | std::ios::failbit); if (!FileUtil::isStdioFilename(filename)) { - file.open(filename); + std::ios::openmode openMode = std::ios::out; + + if (binary) { + openMode |= std::ios::binary; + } - error_stream << "Writing output file: " << filename << '\n'; + file.open(filename, openMode); } - error_stream << "Resolution: " << bits << " bits" - << "\nChannels: " << channels_ << std::endl; + error_stream << "Output file: " + << FileUtil::getOutputFilename(filename) << '\n'; - save(*output, bits); + writer(*output); } catch (const std::exception&) { reportWriteError(filename, strerror(errno)); @@ -310,6 +322,23 @@ bool WaveformBuffer::save(const char* filename, const int bits) const //------------------------------------------------------------------------------ +bool WaveformBuffer::save(const char* filename, const int bits) const +{ + if (bits != 8 && bits != 16) { + error_stream << "Invalid bits: must be either 8 or 16\n"; + return false; + } + + return openOutputStream(filename, true, [this, bits](std::ostream& output) { + error_stream << "Resolution: " << bits << " bits\n" + << "Channels: " << channels_ << std::endl; + + save(output, bits); + }); +} + +//------------------------------------------------------------------------------ + void WaveformBuffer::save(std::ostream& stream, int bits) const { const int32_t version = channels_ == 1 ? 1 : 2; @@ -358,35 +387,9 @@ bool WaveformBuffer::saveAsText(const char* filename, int bits) const return false; } - bool success = true; - - std::ofstream file; - std::ostream* output; - - try { - if (FileUtil::isStdioFilename(filename)) { - output = &std::cout; - } - else { - output = &file; - } - - output->exceptions(std::ios::badbit | std::ios::failbit); - - if (!FileUtil::isStdioFilename(filename)) { - file.open(filename); - - error_stream << "Writing output file: " << filename << '\n'; - } - - saveAsText(*output, bits); - } - catch (const std::exception&) { - reportWriteError(filename, strerror(errno)); - success = false; - } - - return success; + return openOutputStream(filename, false, [this, bits](std::ostream& output) { + saveAsText(output, bits); + }); } //------------------------------------------------------------------------------ @@ -436,35 +439,9 @@ bool WaveformBuffer::saveAsJson(const char* filename, const int bits) const return false; } - bool success = true; - - std::ofstream file; - std::ostream* output; - - try { - if (FileUtil::isStdioFilename(filename)) { - output = &std::cout; - } - else { - output = &file; - } - - output->exceptions(std::ios::badbit | std::ios::failbit); - - if (!FileUtil::isStdioFilename(filename)) { - file.open(filename); - - error_stream << "Writing output file: " << filename << '\n'; - } - - saveAsJson(*output, bits); - } - catch (const std::exception&) { - reportWriteError(filename, strerror(errno)); - success = false; - } - - return success; + return openOutputStream(filename, false, [this, bits](std::ostream& output) { + saveAsJson(output, bits); + }); } //------------------------------------------------------------------------------ diff --git a/src/WaveformGenerator.cpp b/src/WaveformGenerator.cpp index cb3dcf0..65ef835 100644 --- a/src/WaveformGenerator.cpp +++ b/src/WaveformGenerator.cpp @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// Copyright 2013-2018 BBC Research and Development +// Copyright 2013-2019 BBC Research and Development // // Author: Chris Needham // @@ -172,6 +172,13 @@ bool WaveformGenerator::init( //------------------------------------------------------------------------------ +bool WaveformGenerator::shouldContinue() const +{ + return true; +} + +//------------------------------------------------------------------------------ + int WaveformGenerator::getSamplesPerPixel() const { return samples_per_pixel_; diff --git a/src/WaveformGenerator.h b/src/WaveformGenerator.h index 0c197a4..940c54b 100644 --- a/src/WaveformGenerator.h +++ b/src/WaveformGenerator.h @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// Copyright 2013-2018 BBC Research and Development +// Copyright 2013-2019 BBC Research and Development // // Author: Chris Needham // @@ -115,6 +115,8 @@ class WaveformGenerator : public AudioProcessor int buffer_size ); + virtual bool shouldContinue() const; + int getSamplesPerPixel() const; virtual bool process( diff --git a/test/GdImageRendererTest.cpp b/test/GdImageRendererTest.cpp index 0e4a232..ba1995f 100644 --- a/test/GdImageRendererTest.cpp +++ b/test/GdImageRendererTest.cpp @@ -100,7 +100,7 @@ void GdImageRendererTest::testImageRendering(bool axis_labels, const std::string TEST_F(GdImageRendererTest, shouldRenderImageWithAxisLabels) { std::string expected_output( - "Reading waveform data file: ../test/data/test_file_stereo_8bit_64spp_wav.dat\n" + "Input file: ../test/data/test_file_stereo_8bit_64spp_wav.dat\n" "Channels: 1\n" "Sample rate: 16000 Hz\n" "Bits: 8\n" @@ -115,7 +115,7 @@ TEST_F(GdImageRendererTest, shouldRenderImageWithAxisLabels) "Buffer size: 1774\n" "Axis labels: yes\n" "Amplitude scale: 1\n" - "Writing PNG file: " + "Output file: " ); testImageRendering(true, expected_output); @@ -126,7 +126,7 @@ TEST_F(GdImageRendererTest, shouldRenderImageWithAxisLabels) TEST_F(GdImageRendererTest, shouldRenderImageWithoutAxisLabels) { std::string expected_output( - "Reading waveform data file: ../test/data/test_file_stereo_8bit_64spp_wav.dat\n" + "Input file: ../test/data/test_file_stereo_8bit_64spp_wav.dat\n" "Channels: 1\n" "Sample rate: 16000 Hz\n" "Bits: 8\n" @@ -141,7 +141,7 @@ TEST_F(GdImageRendererTest, shouldRenderImageWithoutAxisLabels) "Buffer size: 1774\n" "Axis labels: no\n" "Amplitude scale: 1\n" - "Writing PNG file: " + "Output file: " ); testImageRendering(false, expected_output); diff --git a/test/Mp3AudioFileReaderTest.cpp b/test/Mp3AudioFileReaderTest.cpp index 4be9699..5a635f9 100644 --- a/test/Mp3AudioFileReaderTest.cpp +++ b/test/Mp3AudioFileReaderTest.cpp @@ -94,6 +94,7 @@ TEST_F(Mp3AudioFileReaderTest, shouldFailToProcessIfFileNotOpen) StrictMock processor; EXPECT_CALL(processor, init(_, _, _, _)).Times(0); + EXPECT_CALL(processor, shouldContinue()).Times(0); EXPECT_CALL(processor, process(_, _)).Times(0); EXPECT_CALL(processor, done()).Times(0); @@ -116,6 +117,7 @@ TEST_F(Mp3AudioFileReaderTest, shouldProcessStereoMp3File) InSequence sequence; // Calls expected in the order listed below. EXPECT_CALL(processor, init(16000, 2, 0, 8192)).WillOnce(Return(true)); + EXPECT_CALL(processor, shouldContinue()).WillOnce(Return(true)); // TODO: Audacity reports length = 114624 samples (doesn't account for // decoding delay) @@ -157,6 +159,7 @@ TEST_F(Mp3AudioFileReaderTest, shouldProcessMonoMp3File) InSequence sequence; // Calls expected in the order listed below. EXPECT_CALL(processor, init(16000, 1, 0, 8192)).WillOnce(Return(true)); + EXPECT_CALL(processor, shouldContinue()).WillOnce(Return(true)); // Total number of frames: 114095, which is 13 x 8192 frames then 1 x 7599 EXPECT_CALL(processor, process(_, 8192)).Times(13).WillRepeatedly(Return(true)); @@ -196,6 +199,7 @@ TEST_F(Mp3AudioFileReaderTest, shouldProcessMp3FileWithId3Tags) InSequence sequence; // Calls expected in the order listed below. EXPECT_CALL(processor, init(44100, 1, 0, 8192)).WillOnce(Return(true)); + EXPECT_CALL(processor, shouldContinue()).WillOnce(Return(true)); // Total number of frames: 116352, which is 3 x 8192 frames then 1 x 6528 EXPECT_CALL(processor, process(_, 8192)).Times(3).WillRepeatedly(Return(true)); @@ -248,6 +252,11 @@ class DecodingDelayDetector : public AudioProcessor return true; } + virtual bool shouldContinue() const + { + return true; + } + virtual bool process( const short* input_buffer, int input_frame_count) @@ -309,6 +318,7 @@ TEST_F(Mp3AudioFileReaderTest, shouldNotProcessFileMoreThanOnce) InSequence sequence; // Calls expected in the order listed below. EXPECT_CALL(processor, init(16000, 1, 0, 8192)).WillOnce(Return(true)); + EXPECT_CALL(processor, shouldContinue()).WillOnce(Return(true)); // Total number of frames: 114095, which is 13 x 8192 frames then 1 x 7599 EXPECT_CALL(processor, process(_, 8192)).Times(13).WillRepeatedly(Return(true)); diff --git a/test/OptionHandlerTest.cpp b/test/OptionHandlerTest.cpp index b470ed0..2e0c45e 100644 --- a/test/OptionHandlerTest.cpp +++ b/test/OptionHandlerTest.cpp @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// Copyright 2014-2018 BBC Research and Development +// Copyright 2014-2019 BBC Research and Development // // Author: Chris Needham // @@ -22,11 +22,11 @@ //------------------------------------------------------------------------------ #include "OptionHandler.h" +#include "FileFormat.h" #include "Options.h" #include "Array.h" #include "util/FileDeleter.h" #include "util/FileUtil.h" -#include "util/Streams.h" #include "gmock/gmock.h" @@ -38,8 +38,10 @@ using testing::StartsWith; using testing::EndsWith; using testing::Eq; +using testing::Ne; using testing::NotNull; using testing::StrEq; +using testing::HasSubstr; using testing::Test; //------------------------------------------------------------------------------ @@ -50,17 +52,18 @@ class OptionHandlerTest : public Test OptionHandlerTest() { } +}; - protected: - virtual void SetUp() - { - output.str(std::string()); - error.str(std::string()); - } +//------------------------------------------------------------------------------ - virtual void TearDown() - { - } +enum class InputMethod { + StdIn, + File +}; + +enum class OutputMethod { + StdOut, + File }; //------------------------------------------------------------------------------ @@ -111,16 +114,10 @@ static gdImagePtr openImageFile(const boost::filesystem::path& filename) //------------------------------------------------------------------------------ -static void compareImageFiles( - const boost::filesystem::path& test_filename, - const boost::filesystem::path& ref_filename) +static void compareImages( + gdImagePtr test_image, + gdImagePtr ref_image) { - gdImagePtr test_image = openImageFile(test_filename); - ASSERT_THAT(test_image, NotNull()); - - gdImagePtr ref_image = openImageFile(ref_filename); - ASSERT_THAT(ref_image, NotNull()); - const int test_width = gdImageSX(test_image); const int ref_width = gdImageSX(ref_image); @@ -138,6 +135,21 @@ static void compareImageFiles( ASSERT_THAT(test_pixel, Eq(ref_pixel)); } } +} + +//------------------------------------------------------------------------------ + +static void compareImageFiles( + const boost::filesystem::path& test_filename, + const boost::filesystem::path& ref_filename) +{ + gdImagePtr test_image = openImageFile(test_filename); + ASSERT_THAT(test_image, NotNull()); + + gdImagePtr ref_image = openImageFile(ref_filename); + ASSERT_THAT(ref_image, NotNull()); + + compareImages(test_image, ref_image); gdImageDestroy(test_image); gdImageDestroy(ref_image); @@ -146,8 +158,11 @@ static void compareImageFiles( //------------------------------------------------------------------------------ static void runTest( + InputMethod input_method, const char* input_filename, - const char* output_file_ext, + FileFormat::FileFormat input_file_format, + OutputMethod output_method, + FileFormat::FileFormat output_file_format, const std::vector* args, bool should_succeed, const char* reference_filename = nullptr, @@ -157,72 +172,193 @@ static void runTest( input_pathname /= input_filename; const boost::filesystem::path output_pathname = - FileUtil::getTempFilename(output_file_ext); + FileUtil::getTempFilename(FileFormat::getFileExt(output_file_format).c_str()); - // Ensure temporary file is deleted at end of test. - FileDeleter deleter(output_pathname); + const boost::filesystem::path stdout_pathname = + output_method == OutputMethod::File ? + FileUtil::getTempFilename(".txt") : + FileUtil::getTempFilename(FileFormat::getFileExt(output_file_format).c_str()); - std::vector argv{ - "appname", - "-i", input_pathname.c_str(), - "-o", output_pathname.c_str() - }; + const boost::filesystem::path stderr_pathname = + FileUtil::getTempFilename(".txt"); + + // Ensure temporary files are deleted at end of test. + FileDeleter output_file_deleter(output_pathname); + FileDeleter stdout_file_deleter(stdout_pathname); + FileDeleter stderr_file_deleter(stderr_pathname); + + std::string command_line; + + if (input_method == InputMethod::File) { + command_line = "./audiowaveform"; + command_line += " -i " + input_pathname.string(); + } + else { + command_line = "./audiowaveform"; + command_line += " --input-format "; + command_line += FileFormat::toString(input_file_format); + } + + if (output_method == OutputMethod::File) { + command_line += " -o "; + command_line += output_pathname.string(); + } + else { + command_line += " --output-format "; + command_line += FileFormat::toString(output_file_format); + } if (args != nullptr) { - for (const auto& i : *args) { - argv.push_back(i); + for (const auto& arg : *args) { + command_line += " "; + command_line += arg; } } - Options options; + command_line += " 1>"; + command_line += stdout_pathname.string(); + command_line += " 2>"; + command_line += stderr_pathname.string(); + + if (input_method == InputMethod::StdIn) { + command_line += " <"; + command_line += input_pathname.string(); + } - bool success = options.parseCommandLine(static_cast(argv.size()), &argv[0]); - ASSERT_TRUE(success); + const int result = system(command_line.c_str()); + ASSERT_THAT(result, Ne(-1)); + const int exit_status = WEXITSTATUS(result); - OptionHandler option_handler; + std::vector output_buffer = FileUtil::readFile(stdout_pathname.string().c_str()); + std::vector error_buffer = FileUtil::readFile(stderr_pathname.string().c_str()); - success = option_handler.run(options); - ASSERT_THAT(success, Eq(should_succeed)); + std::string output(output_buffer.begin(), output_buffer.end()); + std::string error(error_buffer.begin(), error_buffer.end()); if (should_succeed) { + ASSERT_THAT(exit_status, Eq(0)); + // Check file was created. bool exists = boost::filesystem::is_regular_file(output_pathname); - ASSERT_TRUE(exists); + + if (output_method == OutputMethod::File) { + ASSERT_TRUE(exists); + } + else { + ASSERT_FALSE(exists); + } if (reference_filename) { boost::filesystem::path reference_pathname = "../test/data"; reference_pathname /= reference_filename; - if (strcmp(output_file_ext, ".png") == 0) { - compareImageFiles(output_pathname, reference_pathname); + const boost::filesystem::path& test_pathname = + output_method == OutputMethod::File ? output_pathname : stdout_pathname; + + if (output_file_format == FileFormat::Png) { + compareImageFiles(test_pathname, reference_pathname); } else { - compareFiles(output_pathname, reference_pathname); + compareFiles(test_pathname, reference_pathname); } } // Check no error message was output. - ASSERT_THAT(error.str(), EndsWith("Done\n")); + ASSERT_THAT(error, EndsWith("Done\n")); + + if (output_method == OutputMethod::File) { + // Check nothing was written to standard output. + ASSERT_THAT(output, StrEq("")); + } } else { + ASSERT_THAT(exit_status, Eq(1)); + // Check output file was not created. bool exists = boost::filesystem::is_regular_file(output_pathname); ASSERT_FALSE(exists); // Check error message. - const std::string str = error.str(); - if (error_message != nullptr) { - ASSERT_THAT(str, EndsWith(error_message)); + ASSERT_THAT(error, EndsWith(error_message)); } else { - ASSERT_THAT(str, StartsWith("Can't generate")); - ASSERT_THAT(str, EndsWith("\n")); + const std::string expected( + "Can't generate " + FileFormat::toString(output_file_format) + + " format output from " + FileFormat::toString(input_file_format) + + " format input\n" + ); + + ASSERT_THAT(error, StrEq(expected)); + ASSERT_THAT(error, StartsWith("Can't generate")); + ASSERT_THAT(error, HasSubstr(FileFormat::toString(input_file_format))); + ASSERT_THAT(error, HasSubstr(FileFormat::toString(output_file_format))); + ASSERT_THAT(error, EndsWith("\n")); } + + // Check nothing was written to standard output. + ASSERT_THAT(output, StrEq("")); } +} - // Check nothing was written to standard output. - ASSERT_THAT(output.str(), StrEq("")); +//------------------------------------------------------------------------------ + +static void runTests( + const char* input_filename, + FileFormat::FileFormat input_file_format, + FileFormat::FileFormat output_file_format, + const std::vector* args, + bool should_succeed, + const char* reference_filename = nullptr, + const char* error_message = nullptr) +{ + runTest( + InputMethod::File, + input_filename, + input_file_format, + OutputMethod::File, + output_file_format, + args, + should_succeed, + reference_filename, + error_message + ); + + runTest( + InputMethod::File, + input_filename, + input_file_format, + OutputMethod::StdOut, + output_file_format, + args, + should_succeed, + reference_filename, + error_message + ); + + runTest( + InputMethod::StdIn, + input_filename, + input_file_format, + OutputMethod::File, + output_file_format, + args, + should_succeed, + reference_filename, + error_message + ); + + runTest( + InputMethod::StdIn, + input_filename, + input_file_format, + OutputMethod::StdOut, + output_file_format, + args, + should_succeed, + reference_filename, + error_message + ); } //------------------------------------------------------------------------------ @@ -233,14 +369,15 @@ static void runTest( TEST_F(OptionHandlerTest, shouldConvertMp3ToWavAudio) { - runTest("test_file_mono.mp3", ".wav", nullptr, true, "test_file_mono_converted.wav"); + runTests("test_file_mono.mp3", FileFormat::Mp3, FileFormat::Wav, nullptr, true, "test_file_mono_converted.wav"); } //------------------------------------------------------------------------------ + TEST_F(OptionHandlerTest, shouldNotConvertWavToMp3Audio) { - runTest("test_file_stereo.wav", ".mp3", nullptr, false); + runTests("test_file_stereo.wav", FileFormat::Wav, FileFormat::Mp3, nullptr, false); } //------------------------------------------------------------------------------ @@ -252,7 +389,7 @@ TEST_F(OptionHandlerTest, shouldNotConvertWavToMp3Audio) TEST_F(OptionHandlerTest, shouldGenerateBinaryWaveformDataFromWavAudio) { std::vector args{ "-b", "8", "-z", "64" }; - runTest("test_file_stereo.wav", ".dat", &args, true, "test_file_stereo_8bit_64spp_wav.dat"); + runTests("test_file_stereo.wav", FileFormat::Wav, FileFormat::Dat, &args, true, "test_file_stereo_8bit_64spp_wav.dat"); } //------------------------------------------------------------------------------ @@ -260,7 +397,7 @@ TEST_F(OptionHandlerTest, shouldGenerateBinaryWaveformDataFromWavAudio) TEST_F(OptionHandlerTest, shouldGenerate2ChannelBinaryWaveformDataFromWavAudio) { std::vector args{ "-b", "8", "-z", "64", "--split-channels" }; - runTest("test_file_stereo.wav", ".dat", &args, true, "test_file_2channel_8bit_64spp_wav.dat"); + runTests("test_file_stereo.wav", FileFormat::Wav, FileFormat::Dat, &args, true, "test_file_2channel_8bit_64spp_wav.dat"); } //------------------------------------------------------------------------------ @@ -268,7 +405,7 @@ TEST_F(OptionHandlerTest, shouldGenerate2ChannelBinaryWaveformDataFromWavAudio) TEST_F(OptionHandlerTest, shouldGenerateBinaryWaveformDataFromFloatingPointWavAudio) { std::vector args{ "-b", "8", "-z", "64" }; - runTest("test_file_mono_float32.wav", ".dat", &args, true, "test_file_mono_float32_8bit_64spp.dat"); + runTests("test_file_mono_float32.wav", FileFormat::Wav, FileFormat::Dat, &args, true, "test_file_mono_float32_8bit_64spp.dat"); } //------------------------------------------------------------------------------ @@ -276,7 +413,7 @@ TEST_F(OptionHandlerTest, shouldGenerateBinaryWaveformDataFromFloatingPointWavAu TEST_F(OptionHandlerTest, shouldGenerateBinaryWaveformDataFromMp3Audio) { std::vector args{ "-b", "8", "-z", "64" }; - runTest("test_file_stereo.mp3", ".dat", &args, true, "test_file_stereo_8bit_64spp_mp3.dat"); + runTests("test_file_stereo.mp3", FileFormat::Mp3, FileFormat::Dat, &args, true, "test_file_stereo_8bit_64spp_mp3.dat"); } //------------------------------------------------------------------------------ @@ -284,7 +421,7 @@ TEST_F(OptionHandlerTest, shouldGenerateBinaryWaveformDataFromMp3Audio) TEST_F(OptionHandlerTest, shouldGenerateBinaryWaveformDataFromFlacAudio) { std::vector args{ "-b", "8", "-z", "64" }; - runTest("test_file_stereo.flac", ".dat", &args, true, "test_file_stereo_8bit_64spp_flac.dat"); + runTests("test_file_stereo.flac", FileFormat::Flac, FileFormat::Dat, &args, true, "test_file_stereo_8bit_64spp_flac.dat"); } //------------------------------------------------------------------------------ @@ -292,7 +429,7 @@ TEST_F(OptionHandlerTest, shouldGenerateBinaryWaveformDataFromFlacAudio) TEST_F(OptionHandlerTest, shouldGenerateBinaryWaveformDataFromOggVorbisAudio) { std::vector args{ "-b", "8", "-z", "64" }; - runTest("test_file_stereo.oga", ".dat", &args, true, "test_file_stereo_8bit_64spp_oga.dat"); + runTests("test_file_stereo.oga", FileFormat::Ogg, FileFormat::Dat, &args, true, "test_file_stereo_8bit_64spp_oga.dat"); } //------------------------------------------------------------------------------ @@ -300,7 +437,7 @@ TEST_F(OptionHandlerTest, shouldGenerateBinaryWaveformDataFromOggVorbisAudio) TEST_F(OptionHandlerTest, shouldGenerateJsonWaveformDataFromWavAudio) { std::vector args{ "-b", "8", "-z", "64" }; - runTest("test_file_stereo.wav", ".json", &args, true, "test_file_stereo_8bit_64spp_wav.json"); + runTests("test_file_stereo.wav", FileFormat::Wav, FileFormat::Json, &args, true, "test_file_stereo_8bit_64spp_wav.json"); } //------------------------------------------------------------------------------ @@ -308,7 +445,7 @@ TEST_F(OptionHandlerTest, shouldGenerateJsonWaveformDataFromWavAudio) TEST_F(OptionHandlerTest, shouldGenerateJsonWaveformDataFromMp3Audio) { std::vector args{ "-b", "8", "-z", "64" }; - runTest("test_file_stereo.mp3", ".json", &args, true, "test_file_stereo_8bit_64spp_mp3.json"); + runTests("test_file_stereo.mp3", FileFormat::Mp3, FileFormat::Json, &args, true, "test_file_stereo_8bit_64spp_mp3.json"); } //------------------------------------------------------------------------------ @@ -316,7 +453,7 @@ TEST_F(OptionHandlerTest, shouldGenerateJsonWaveformDataFromMp3Audio) TEST_F(OptionHandlerTest, shouldGenerateJsonWaveformDataFromFlacAudio) { std::vector args{ "-b", "8", "-z", "64" }; - runTest("test_file_stereo.flac", ".json", &args, true, "test_file_stereo_8bit_64spp_flac.json"); + runTests("test_file_stereo.flac", FileFormat::Flac, FileFormat::Json, &args, true, "test_file_stereo_8bit_64spp_flac.json"); } //------------------------------------------------------------------------------ @@ -324,7 +461,7 @@ TEST_F(OptionHandlerTest, shouldGenerateJsonWaveformDataFromFlacAudio) TEST_F(OptionHandlerTest, shouldGenerateJsonWaveformDataFromOggVorbisAudio) { std::vector args{ "-b", "8", "-z", "64" }; - runTest("test_file_stereo.oga", ".json", &args, true, "test_file_stereo_8bit_64spp_oga.json"); + runTests("test_file_stereo.oga", FileFormat::Ogg, FileFormat::Json, &args, true, "test_file_stereo_8bit_64spp_oga.json"); } //------------------------------------------------------------------------------ @@ -335,56 +472,56 @@ TEST_F(OptionHandlerTest, shouldGenerateJsonWaveformDataFromOggVorbisAudio) TEST_F(OptionHandlerTest, shouldConvertBinaryWaveformDataToJson) { - runTest("test_file_stereo_8bit_64spp_wav.dat", ".json", nullptr, true, "test_file_stereo_8bit_64spp_wav.json"); + runTests("test_file_stereo_8bit_64spp_wav.dat", FileFormat::Dat, FileFormat::Json, nullptr, true, "test_file_stereo_8bit_64spp_wav.json"); } //------------------------------------------------------------------------------ TEST_F(OptionHandlerTest, shouldConvert2ChannelBinaryWaveformDataToJson) { - runTest("07023003_8bit_64spp_2channel.dat", ".json", nullptr, true, "07023003_8bit_64spp_2channel.json"); + runTests("07023003_8bit_64spp_2channel.dat", FileFormat::Dat, FileFormat::Json, nullptr, true, "07023003_8bit_64spp_2channel.json"); } //------------------------------------------------------------------------------ TEST_F(OptionHandlerTest, shouldConvertBinaryWaveformDataToText) { - runTest("test_file_stereo_8bit_64spp_wav.dat", ".txt", nullptr, true, "test_file_stereo_8bit_64spp_wav.txt"); + runTests("test_file_stereo_8bit_64spp_wav.dat", FileFormat::Dat, FileFormat::Txt, nullptr, true, "test_file_stereo_8bit_64spp_wav.txt"); } //------------------------------------------------------------------------------ TEST_F(OptionHandlerTest, shouldConvert2ChannelBinaryWaveformDataToText) { - runTest("07023003_8bit_64spp_2channel.dat", ".txt", nullptr, true, "07023003_8bit_64spp_2channel.txt"); + runTests("07023003_8bit_64spp_2channel.dat", FileFormat::Dat, FileFormat::Txt, nullptr, true, "07023003_8bit_64spp_2channel.txt"); } //------------------------------------------------------------------------------ TEST_F(OptionHandlerTest, shouldNotConvertJsonWaveformDataToBinary) { - runTest("test_file_stereo_8bit_64spp_mp3.json", ".dat", nullptr, false); + runTests("test_file_stereo_8bit_64spp_mp3.json", FileFormat::Json, FileFormat::Dat, nullptr, false); } //------------------------------------------------------------------------------ TEST_F(OptionHandlerTest, shouldNotConvertJsonWaveformDataToText) { - runTest("test_file_stereo_8bit_64spp_mp3.json", ".txt", nullptr, false); + runTests("test_file_stereo_8bit_64spp_mp3.json", FileFormat::Json, FileFormat::Txt, nullptr, false); } //------------------------------------------------------------------------------ TEST_F(OptionHandlerTest, shouldNotConvertTextWaveformDataToBinary) { - runTest("test_file_stereo_8bit_64spp.txt", ".dat", nullptr, false); + runTests("test_file_stereo_8bit_64spp_wav.txt", FileFormat::Txt, FileFormat::Dat, nullptr, false); } //------------------------------------------------------------------------------ TEST_F(OptionHandlerTest, shouldNotConvertTextWaveformDataToJson) { - runTest("test_file_stereo_8bit_64spp.txt", ".json", nullptr, false); + runTests("test_file_stereo_8bit_64spp_wav.txt", FileFormat::Txt, FileFormat::Json, nullptr, false); } //------------------------------------------------------------------------------ @@ -396,7 +533,7 @@ TEST_F(OptionHandlerTest, shouldNotConvertTextWaveformDataToJson) TEST_F(OptionHandlerTest, shouldRenderSingleChannelWaveformImageWithCorrectAmplitudeScale) { std::vector args{ "-z", "64" }; - runTest("test_file_image_amplitude_scale_1channel.dat", ".png", &args, true, "test_file_image_amplitude_scale_1channel.png"); + runTests("test_file_image_amplitude_scale_1channel.dat", FileFormat::Dat, FileFormat::Png, &args, true, "test_file_image_amplitude_scale_1channel.png"); } //------------------------------------------------------------------------------ @@ -404,7 +541,7 @@ TEST_F(OptionHandlerTest, shouldRenderSingleChannelWaveformImageWithCorrectAmpli TEST_F(OptionHandlerTest, shouldRenderTwoChannelWaveformImageWithCorrectAmplitudeScale) { std::vector args{ "-z", "64" }; - runTest("test_file_image_amplitude_scale_2channel.dat", ".png", &args, true, "test_file_image_amplitude_scale_2channel.png"); + runTests("test_file_image_amplitude_scale_2channel.dat", FileFormat::Dat, FileFormat::Png, &args, true, "test_file_image_amplitude_scale_2channel.png"); } //------------------------------------------------------------------------------ @@ -412,7 +549,7 @@ TEST_F(OptionHandlerTest, shouldRenderTwoChannelWaveformImageWithCorrectAmplitud TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromBinaryWaveformData) { std::vector args{ "-z", "128" }; - runTest("test_file_stereo_8bit_64spp_wav.dat", ".png", &args, true, "test_file_stereo_dat_128spp.png"); + runTests("test_file_stereo_8bit_64spp_wav.dat", FileFormat::Dat, FileFormat::Png, &args, true, "test_file_stereo_dat_128spp.png"); } //------------------------------------------------------------------------------ @@ -420,7 +557,7 @@ TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromBinaryWaveformData) TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromFloatingPointWavAudio) { std::vector args{ "-z", "128" }; - runTest("test_file_mono_float32.wav", ".png", &args, true, "test_file_mono_float32_128spp.png"); + runTests("test_file_mono_float32.wav", FileFormat::Wav, FileFormat::Png, &args, true, "test_file_mono_float32_128spp.png"); } //------------------------------------------------------------------------------ @@ -428,7 +565,7 @@ TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromFloatingPointWavAudio) TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromWavAudio) { std::vector args{ "-z", "128" }; - runTest("test_file_stereo.wav", ".png", &args, true, "test_file_stereo_wav_128spp.png"); + runTests("test_file_stereo.wav", FileFormat::Wav, FileFormat::Png, &args, true, "test_file_stereo_wav_128spp.png"); } //------------------------------------------------------------------------------ @@ -436,7 +573,7 @@ TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromWavAudio) TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromMp3Audio) { std::vector args{ "-z", "128" }; - runTest("test_file_stereo.mp3", ".png", &args, true, "test_file_stereo_mp3_128spp.png"); + runTests("test_file_stereo.mp3", FileFormat::Mp3, FileFormat::Png, &args, true, "test_file_stereo_mp3_128spp.png"); } //------------------------------------------------------------------------------ @@ -444,7 +581,7 @@ TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromMp3Audio) TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromFlacAudio) { std::vector args{ "-z", "128" }; - runTest("test_file_stereo.flac", ".png", &args, true, "test_file_stereo_flac_128spp.png"); + runTests("test_file_stereo.flac", FileFormat::Flac, FileFormat::Png, &args, true, "test_file_stereo_flac_128spp.png"); } //------------------------------------------------------------------------------ @@ -452,7 +589,7 @@ TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromFlacAudio) TEST_F(OptionHandlerTest, shouldRenderWaveformImageFromOggVorbisAudio) { std::vector args{ "-z", "128" }; - runTest("test_file_stereo.oga", ".png", &args, true, "test_file_stereo_oga_128spp.png"); + runTests("test_file_stereo.oga", FileFormat::Ogg, FileFormat::Png, &args, true, "test_file_stereo_oga_128spp.png"); } //------------------------------------------------------------------------------ @@ -461,7 +598,7 @@ TEST_F(OptionHandlerTest, shouldRenderWaveformWithColorScheme) { std::vector args{ "-z", "128", "--colors", "audition" }; - runTest("test_file_stereo_8bit_64spp_wav.dat", ".png", &args, true, "test_file_stereo_dat_128spp_audition.png"); + runTests("test_file_stereo_8bit_64spp_wav.dat", FileFormat::Dat, FileFormat::Png, &args, true, "test_file_stereo_dat_128spp_audition.png"); } //------------------------------------------------------------------------------ @@ -476,7 +613,7 @@ TEST_F(OptionHandlerTest, shouldRenderWaveformWithSpecifiedColors) "--axis-label-color", "ffffff", }; - runTest("test_file_stereo_8bit_64spp_wav.dat", ".png", &args, true, "test_file_stereo_dat_128spp_colors.png"); + runTests("test_file_stereo_8bit_64spp_wav.dat", FileFormat::Dat, FileFormat::Png, &args, true, "test_file_stereo_dat_128spp_colors.png"); } //------------------------------------------------------------------------------ @@ -485,7 +622,7 @@ TEST_F(OptionHandlerTest, shouldRenderWaveformWithNoAxisLabels) { std::vector args{ "-z", "128", "--no-axis-labels" }; - runTest("test_file_stereo_8bit_64spp_wav.dat", ".png", &args, true, "test_file_stereo_dat_128spp_no_axis_labels.png"); + runTests("test_file_stereo_8bit_64spp_wav.dat", FileFormat::Dat, FileFormat::Png, &args, true, "test_file_stereo_dat_128spp_no_axis_labels.png"); } //------------------------------------------------------------------------------ @@ -494,7 +631,7 @@ TEST_F(OptionHandlerTest, shouldRenderWaveformWithFixedAmplitudeScale) { std::vector args{ "-z", "128", "--amplitude-scale", "1.5" }; - runTest("test_file_stereo_8bit_64spp_wav.dat", ".png", &args, true, "test_file_stereo_dat_128spp_scale_1.5.png"); + runTests("test_file_stereo_8bit_64spp_wav.dat", FileFormat::Dat, FileFormat::Png, &args, true, "test_file_stereo_dat_128spp_scale_1.5.png"); } //------------------------------------------------------------------------------ @@ -503,30 +640,39 @@ TEST_F(OptionHandlerTest, shouldRenderWaveformWithAutoAmplitudeScale) { std::vector args{ "-z", "128", "--amplitude-scale", "auto" }; - runTest("test_file_stereo_8bit_64spp_wav.dat", ".png", &args, true, "test_file_stereo_dat_128spp_scale_auto.png"); + runTests("test_file_stereo_8bit_64spp_wav.dat", FileFormat::Dat, FileFormat::Png, &args, true, "test_file_stereo_dat_128spp_scale_auto.png"); +} + +//------------------------------------------------------------------------------ + +TEST_F(OptionHandlerTest, shouldRenderWaveformFitToImageWidthFromMp3) +{ + std::vector args{ "-z", "auto", "-w", "500", "-h", "150" }; + + runTests("test_file_stereo.mp3", FileFormat::Mp3, FileFormat::Png, &args, true, "test_file_stereo_mp3_500.png"); } //------------------------------------------------------------------------------ -TEST_F(OptionHandlerTest, shouldRenderWaveformFitToImageWidth) +TEST_F(OptionHandlerTest, shouldRenderWaveformFitToImageWidthFromWav) { std::vector args{ "-z", "auto", "-w", "500", "-h", "150" }; - runTest("test_file_stereo.mp3", ".png", &args, true, "test_file_stereo_mp3_500.png"); + runTests("test_file_stereo.wav", FileFormat::Wav, FileFormat::Png, &args, true, "test_file_stereo_wav_500.png"); } //------------------------------------------------------------------------------ TEST_F(OptionHandlerTest, shouldNotRenderWaveformImageFromJsonWaveformData) { - runTest("test_file_stereo.json", ".png", nullptr, false); + runTests("test_file_stereo_8bit_64spp_wav.json", FileFormat::Json, FileFormat::Png, nullptr, false); } //------------------------------------------------------------------------------ TEST_F(OptionHandlerTest, shouldNotRenderWaveformImageFromTextWaveformData) { - runTest("test_file_stereo.txt", ".png", nullptr, false); + runTests("test_file_stereo_8bit_64spp_wav.txt", FileFormat::Txt, FileFormat::Png, nullptr, false); } //------------------------------------------------------------------------------ @@ -534,7 +680,7 @@ TEST_F(OptionHandlerTest, shouldNotRenderWaveformImageFromTextWaveformData) TEST_F(OptionHandlerTest, shouldFailIfZoomIsZeroWithWavAudio) { std::vector args{ "-z", "0" }; - runTest("test_file_stereo.wav", ".png", &args, false, nullptr, "Invalid zoom: minimum 2\n"); + runTests("test_file_stereo.wav", FileFormat::Wav, FileFormat::Png, &args, false, nullptr, "Invalid zoom: minimum 2\n"); } //------------------------------------------------------------------------------ @@ -542,7 +688,7 @@ TEST_F(OptionHandlerTest, shouldFailIfZoomIsZeroWithWavAudio) TEST_F(OptionHandlerTest, shouldFailIfZoomIsZeroWithMp3Audio) { std::vector args{ "-z", "0" }; - runTest("test_file_stereo.mp3", ".png", &args, false, nullptr, "Invalid zoom: minimum 2\n"); + runTests("test_file_stereo.mp3", FileFormat::Mp3, FileFormat::Png, &args, false, nullptr, "Invalid zoom: minimum 2\n"); } //------------------------------------------------------------------------------ @@ -550,7 +696,7 @@ TEST_F(OptionHandlerTest, shouldFailIfZoomIsZeroWithMp3Audio) TEST_F(OptionHandlerTest, shouldFailIfPixelsPerSecondIsZero) { std::vector args{ "--pixels-per-second", "0" }; - runTest("test_file_stereo.wav", ".png", &args, false, nullptr, "Invalid pixels per second: must be greater than zero\n"); + runTests("test_file_stereo.wav", FileFormat::Wav, FileFormat::Png, &args, false, nullptr, "Invalid pixels per second: must be greater than zero\n"); } //------------------------------------------------------------------------------ @@ -558,7 +704,7 @@ TEST_F(OptionHandlerTest, shouldFailIfPixelsPerSecondIsZero) TEST_F(OptionHandlerTest, shouldFailIfPixelsPerSecondIsNegative) { std::vector args{ "--pixels-per-second", "-1" }; - runTest("test_file_stereo.wav", ".png", &args, false, nullptr, "Invalid pixels per second: must be greater than zero\n"); + runTests("test_file_stereo.wav", FileFormat::Wav, FileFormat::Png, &args, false, nullptr, "Invalid pixels per second: must be greater than zero\n"); } //------------------------------------------------------------------------------ diff --git a/test/SndFileAudioFileReaderTest.cpp b/test/SndFileAudioFileReaderTest.cpp index ab7ad64..742166f 100644 --- a/test/SndFileAudioFileReaderTest.cpp +++ b/test/SndFileAudioFileReaderTest.cpp @@ -126,6 +126,7 @@ TEST_F(SndFileAudioFileReaderTest, shouldFailToProcessIfFileNotOpen) StrictMock processor; EXPECT_CALL(processor, init(_, _, _, _)).Times(0); + EXPECT_CALL(processor, shouldContinue()).Times(0); EXPECT_CALL(processor, process(_, _)).Times(0); EXPECT_CALL(processor, done()).Times(0); @@ -153,6 +154,7 @@ static void testProcessStereo(const std::string& filename, const std::string& fo InSequence sequence; // Calls expected in the order listed below. EXPECT_CALL(processor, init(16000, 2, 113519, 16384)).WillOnce(Return(true)); + EXPECT_CALL(processor, shouldContinue()).WillOnce(Return(true)); // Total number of frames: 113519, 13 x 8192 frames then 1 x 7023 EXPECT_CALL(processor, process(_, 8192)).Times(13).WillRepeatedly(Return(true)); @@ -222,6 +224,7 @@ static void testProcessMono(const std::string& filename, const std::string& form InSequence sequence; // Calls expected in the order listed below. EXPECT_CALL(processor, init(16000, 1, 113519, 16384)).WillOnce(Return(true)); + EXPECT_CALL(processor, shouldContinue()).WillOnce(Return(true)); // Total number of frames: 113519, 6 x 16384 frames then 1 x 15215 EXPECT_CALL(processor, process(_, 16384)).Times(6).WillRepeatedly(Return(true)); @@ -279,6 +282,7 @@ TEST_F(SndFileAudioFileReaderTest, shouldNotProcessFileMoreThanOnce) InSequence sequence; // Calls expected in the order listed below. EXPECT_CALL(processor, init(16000, 2, 113519, 16384)).WillOnce(Return(true)); + EXPECT_CALL(processor, shouldContinue()).WillOnce(Return(true)); // Total number of frames: 113519, 13 x 8192 frames then 1 x 7023 EXPECT_CALL(processor, process(_, 8192)).Times(13).WillRepeatedly(Return(true)); diff --git a/test/WaveformBufferTest.cpp b/test/WaveformBufferTest.cpp index f98da4c..83eb87d 100644 --- a/test/WaveformBufferTest.cpp +++ b/test/WaveformBufferTest.cpp @@ -96,7 +96,7 @@ TEST_F(WaveformBufferTest, shouldLoad16BitVersion1DataFile) ASSERT_THAT(output.str(), StrEq("")); ASSERT_THAT(error.str(), StrEq( - "Reading waveform data file: ../test/data/test_file_stereo_16bit_64spp_wav.dat\n" + "Input file: ../test/data/test_file_stereo_16bit_64spp_wav.dat\n" "Channels: 1\n" "Sample rate: 16000 Hz\n" "Bits: 16\n" @@ -118,7 +118,7 @@ TEST_F(WaveformBufferTest, shouldLoad16BitVersion2DataFile) ASSERT_THAT(output.str(), StrEq("")); ASSERT_THAT(error.str(), StrEq( - "Reading waveform data file: ../test/data/test_file_stereo_16bit_64spp_wav_v2.dat\n" + "Input file: ../test/data/test_file_stereo_16bit_64spp_wav_v2.dat\n" "Channels: 1\n" "Sample rate: 16000 Hz\n" "Bits: 16\n" @@ -140,7 +140,7 @@ TEST_F(WaveformBufferTest, shouldLoad8BitVersion1DataFile) ASSERT_THAT(output.str(), StrEq("")); ASSERT_THAT(error.str(), StrEq( - "Reading waveform data file: ../test/data/test_file_stereo_8bit_64spp_wav.dat\n" + "Input file: ../test/data/test_file_stereo_8bit_64spp_wav.dat\n" "Channels: 1\n" "Sample rate: 16000 Hz\n" "Bits: 8\n" @@ -162,7 +162,7 @@ TEST_F(WaveformBufferTest, shouldLoad8BitVersion2DataFile) ASSERT_THAT(output.str(), StrEq("")); ASSERT_THAT(error.str(), StrEq( - "Reading waveform data file: ../test/data/test_file_stereo_8bit_64spp_wav_v2.dat\n" + "Input file: ../test/data/test_file_stereo_8bit_64spp_wav_v2.dat\n" "Channels: 1\n" "Sample rate: 16000 Hz\n" "Bits: 8\n" @@ -201,7 +201,7 @@ TEST_F(WaveformBufferTest, shouldReportErrorIfSizeMismatch) ASSERT_THAT(output.str(), StrEq("")); ASSERT_THAT(error.str(), StrEq( - "Reading waveform data file: ../test/data/size_mismatch.dat\n" + "Input file: ../test/data/size_mismatch.dat\n" "Expected 2056 points, read 1800 min and max points\n" )); } diff --git a/test/mocks/MockAudioProcessor.h b/test/mocks/MockAudioProcessor.h index 3b6ccfb..59a8ea7 100644 --- a/test/mocks/MockAudioProcessor.h +++ b/test/mocks/MockAudioProcessor.h @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// Copyright 2013-2018 BBC Research and Development +// Copyright 2013-2019 BBC Research and Development // // Author: Chris Needham // @@ -36,6 +36,7 @@ class MockAudioProcessor : public AudioProcessor { public: MOCK_METHOD4(init, bool(int sample_rate, int channels, long frame_count, int buffer_size)); + MOCK_CONST_METHOD0(shouldContinue, bool()); MOCK_METHOD2(process, bool(const short* buffer, int frame_count)); MOCK_METHOD0(done, void()); };