Rework the flux decoders to add a desync opcode for separating multiple reads
in a flux stream. Finally add a unit test for the flux decoder.
This commit is contained in:
parent
298f77f52e
commit
18d90c44dd
21 changed files with 210 additions and 47 deletions
|
|
@ -30,7 +30,7 @@ public:
|
|||
_config(config.amiga())
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
RecordType advanceToNextRecord() override
|
||||
{
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
|
||||
if (_fmr->eof() || !_sector->clock)
|
||||
|
|
@ -38,7 +38,7 @@ public:
|
|||
return SECTOR_RECORD;
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
|
||||
if (rawbits.size() < (AMIGA_RECORD_SIZE*16))
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ public:
|
|||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image)
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
int logicalTrack;
|
||||
if (physicalSide != 0)
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ public:
|
|||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image)
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
/* The format ID Character # 1 and # 2 are in the .d64 image only present
|
||||
* in track 18 sector zero which contains the BAM info in byte 162 and 163.
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ public:
|
|||
_config(config.ibm())
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord()
|
||||
RecordType advanceToNextRecord() override
|
||||
{
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
|
||||
|
|
@ -130,7 +130,7 @@ public:
|
|||
return RecordType::UNKNOWN_RECORD;
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
|
||||
auto bits = readRawBits(recordSize*16);
|
||||
|
|
@ -156,7 +156,7 @@ public:
|
|||
_sector->logicalTrack = _sector->physicalCylinder;
|
||||
}
|
||||
|
||||
void decodeDataRecord()
|
||||
void decodeDataRecord() override
|
||||
{
|
||||
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
|
||||
auto bits = readRawBits(recordLength*16);
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ public:
|
|||
{}
|
||||
|
||||
/* Search for FM or MFM sector record */
|
||||
RecordType advanceToNextRecord()
|
||||
RecordType advanceToNextRecord() override
|
||||
{
|
||||
nanoseconds_t now = _fmr->tell().ns();
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ public:
|
|||
return UNKNOWN_RECORD;
|
||||
}
|
||||
|
||||
void decodeSectorRecord()
|
||||
void decodeSectorRecord() override
|
||||
{
|
||||
unsigned recordSize, payloadSize, headerSize;
|
||||
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ public:
|
|||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image)
|
||||
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, physicalTrack, physicalSide);
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ std::unique_ptr<TrackDataFlux> AbstractDecoder::decodeToSectors(
|
|||
return std::move(_trackdata);
|
||||
if ((r == UNKNOWN_RECORD) || (r == DATA_RECORD))
|
||||
{
|
||||
fmr.findEvent(F_BIT_PULSE);
|
||||
fmr.skipToEvent(F_BIT_PULSE);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ std::unique_ptr<TrackDataFlux> AbstractDecoder::decodeToSectors(
|
|||
r = advanceToNextRecord();
|
||||
if (r != UNKNOWN_RECORD)
|
||||
break;
|
||||
if (fmr.findEvent(F_BIT_PULSE) == 0)
|
||||
if (fmr.eof())
|
||||
break;
|
||||
}
|
||||
recordStart = fmr.tell();
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ FluxmapReader::FluxmapReader(const Fluxmap& fluxmap):
|
|||
rewind();
|
||||
}
|
||||
|
||||
uint8_t FluxmapReader::getNextEvent(unsigned& ticks)
|
||||
void FluxmapReader::getNextEvent(int& event, unsigned& ticks)
|
||||
{
|
||||
ticks = 0;
|
||||
|
||||
|
|
@ -26,30 +26,42 @@ uint8_t FluxmapReader::getNextEvent(unsigned& ticks)
|
|||
{
|
||||
uint8_t b = _bytes[_pos.bytes++];
|
||||
ticks += b & 0x3f;
|
||||
if (b & (F_BIT_PULSE|F_BIT_INDEX))
|
||||
if (!b || (b & (F_BIT_PULSE|F_BIT_INDEX)))
|
||||
{
|
||||
_pos.ticks += ticks;
|
||||
return b;
|
||||
event = b & 0xc0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_pos.ticks += ticks;
|
||||
return 0;
|
||||
event = F_EOF;
|
||||
}
|
||||
|
||||
unsigned FluxmapReader::findEvent(uint8_t target)
|
||||
void FluxmapReader::skipToEvent(int event)
|
||||
{
|
||||
unsigned ticks = 0;
|
||||
unsigned ticks;
|
||||
findEvent(event, ticks);
|
||||
}
|
||||
|
||||
bool FluxmapReader::findEvent(int event, unsigned& ticks)
|
||||
{
|
||||
ticks = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
unsigned thisTicks;
|
||||
uint8_t bits = getNextEvent(thisTicks);
|
||||
ticks += thisTicks;
|
||||
int thisEvent;
|
||||
getNextEvent(thisEvent, thisTicks);
|
||||
ticks += thisTicks;
|
||||
|
||||
if (thisEvent == F_EOF)
|
||||
return false;
|
||||
|
||||
if (eof())
|
||||
return 0;
|
||||
if (bits & target)
|
||||
return ticks;
|
||||
return false;
|
||||
if ((event == thisEvent) || (event & thisEvent))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,8 +72,8 @@ unsigned FluxmapReader::readInterval(nanoseconds_t clock)
|
|||
|
||||
while (ticks <= thresholdTicks)
|
||||
{
|
||||
unsigned thisTicks = findEvent(F_BIT_PULSE);
|
||||
if (!thisTicks)
|
||||
unsigned thisTicks;
|
||||
if (!findEvent(F_BIT_PULSE, thisTicks))
|
||||
break;
|
||||
ticks += thisTicks;
|
||||
}
|
||||
|
|
@ -183,8 +195,9 @@ void FluxmapReader::seek(nanoseconds_t ns)
|
|||
|
||||
while (!eof() && (_pos.ticks < ticks))
|
||||
{
|
||||
int e;
|
||||
unsigned t;
|
||||
getNextEvent(t);
|
||||
getNextEvent(e, t);
|
||||
}
|
||||
_pos.zeroes = 0;
|
||||
}
|
||||
|
|
@ -225,7 +238,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
|
|||
positions[i] = positions[i+1];
|
||||
candidates[i] = candidates[i+1];
|
||||
}
|
||||
candidates[intervalCount] = findEvent(F_BIT_PULSE);
|
||||
findEvent(F_BIT_PULSE, candidates[intervalCount]);
|
||||
positions[intervalCount] = tell();
|
||||
|
||||
}
|
||||
|
|
@ -236,7 +249,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
|
|||
|
||||
void FluxmapReader::seekToIndexMark()
|
||||
{
|
||||
findEvent(F_BIT_INDEX);
|
||||
skipToEvent(F_BIT_INDEX);
|
||||
_pos.zeroes = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,8 +93,9 @@ public:
|
|||
return (_fluxmap.duration());
|
||||
}
|
||||
|
||||
uint8_t getNextEvent(unsigned& ticks);
|
||||
unsigned findEvent(uint8_t bits);
|
||||
void getNextEvent(int& event, unsigned& ticks);
|
||||
void skipToEvent(int event);
|
||||
bool findEvent(int event, unsigned& ticks);
|
||||
unsigned readInterval(nanoseconds_t clock); /* with debounce support */
|
||||
|
||||
/* Important! You can only reliably seek to 1 bits. */
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ Fluxmap& Fluxmap::appendIndex()
|
|||
return *this;
|
||||
}
|
||||
|
||||
Fluxmap& Fluxmap::appendDesync()
|
||||
{
|
||||
appendByte(F_DESYNC);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Fluxmap::precompensate(int threshold_ticks, int amount_ticks)
|
||||
{
|
||||
uint8_t junk = 0xff;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ public:
|
|||
Fluxmap& appendInterval(uint32_t ticks);
|
||||
Fluxmap& appendPulse();
|
||||
Fluxmap& appendIndex();
|
||||
Fluxmap& appendDesync();
|
||||
|
||||
Fluxmap& appendBytes(const Bytes& bytes);
|
||||
Fluxmap& appendBytes(const uint8_t* ptr, size_t len);
|
||||
|
|
|
|||
|
|
@ -68,14 +68,15 @@ public:
|
|||
while (!fmr.eof())
|
||||
{
|
||||
unsigned ticks;
|
||||
uint8_t bits = fmr.getNextEvent(ticks);
|
||||
int event;
|
||||
fmr.getNextEvent(event, ticks);
|
||||
if (fmr.eof())
|
||||
break;
|
||||
timestamp += ticks;
|
||||
|
||||
if (bits & F_BIT_PULSE)
|
||||
if (event & F_BIT_PULSE)
|
||||
data[timestamp*channels + 0] = 0x7f;
|
||||
if (_config.index_markers() && (bits & F_BIT_INDEX))
|
||||
if (_config.index_markers() && (event & F_BIT_INDEX))
|
||||
data[timestamp*channels + 1] = 0x7f;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ public:
|
|||
ByteWriter fluxdataWriter(fluxdata);
|
||||
|
||||
if (_config.align_with_index())
|
||||
fmr.findEvent(F_BIT_INDEX);
|
||||
fmr.skipToEvent(F_BIT_INDEX);
|
||||
|
||||
int revolution = 0;
|
||||
unsigned revTicks = 0;
|
||||
|
|
@ -107,12 +107,14 @@ public:
|
|||
while (revolution < 5)
|
||||
{
|
||||
unsigned ticks;
|
||||
uint8_t bits = fmr.getNextEvent(ticks);
|
||||
int event;
|
||||
fmr.getNextEvent(event, ticks);
|
||||
|
||||
ticksSinceLastPulse += ticks;
|
||||
totalTicks += ticks;
|
||||
revTicks += ticks;
|
||||
|
||||
if (fmr.eof() || (bits & F_BIT_INDEX))
|
||||
if (fmr.eof() || (event & F_BIT_INDEX))
|
||||
{
|
||||
auto* revheader = &trackheader.revolution[revolution];
|
||||
write_le32(revheader->offset, startOffset + sizeof(ScpTrack));
|
||||
|
|
@ -124,7 +126,7 @@ public:
|
|||
startOffset = fluxdataWriter.pos;
|
||||
}
|
||||
|
||||
if (bits & F_BIT_PULSE)
|
||||
if (event & F_BIT_PULSE)
|
||||
{
|
||||
unsigned t = ticksSinceLastPulse * NS_PER_TICK / 25;
|
||||
while (t >= 0x10000)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ public:
|
|||
while (!fmr.eof())
|
||||
{
|
||||
unsigned ticks;
|
||||
uint8_t bits = fmr.getNextEvent(ticks);
|
||||
int event;
|
||||
fmr.getNextEvent(event, ticks);
|
||||
if (fmr.eof())
|
||||
break;
|
||||
|
||||
|
|
@ -55,9 +56,9 @@ public:
|
|||
of << fmt::format("#{} ", (uint64_t)(timestamp * NS_PER_TICK));
|
||||
}
|
||||
|
||||
if (bits & F_BIT_PULSE)
|
||||
if (event & F_BIT_PULSE)
|
||||
of << "1p ";
|
||||
if (bits & F_BIT_INDEX)
|
||||
if (event & F_BIT_INDEX)
|
||||
of << "1i ";
|
||||
|
||||
lasttimestamp = timestamp;
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@ static std::shared_ptr<Fluxmap> readFluxmap(FluxSource& fluxsource, unsigned cyl
|
|||
"{0:.0} ms in {1} bytes\n",
|
||||
fluxmap->duration()/1e6,
|
||||
fluxmap->bytes());
|
||||
if (outputFluxSink)
|
||||
outputFluxSink->writeFlux(cylinder, head, *fluxmap);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
|
|
@ -83,10 +81,13 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
|
|||
auto track = std::make_unique<TrackFlux>();
|
||||
std::set<std::shared_ptr<Sector>> track_sectors;
|
||||
std::set<std::shared_ptr<Record>> track_records;
|
||||
Fluxmap totalFlux;
|
||||
|
||||
for (int retry = config.decoder().retries(); retry >= 0; retry--)
|
||||
{
|
||||
auto fluxmap = readFluxmap(fluxsource, cylinder, head);
|
||||
totalFlux.appendDesync().appendBytes(fluxmap->rawBytes());
|
||||
|
||||
{
|
||||
auto trackdata = decoder.decodeToSectors(fluxmap, cylinder, head);
|
||||
|
||||
|
|
@ -105,6 +106,7 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
|
|||
track_records.insert(trackdata->records.begin(), trackdata->records.end());
|
||||
track->trackDatas.push_back(std::move(trackdata));
|
||||
}
|
||||
|
||||
auto collected_sectors = collect_sectors(track_sectors);
|
||||
std::cout << fmt::format("{} distinct sectors; ", collected_sectors.size());
|
||||
|
||||
|
|
@ -147,6 +149,9 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
|
|||
std::cout << retry << " retries remaining" << std::endl;
|
||||
}
|
||||
|
||||
if (outputFluxSink)
|
||||
outputFluxSink->writeFlux(cylinder, head, totalFlux);
|
||||
|
||||
if (config.decoder().dump_records())
|
||||
{
|
||||
std::cout << "\nRaw (undecoded) records follow:\n\n";
|
||||
|
|
|
|||
|
|
@ -511,6 +511,7 @@ runtest bytes-test tests/bytes.cc
|
|||
runtest compression-test tests/compression.cc
|
||||
runtest csvreader-test tests/csvreader.cc
|
||||
runtest flags-test tests/flags.cc
|
||||
runtest fluxmapreader-test tests/fluxmapreader.cc
|
||||
runtest fluxpattern-test tests/fluxpattern.cc
|
||||
runtest fmmfm-test tests/fmmfm.cc
|
||||
runtest greaseweazle-test tests/greaseweazle.cc
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ enum
|
|||
{
|
||||
F_BIT_PULSE = 0x80,
|
||||
F_BIT_INDEX = 0x40,
|
||||
F_DESYNC = 0xc0
|
||||
F_DESYNC = 0x00,
|
||||
F_EOF = 0x100 /* synthetic, only produced by library */
|
||||
};
|
||||
|
||||
struct frame_header
|
||||
|
|
|
|||
|
|
@ -257,10 +257,11 @@ int mainAnalyseDriveResponse(int argc, const char* argv[])
|
|||
|
||||
FluxmapReader fmr(inFluxmap);
|
||||
fmr.seek((double)period*0.1); /* skip first 10% and last 10% as contains junk */
|
||||
fmr.findEvent(F_BIT_PULSE);
|
||||
fmr.skipToEvent(F_BIT_PULSE);
|
||||
while (fmr.tell().ns() < ((double)period*0.9))
|
||||
{
|
||||
unsigned ticks = fmr.findEvent(F_BIT_PULSE);
|
||||
unsigned ticks;
|
||||
fmr.findEvent(F_BIT_PULSE, ticks);
|
||||
if (ticks < numColumns)
|
||||
frequencies[row][ticks]++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,8 @@ static nanoseconds_t guessClock(const Fluxmap& fluxmap)
|
|||
|
||||
while (!fr.eof())
|
||||
{
|
||||
unsigned interval = fr.findEvent(F_BIT_PULSE);
|
||||
unsigned interval;
|
||||
fr.findEvent(F_BIT_PULSE, interval);
|
||||
if (interval > 0xff)
|
||||
continue;
|
||||
buckets[interval]++;
|
||||
|
|
@ -234,7 +235,9 @@ int mainInspect(int argc, const char* argv[])
|
|||
nanoseconds_t lasttransition = 0;
|
||||
while (!fmr.eof())
|
||||
{
|
||||
ticks += fmr.findEvent(F_BIT_PULSE);
|
||||
unsigned thisTicks;
|
||||
fmr.findEvent(F_BIT_PULSE, thisTicks);
|
||||
ticks += thisTicks;
|
||||
|
||||
nanoseconds_t transition = ticks*NS_PER_TICK;
|
||||
nanoseconds_t next;
|
||||
|
|
|
|||
89
tests/fluxmapreader.cc
Normal file
89
tests/fluxmapreader.cc
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "protocol.h"
|
||||
#include "fmt/format.h"
|
||||
#include "tests.h"
|
||||
#include <sstream>
|
||||
|
||||
static Fluxmap fluxmap(Bytes {
|
||||
F_DESYNC,
|
||||
F_BIT_PULSE | 0x30,
|
||||
F_BIT_INDEX | 0x30,
|
||||
F_BIT_PULSE | F_BIT_INDEX | 0x30,
|
||||
0x30, F_BIT_PULSE | 0x30,
|
||||
F_DESYNC,
|
||||
F_BIT_PULSE | 0x30,
|
||||
0x30,
|
||||
F_DESYNC,
|
||||
0x30,
|
||||
F_BIT_PULSE | 0x30
|
||||
});
|
||||
|
||||
#define ASSERT_NEXT_EVENT(wantedEvent, wantedTicks) \
|
||||
do { \
|
||||
unsigned gotTicks; \
|
||||
int gotEvent; \
|
||||
fmr.getNextEvent(gotEvent, gotTicks); \
|
||||
assert((gotEvent == (wantedEvent)) && (gotTicks == (wantedTicks))); \
|
||||
} while(0)
|
||||
|
||||
#define ASSERT_READ_SPECIFIC_EVENT(wantedEvent, wantedTicks) \
|
||||
do { \
|
||||
unsigned gotTicks; \
|
||||
fmr.findEvent(wantedEvent, gotTicks); \
|
||||
assertThat(gotTicks).isEqualTo(wantedTicks); \
|
||||
} while(0)
|
||||
|
||||
void test_read_all_events()
|
||||
{
|
||||
FluxmapReader fmr(fluxmap);
|
||||
ASSERT_NEXT_EVENT(F_DESYNC, 0);
|
||||
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x30);
|
||||
ASSERT_NEXT_EVENT(F_BIT_INDEX, 0x30);
|
||||
ASSERT_NEXT_EVENT(F_BIT_PULSE | F_BIT_INDEX, 0x30);
|
||||
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x60);
|
||||
ASSERT_NEXT_EVENT(F_DESYNC, 0);
|
||||
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x30);
|
||||
ASSERT_NEXT_EVENT(F_DESYNC, 0x30);
|
||||
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x60);
|
||||
ASSERT_NEXT_EVENT(F_EOF, 0);
|
||||
ASSERT_NEXT_EVENT(F_EOF, 0);
|
||||
}
|
||||
|
||||
void test_read_pulses()
|
||||
{
|
||||
FluxmapReader fmr(fluxmap);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x30);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x60);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x60);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x30);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x90);
|
||||
}
|
||||
|
||||
void test_read_indices()
|
||||
{
|
||||
FluxmapReader fmr(fluxmap);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_BIT_INDEX, 0x60);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_BIT_INDEX, 0x30);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_BIT_INDEX, 6*0x30);
|
||||
}
|
||||
|
||||
void test_read_desyncs()
|
||||
{
|
||||
FluxmapReader fmr(fluxmap);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0xf0);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0x60);
|
||||
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0x60);
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
test_read_all_events();
|
||||
test_read_pulses();
|
||||
test_read_indices();
|
||||
test_read_desyncs();
|
||||
return 0;
|
||||
}
|
||||
|
||||
38
tests/tests.h
Normal file
38
tests/tests.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef TESTS_H
|
||||
#define TESTS_H
|
||||
|
||||
class AssertionError
|
||||
{};
|
||||
|
||||
template <class T>
|
||||
class Subject
|
||||
{
|
||||
public:
|
||||
Subject(const std::string& filename, int lineno, T value):
|
||||
_filename(filename),
|
||||
_lineno(lineno),
|
||||
_value(value) {}
|
||||
|
||||
public:
|
||||
void isEqualTo(T wanted)
|
||||
{
|
||||
if (_value != wanted)
|
||||
fail(fmt::format("wanted {}, got {}", wanted, _value));
|
||||
}
|
||||
|
||||
private:
|
||||
void fail(const std::string& message)
|
||||
{
|
||||
Error() << "assertion failed: " << _filename << ":" << _lineno << ": " << message;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string _filename;
|
||||
int _lineno;
|
||||
T _value;
|
||||
};
|
||||
|
||||
#define assertThat(value) Subject(__FILE__, __LINE__, value)
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Reference in a new issue