Skip to content

Commit

Permalink
Introduce unit tests
Browse files Browse the repository at this point in the history
Add some basic, mostly text-related unit tests using Catch2

Also, add -o option
  • Loading branch information
windytan committed Jun 1, 2024
1 parent 0400813 commit 503fbc3
Show file tree
Hide file tree
Showing 12 changed files with 340 additions and 79 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: build

on:
push:
branches: [ master ]
branches: [ master, meson ]
pull_request:
branches: [ master ]

Expand All @@ -21,17 +21,19 @@ jobs:
- name: compile
run: cd build && meson compile

macos-build:
macos-build-and-test:
runs-on: macos-latest

steps:
- uses: actions/checkout@v4
- name: Install dependencies (brew)
run: brew install meson libsndfile liquid-dsp
run: brew install meson libsndfile liquid-dsp catch2
- name: meson setup
run: meson setup build
- name: compile
run: cd build && meson compile
- name: test
run: cd build && meson test

windows-msys2-mingw-build:
runs-on: windows-latest
Expand Down Expand Up @@ -107,7 +109,8 @@ jobs:
./bootstrap.sh &&
./configure --prefix=/usr &&
make && make install
# Cygwin does not allow underscore variables that start with an uppercase when compiling with gcc
# Cygwin does not allow underscore variables that start with an uppercase when
# compiling with gcc
- name: Patch liquid-dsp
shell: C:\cygwin\bin\bash.exe -eo pipefail '{0}'
run: perl -i -p -e 's/(?<=\s)(_[A-Z])(?=[,\)])/\L\1__/g' /usr/include/liquid/liquid.h
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* Migrate build system from autotools to meson (#90)
* Add GitHub Workflows CI builds for macOS and Windows (MSYS2/MinGW + Cygwin)
* Add basic unit tests for hex input using Catch2 (#84)
* Remove unmaintained build options for non-liquid, non-TMC builds
* Add support for enhanced RadioText (eRT)
* Add support for Long PS in Group 15A (#104)
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,20 @@ redsea -f WAVEFILE
readable by libsndfile should work.
-i, --input FORMAT Decode stdin as FORMAT (see the wiki for more info):
bits Unsynchronized ASCII bit stream (011010110...).
All characters but '0' and '1' are ignored.
hex RDS Spy hex format.
mpx Mono S16LE PCM-encoded MPX waveform (default).
tef Serial data from the TEF6686 tuner.
bits (-b) Unsynchronized ASCII bit stream (011010110...).
All characters but '0' and '1' are ignored.
hex (-h) RDS Spy hex format.
mpx Mono S16LE PCM-encoded MPX waveform (default).
tef Serial data from the TEF6686 tuner.
-l, --loctable DIR Load TMC location table from a directory in TMC
Exchange format. This option can be specified
multiple times to load several location tables.
-o, --output FORMAT Print output groups as FORMAT:
hex (-x) RDS Spy hex format.
json Newline-delimited json (default).
-p, --show-partial Show some multi-group data even before they've been
fully received (PS names, RadioText, alternative
frequencies). partial_ will be prepended to their
Expand All @@ -144,9 +148,6 @@ redsea -f WAVEFILE
TMC.
-v, --version Print version string and exit.
-x, --output-hex Output hex groups in the RDS Spy format,
suppressing JSON output.
```

### Formatting and filtering the JSON output
Expand Down
14 changes: 11 additions & 3 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ else
add_project_arguments('-std=c++14', language : 'cpp')
endif

sources = [
sources_no_main = [
'ext/json/jsoncpp.cpp',
'src/block_sync.cc',
'src/channel.cc',
Expand All @@ -64,12 +64,20 @@ sources = [
'src/liquid_wrappers.cc',
'src/options.cc',
'src/rdsstring.cc',
'src/redsea.cc',
'src/subcarrier.cc',
'src/tables.cc',
'src/tmc/tmc.cc',
'src/tmc/locationdb.cc',
'src/util.cc'
]

executable('redsea', sources, dependencies: [iconv, sndfile, liquid])
executable('redsea', [sources_no_main, 'src/redsea.cc'], dependencies: [iconv, sndfile, liquid])

### Unit tests ###

catch2_dep = dependency('catch2-with-main', required: false)

if catch2_dep.found()
test_exec = executable('redsea-test', [sources_no_main, 'test/test.cc'], dependencies: [ iconv, sndfile, liquid, catch2_dep ])
test('Smoke tests', test_exec)
endif
29 changes: 20 additions & 9 deletions src/channel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
*/
#include "src/channel.h"

#include <chrono>
#include <iostream>

#include "src/common.h"

namespace redsea {
Expand All @@ -29,10 +32,20 @@ namespace redsea {
* bits, or groups. Usage of these inputs shouldn't be intermixed.
*
*/
Channel::Channel(const Options& options, int which_channel) :
Channel::Channel(const Options& options, int which_channel, std::ostream& output_stream = std::cout) :
options_(options),
which_channel_(which_channel),
block_stream_(options), station_(0x0000, options, which_channel, false) {
output_stream_(output_stream),
block_stream_(options), station_(options, which_channel) {
}

// Used for testing (PI is already known)
Channel::Channel(const Options& options, std::ostream& output_stream, uint16_t pi) :
options_(options),
which_channel_(0),
output_stream_(output_stream),
block_stream_(options), station_(options, 0, pi) {
cached_pi_.update(pi);
}

void Channel::processBit(bool bit) {
Expand Down Expand Up @@ -92,7 +105,7 @@ void Channel::processGroup(Group group) {
const auto pi_status = cached_pi_.update(group.getPI());
switch (pi_status) {
case CachedPI::Result::ChangeConfirmed:
station_ = Station(cached_pi_.get(), options_, which_channel_);
station_ = Station(options_, which_channel_, cached_pi_.get());
break;

case CachedPI::Result::SpuriousChange:
Expand All @@ -103,17 +116,15 @@ void Channel::processGroup(Group group) {
}
}

auto stream = options_.feed_thru ? &std::cerr : &std::cout;

if (options_.output_type == redsea::OutputType::Hex) {
if (!group.isEmpty()) {
group.printHex(stream);
group.printHex(output_stream_);
if (options_.timestamp)
*stream << ' ' << getTimePointString(group.getRxTime(), options_.time_format);
*stream << '\n' << std::flush;
output_stream_ << ' ' << getTimePointString(group.getRxTime(), options_.time_format);
output_stream_ << '\n' << std::flush;
}
} else {
station_.updateAndPrint(group, stream);
station_.updateAndPrint(group, output_stream_);
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#ifndef CHANNEL_H_
#define CHANNEL_H_

#include <iostream>

#include "src/block_sync.h"
#include "src/common.h"
#include "src/options.h"
Expand Down Expand Up @@ -70,7 +72,8 @@ class CachedPI {

class Channel {
public:
Channel(const Options& options, int which_channel);
Channel(const Options& options, int which_channel, std::ostream& output_stream);
Channel(const Options& options, std::ostream& output_stream, uint16_t pi);
void processBit(bool bit);
void processBits(const BitBuffer& buffer);
void processGroup(Group group);
Expand All @@ -81,6 +84,7 @@ class Channel {
private:
Options options_;
int which_channel_;
std::ostream& output_stream_;
CachedPI cached_pi_{};
BlockStream block_stream_;
Station station_;
Expand Down
30 changes: 16 additions & 14 deletions src/groups.cc
Original file line number Diff line number Diff line change
Expand Up @@ -194,19 +194,19 @@ void Group::setAverageBLER(float bler) {
* Invalid blocks are replaced with "----".
*
*/
void Group::printHex(std::ostream* stream) const {
stream->fill('0');
stream->setf(std::ios_base::uppercase);
void Group::printHex(std::ostream& stream) const {
stream.fill('0');
stream.setf(std::ios_base::uppercase);

for (eBlockNumber block_num : {BLOCK1, BLOCK2, BLOCK3, BLOCK4}) {
const Block& block = blocks_[block_num];
if (block.is_received)
*stream << std::hex << std::setw(4) << block.data;
stream << std::hex << std::setw(4) << block.data;
else
*stream << "----";
stream << "----";

if (block_num != BLOCK4)
*stream << " ";
stream << " ";
}
}

Expand All @@ -215,19 +215,21 @@ void Group::printHex(std::ostream* stream) const {
* code.
*
*/
Station::Station() : Station(0x0000, Options(), 0) {
}

Station::Station(uint16_t _pi, const Options& options, int which_channel, bool has_pi) :
pi_(_pi), has_pi_(has_pi), options_(options), which_channel_(which_channel), tmc_(options) {
Station::Station(const Options& options, int which_channel) :
options_(options), which_channel_(which_channel), tmc_(options) {
writer_builder_["indentation"] = "";
writer_builder_["precision"] = 7;
writer_builder_.settings_["emitUTF8"] = true;
writer_ =
std::unique_ptr<Json::StreamWriter>(writer_builder_.newStreamWriter());
}

void Station::updateAndPrint(const Group& group, std::ostream* stream) {
Station::Station(const Options& options, int which_channel, uint16_t _pi) : Station(options, which_channel) {
pi_ = _pi;
has_pi_ = true;
}

void Station::updateAndPrint(const Group& group, std::ostream& stream) {
if (!has_pi_)
return;

Expand Down Expand Up @@ -265,7 +267,7 @@ void Station::updateAndPrint(const Group& group, std::ostream* stream) {

if (options_.show_raw) {
std::stringstream ss;
group.printHex(&ss);
group.printHex(ss);
json_["raw_data"] = ss.str();
}

Expand Down Expand Up @@ -337,7 +339,7 @@ void Station::updateAndPrint(const Group& group, std::ostream* stream) {
writer_->write(json_, &ss);
ss << '\n';

*stream << ss.str() << std::flush;
stream << ss.str() << std::flush;
}

uint16_t Station::getPI() const {
Expand Down
23 changes: 12 additions & 11 deletions src/groups.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,29 +72,29 @@ bool operator<(const GroupType& type1, const GroupType& type2);

class ProgramServiceName {
public:
ProgramServiceName() : text(8) {}
ProgramServiceName() = default;
void update(size_t pos, uint8_t byte1, uint8_t byte2) {
text.set(pos, byte1, byte2);
}

RDSString text;
RDSString text{8};
};

class LongPS {
public:
LongPS() : text(32) {
LongPS() {
text.setEncoding(RDSString::Encoding::UTF8);
}
void update(size_t pos, uint8_t byte1, uint8_t byte2) {
text.set(pos, byte1, byte2);
}

RDSString text;
RDSString text{32};
};

class RadioText {
public:
RadioText() : text(64) {}
RadioText() = default;
bool isABChanged(int new_ab) {
const bool is = (ab != new_ab);
ab = new_ab;
Expand All @@ -119,10 +119,10 @@ class RadioText {
};
};

RDSString text;
RDSString text{64};
Plus plus;
std::string previous_potentially_complete_message;
int ab { 0 };
int ab{};
};

class PTYName {
Expand Down Expand Up @@ -174,7 +174,7 @@ class Group {
bool hasBLER() const;
bool hasTime() const;
std::chrono::time_point<std::chrono::system_clock> getRxTime() const;
void printHex(std::ostream* stream) const;
void printHex(std::ostream& stream) const;

void disableOffsets();
void setBlock(eBlockNumber block_num, Block block);
Expand All @@ -195,9 +195,10 @@ class Group {

class Station {
public:
Station();
Station(uint16_t _pi, const Options& options, int which_channel, bool has_pi=true);
void updateAndPrint(const Group& group, std::ostream* stream);
Station() = delete;
Station(const Options& options, int which_channel, uint16_t _pi);
Station(const Options& options, int which_channel);
void updateAndPrint(const Group& group, std::ostream& stream);
uint16_t getPI() const;

private:
Expand Down
17 changes: 15 additions & 2 deletions src/options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Options getOptions(int argc, char** argv) {
{ "input-hex", no_argument, 0, 'h'},
{ "input", 1, 0, 'i'},
{ "loctable", 1, 0, 'l'},
{ "output", 1, 0, 'o'},
{ "show-partial", no_argument, 0, 'p'},
{ "samplerate", 1, 0, 'r'},
{ "show-raw", no_argument, 0, 'R'},
Expand All @@ -49,7 +50,7 @@ Options getOptions(int argc, char** argv) {
int option_index = 0;
int option_char;

while ((option_char = getopt_long(argc, argv, "bc:eEf:hi:l:pr:Rt:uvx",
while ((option_char = getopt_long(argc, argv, "bc:eEf:hi:l:o:pr:Rt:uvx",
long_options, &option_index)) >= 0) {
switch (option_char) {
case 'b': // For backwards compatibility
Expand Down Expand Up @@ -92,7 +93,19 @@ Options getOptions(int argc, char** argv) {
}
break;
}
case 'x':
case 'o': {
const std::string output_type(optarg);
if (output_type == "hex") {
options.output_type = OutputType::Hex;
} else if (output_type == "json") {
options.output_type = OutputType::JSON;
} else {
std::cerr << "error: unknown output format '" << output_type << "'" << std::endl;
options.exit_failure = true;
}
break;
}
case 'x': // For backwards compatibility
options.output_type = OutputType::Hex;
break;
case 'p':
Expand Down
Loading

0 comments on commit 503fbc3

Please sign in to comment.