From 5159844b0373d96010add2ee272804acdc9a8821 Mon Sep 17 00:00:00 2001 From: Peter De Wachter Date: Tue, 4 Feb 2014 21:51:34 +0100 Subject: [PATCH] Add some basic tests --- Makefile.am | 17 +++ test/lest.hpp | 197 ++++++++++++++++++++++++++++++ test/tempfile.h | 54 +++++++++ test/test_block.cc | 75 ++++++++++++ test/test_lest.cc | 291 +++++++++++++++++++++++++++++++++++++++++++++ test/test_line.cc | 150 +++++++++++++++++++++++ 6 files changed, 784 insertions(+) create mode 100644 test/lest.hpp create mode 100644 test/tempfile.h create mode 100644 test/test_block.cc create mode 100644 test/test_lest.cc create mode 100644 test/test_line.cc diff --git a/Makefile.am b/Makefile.am index d21d560..ee711ac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,3 +25,20 @@ rastertobrlaser_LDADD = $(CUPS_LIBS) brdecode_SOURCES = \ src/brdecode.cc + + +TESTS = test_lest test_line test_block +check_PROGRAMS = $(TESTS) +test_lest_SOURCES = \ + test/test_lest.cc \ + test/lest.h +test_line_SOURCES = \ + test/test_line.cc \ + src/line.h \ + src/line.cc \ + test/lest.h +test_block_SOURCES = \ + test/test_block.cc \ + src/block.h \ + test/tempfile.h \ + test/lest.h diff --git a/test/lest.hpp b/test/lest.hpp new file mode 100644 index 0000000..caf2d56 --- /dev/null +++ b/test/lest.hpp @@ -0,0 +1,197 @@ +// Copyright 2013 by Martin Moene +// +// lest is based on ideas by Kevlin Henney, see video at +// http://skillsmatter.com/podcast/agile-testing/kevlin-henney-rethinking-unit-testing-in-c-plus-plus +// +// Distributed under the Boost Software License, Version 1.0: +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef LEST_LEST_H_INCLUDED +#define LEST_LEST_H_INCLUDED + +#include +#include +#include +#include +#include + +#ifndef lest_NO_SHORT_ASSERTION_NAMES +# define EXPECT lest_EXPECT +# define EXPECT_THROWS lest_EXPECT_THROWS +# define EXPECT_THROWS_AS lest_EXPECT_THROWS_AS +#endif + +#define lest_EXPECT( expr ) \ + try \ + { \ + if ( ! (expr) ) \ + throw lest::failure{ lest_LOCATION, #expr }; \ + } \ + catch( lest::failure const & ) \ + { \ + throw ; \ + } \ + catch( std::exception const & e ) \ + { \ + throw lest::unexpected{ lest_LOCATION, #expr, lest::with_message( e.what() ) }; \ + } \ + catch(...) \ + { \ + throw lest::unexpected{ lest_LOCATION, #expr, "of unknown type" }; \ + } + +#define lest_EXPECT_THROWS( expr ) \ + for (;;) \ + { \ + try { lest::serum( expr ); } catch (...) { break; } \ + throw lest::expected{ lest_LOCATION, #expr }; \ + } + +#define lest_EXPECT_THROWS_AS( expr, excpt ) \ + for (;;) \ + { \ + try { lest::serum( expr ); } catch ( excpt & ) { break; } catch (...) {} \ + throw lest::expected{ lest_LOCATION, #expr, lest::of_type( #excpt ) }; \ + } + +#define lest_LOCATION lest::location{__FILE__, __LINE__} + +namespace lest { + +struct test +{ + const std::string name; + const std::function behaviour; +}; + +struct location +{ + const std::string file; + const int line; + + location( std::string file, int line ) + : file{ file }, line{ line } {} +}; + +struct comment +{ + const std::string text; + + comment( std::string text ) : text{ text } {} + explicit operator bool() { return ! text.empty(); } +}; + +struct message : std::runtime_error +{ + const std::string kind; + const location where; + const comment note; + + message( std::string kind, location where, std::string expr, std::string note = "" ) + : std::runtime_error{ expr }, kind{ kind }, where{ where }, note{ note } {} +}; + +struct failure : message +{ + failure( location where, std::string expr ) + : message{ "failed", where, expr } {} +}; + +struct expected : message +{ + expected( location where, std::string expr, std::string excpt = "" ) + : message{ "failed: didn't get exception", where, expr, excpt } {} +}; + +struct unexpected : message +{ + unexpected( location where, std::string expr, std::string note ) + : message{ "failed: got unexpected exception", where, expr, note } {} +}; + +inline bool serum( bool verum ) { return verum; } + +inline std::string with_message( std::string text ) +{ + return "with message \"" + text + "\""; +} + +inline std::string of_type( std::string text ) +{ + return "of type " + text; +} + +inline std::string pluralise( int n, std::string text ) +{ + return n == 1 ? text : text + "s"; +} + +inline std::ostream & operator<<( std::ostream & os, comment note ) +{ + return os << (note ? " " + note.text : "" ); +} + +inline std::ostream & operator<<( std::ostream & os, location where ) +{ +#ifdef __GNUG__ + return os << where.file << ":" << where.line; +#else + return os << where.file << "(" << where.line << ")"; +#endif +} + +inline void report( std::ostream & os, message const & e, std::string test ) +{ + os << e.where << ": " << e.kind << e.note << ": " << test << ": " << e.what() << std::endl; +} + +template +int run( test const (&specification)[N], std::ostream & os = std::cout ) +{ + int failures = 0; + + for ( auto & testing : specification ) + { + try + { + testing.behaviour(); + } + catch( message const & e ) + { + ++failures; + report( os, e, testing.name ); + } + } + + if ( failures > 0 ) + { + os << failures << " out of " << N << " " << pluralise(N, "test") << " failed." << std::endl; + } + + return failures; +} + +} // namespace lest + +#endif // LEST_LEST_H_INCLUDED diff --git a/test/tempfile.h b/test/tempfile.h new file mode 100644 index 0000000..f83e306 --- /dev/null +++ b/test/tempfile.h @@ -0,0 +1,54 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2014 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#ifndef TEMPFILE_H +#define TEMPFILE_H + +#include +#include +#include + +class tempfile { + public: + explicit tempfile() + : ptr_(0), + size_(0), + file_(open_memstream(&ptr_, &size_)) { + } + + ~tempfile() { + fclose(file_); + free(ptr_); + } + + FILE *file() { + return file_; + } + + std::vector data() { + if (fflush(file_)) + return std::vector(); + return std::vector(ptr_, ptr_ + size_); + } + + private: + char *ptr_; + size_t size_; + FILE *file_; +}; + +#endif // TEMPFILE_H diff --git a/test/test_block.cc b/test/test_block.cc new file mode 100644 index 0000000..0b39a30 --- /dev/null +++ b/test/test_block.cc @@ -0,0 +1,75 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2014 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#include "lest.hpp" +#include +#include +#include "tempfile.h" +#include "src/block.h" + +typedef std::vector vec; + +const lest::test specification[] = { + "Block line limit", + [] { + block b; + for (int i = 0; i < 128; ++i) { + EXPECT(b.line_fits(1)); + b.add_line(vec(1)); + } + EXPECT(!b.line_fits(1)); + }, + + "Block size limit", + [] { + block b; + for (int i = 0; i < 16; ++i) { + EXPECT(b.line_fits(1000)); + b.add_line(vec(1000)); + } + EXPECT(!b.line_fits(400)); + }, + + "Flush", + [] { + block b; + { + tempfile f; + b.flush(f.file()); + EXPECT(f.data().empty()); + } + for (uint8_t n = 0; n < 10; n += 2) { + vec line = {n, static_cast(n+1)}; + EXPECT(b.line_fits(line.size())); + b.add_line(std::move(line)); + } + { + tempfile f; + b.flush(f.file()); + EXPECT(( f.data() == vec{'1','2','w',0,5,0,1,2,3,4,5,6,7,8,9} )); + } + { + tempfile f; + b.flush(f.file()); + EXPECT(f.data().empty()); + } + }, +}; + +int main() { + return lest::run(specification); +} diff --git a/test/test_lest.cc b/test/test_lest.cc new file mode 100644 index 0000000..1eb7d6a --- /dev/null +++ b/test/test_lest.cc @@ -0,0 +1,291 @@ +// Copyright 2013 by Martin Moene +// +// Distributed under the Boost Software License, Version 1.0: +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#include "lest.hpp" +#include + +using namespace lest; + +const lest::test specification[] = +{ + "Function to suppress warning \"expression has no effect\" acts as identity function", [] + { + EXPECT( false == serum( false ) ); + EXPECT( true == serum( true ) ); + }, + + "Function with_message() returns correct string", [] + { + std::string msg = "Let writing tests become irresistibly easy and attractive."; + EXPECT( "with message \"" + msg + "\"" == with_message( msg ) ); + }, + + "Function of_type() returns correct string", [] + { + std::string msg = "this_type"; + EXPECT( "of type " + msg == of_type( msg ) ); + }, + + "Function pluralise() adds 's' except for 1 item", [] + { + std::string word = "hammer"; + EXPECT( word == pluralise( 1, word ) ); + for ( auto i : {0,2,3,4,5,6,7,8,9,10,11,12} ) + EXPECT( word + "s" == pluralise( i, word ) ); + }, + + "Location constructs properly", [] + { + char const * file = __FILE__; int line = __LINE__; + location where{ file, line }; + EXPECT( file == where.file ); + EXPECT( line == where.line ); + }, + + "Comment constructs properly", [] + { + std::string text = __FILE__; + comment note = text; + EXPECT( text == note.text ); + }, + + "Comment converted to bool indicates absence or presence of comment", [] + { + EXPECT( false == bool( comment( "") ) ); + EXPECT( true == bool( comment("x") ) ); + }, + + "Failure exception type constructs and prints properly", [] + { + std::string name = "test-name"; + failure msg( location{"filename.cpp", 765}, "expression" ); + + std::ostringstream os; + report( os, msg, name ); + +#ifndef __GNUG__ + EXPECT( os.str() == "filename.cpp(765): failed: test-name: expression\n" ); +#else + EXPECT( os.str() == "filename.cpp:765: failed: test-name: expression\n" ); +#endif + }, + + "Expected exception type constructs and prints properly", [] + { + std::string name = "test-name"; + expected msg( location{"filename.cpp", 765}, "expression" ); + + std::ostringstream os; + report( os, msg, name ); + +#ifndef __GNUG__ + EXPECT( os.str() == "filename.cpp(765): failed: didn't get exception: test-name: expression\n" ); +#else + EXPECT( os.str() == "filename.cpp:765: failed: didn't get exception: test-name: expression\n" ); +#endif + }, + + "Unexpected exception type constructs and prints properly", [] + { + std::string name = "test-name"; + unexpected msg( location{"filename.cpp", 765}, "expression", "exception-type" ); + + std::ostringstream os; + report( os, msg, name ); + +#ifndef __GNUG__ + EXPECT( os.str() == "filename.cpp(765): failed: got unexpected exception exception-type: test-name: expression\n" ); +#else + EXPECT( os.str() == "filename.cpp:765: failed: got unexpected exception exception-type: test-name: expression\n" ); +#endif + }, + + "Expect generates no message exception for a succeeding test", [] + { + test pass = { "P", [] { EXPECT( true ); } }; + + try { pass.behaviour(); } + catch(...) { throw failure(location{__FILE__,__LINE__}, "unexpected error generated"); } + }, + + "Expect generates a message exception for a failing test", [] + { + test fail = { "F", [] { EXPECT( false ); } }; + + for (;;) + { + try { fail.behaviour(); } catch ( message & ) { break; } + throw failure(location{__FILE__,__LINE__}, "no error generated"); + } + }, + + "Expect succeeds for success (true) and failure (false)", [] + { + test pass[] = {{ "P", [] { EXPECT( true ); } }}; + test fail[] = {{ "F", [] { EXPECT( false ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass, os ) ); + EXPECT( 1 == run( fail, os ) ); + }, + + "Expect succeeds for integer comparation", [] + { + test pass [] = {{ "P" , [] { EXPECT( 7 == 7 ); EXPECT( 7 != 8 ); + EXPECT( 7 >= 6 ); EXPECT( 7 <= 8 ); + EXPECT( 7 > 6 ); EXPECT( 7 < 8 ); } }}; + test fail_1[] = {{ "F1", [] { EXPECT( 7 == 8 ); } }}; + test fail_2[] = {{ "F2", [] { EXPECT( 7 != 7 ); } }}; + test fail_3[] = {{ "F3", [] { EXPECT( 7 <= 6 ); } }}; + test fail_4[] = {{ "F4", [] { EXPECT( 7 >= 8 ); } }}; + test fail_5[] = {{ "F5", [] { EXPECT( 7 < 6 ); } }}; + test fail_6[] = {{ "F6", [] { EXPECT( 7 > 8 ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass , os ) ); + EXPECT( 1 == run( fail_1, os ) ); + EXPECT( 1 == run( fail_2, os ) ); + EXPECT( 1 == run( fail_3, os ) ); + EXPECT( 1 == run( fail_4, os ) ); + EXPECT( 1 == run( fail_5, os ) ); + EXPECT( 1 == run( fail_6, os ) ); + }, + + "Expect succeeds for string comparation", [] + { + std::string a("a"); std::string b("b"); + test pass [] = {{ "P" , [=]() { EXPECT( a == a ); EXPECT( a != b ); + EXPECT( b >= a ); EXPECT( a <= b ); + EXPECT( b > a ); EXPECT( a < b ); } }}; + test fail_1[] = {{ "F1", [=]() { EXPECT( a == b ); } }}; + test fail_2[] = {{ "F2", [=]() { EXPECT( a != a ); } }}; + test fail_3[] = {{ "F3", [=]() { EXPECT( b <= a ); } }}; + test fail_4[] = {{ "F4", [=]() { EXPECT( a >= b ); } }}; + test fail_5[] = {{ "F5", [=]() { EXPECT( b < a ); } }}; + test fail_6[] = {{ "F6", [=]() { EXPECT( a > b ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass , os ) ); + EXPECT( 1 == run( fail_1, os ) ); + EXPECT( 1 == run( fail_2, os ) ); + EXPECT( 1 == run( fail_3, os ) ); + EXPECT( 1 == run( fail_4, os ) ); + EXPECT( 1 == run( fail_5, os ) ); + EXPECT( 1 == run( fail_6, os ) ); + }, + + "Function run() returns the right failure count", [] + { + test pass [] = {{ "P" , [] { EXPECT( 1==1 ); } }}; + test fail_1[] = {{ "F1", [] { EXPECT( 0==1 ); } }}; + test fail_3[] = {{ "F1", [] { EXPECT( 0==1 ); } }, + { "F2", [] { EXPECT( 0==1 ); } }, + { "F3", [] { EXPECT( 0==1 ); } },}; + + std::ostringstream os; + + EXPECT( 0 == run( pass , os ) ); + EXPECT( 1 == run( fail_1, os ) ); + EXPECT( 3 == run( fail_3, os ) ); + }, + + "Expect succeeds with an unexpected standard exception", [] + { + std::string text = "hello-world"; + test pass[] = {{ "P", [=]() { EXPECT( (throw std::runtime_error(text), true) ); } }}; + + std::ostringstream os; + + EXPECT( 1 == run( pass, os ) ); + EXPECT( std::string::npos != os.str().find(text) ); + }, + + "Expect succeeds with an unexpected non-standard exception", [] + { + test pass[] = {{ "P", [] { EXPECT( (throw 77, true) ); } }}; + + std::ostringstream os; + + EXPECT( 1 == run( pass, os ) ); + }, + + "Expect_throws succeeds with an expected standard exception", [] + { + std::string text = "hello-world"; + test pass[] = {{ "P", [=]() { EXPECT_THROWS( (throw std::runtime_error(text), true) ); } }}; + test fail[] = {{ "F", [ ]() { EXPECT_THROWS( true ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass, os ) ); + EXPECT( 1 == run( fail, os ) ); + }, + + "Expect_throws succeeds with an expected non-standard exception", [] + { + test pass[] = {{ "P", [] { EXPECT_THROWS( (throw 77, true) ); } }}; + test fail[] = {{ "F", [] { EXPECT_THROWS( true ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass, os ) ); + EXPECT( 1 == run( fail, os ) ); + }, + + "Expect_throws_as succeeds with a specific expected standard exception", [] + { + test pass[] = {{ "P", [] { EXPECT_THROWS_AS( (throw std::bad_alloc(), true), std::bad_alloc ); } }}; + test fail[] = {{ "F", [] { EXPECT_THROWS_AS( (throw std::bad_alloc(), true), std::runtime_error ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass, os ) ); + EXPECT( 1 == run( fail, os ) ); + }, + + "Expect_throws_as succeeds with a specific expected non-standard exception", [] + { + test pass[] = {{ "P", [] { EXPECT_THROWS_AS( (throw 77, true), int ); } }}; + test fail[] = {{ "F", [] { EXPECT_THROWS_AS( (throw 77, true), std::runtime_error ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass, os ) ); + EXPECT( 1 == run( fail, os ) ); + }, +}; + +int main() +{ + return lest::run( specification ); +} + +// cl -nologo -Wall -EHsc test_lest.cpp && test_lest +// g++ -Wall -Wextra -Weffc++ -std=c++11 -o test_lest.exe test_lest.cpp && test_lest + diff --git a/test/test_line.cc b/test/test_line.cc new file mode 100644 index 0000000..fd89c57 --- /dev/null +++ b/test/test_line.cc @@ -0,0 +1,150 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2014 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#include "lest.hpp" +#include +#include +#include +#include "src/line.h" + +typedef std::vector vec; + + +uint8_t sub(uint8_t offset, uint8_t count) { + assert(offset < 16); + assert(count < 8); + return (offset << 3) | count; +} + +uint8_t rep(uint8_t offset, uint8_t count) { + assert(offset < 4); + assert(count < 32); + return 128 | (offset << 5) | count; +} + + +const lest::test specification[] = { + "Don't crash on zero-length lines", + [] { + EXPECT(( encode_line(vec{}) == vec{0xFF} )); + EXPECT(( encode_line(vec{}, vec{}) == vec{0xFF} )); + }, + + "Encoding an initial blank line", + [] { + EXPECT(( encode_line(vec{0,0,0}) == vec{0xFF} )); + }, + + "Encoding an initial non-blank line", + [] { + EXPECT(( encode_line(vec{1,2,3}) == (vec{1,sub(0,2),1,2,3}) )); + }, + + "Encoding a (non-initial) blank line", + [] { + EXPECT(( encode_line(vec{0,0,0}, vec{1,2,3}) == vec{0xFF} )); + }, + + "Encoding a repeated line", + [] { + EXPECT(( encode_line(vec{1,2,3}, vec{1,2,3}) == vec{0} )); + }, + + "Using a subsitute command", + [] { + EXPECT(( encode_line(vec{0,0,1,2,3,0,0}, vec(7)) == vec{1,sub(2,2),1,2,3} )); + }, + + "Using a repeat command", + [] { + EXPECT(( encode_line(vec{0,0,1,1,0,0}, vec(6)) == vec{1,rep(2,0),1} )); + }, + + "Repeat command followed by substitute command", + [] { + EXPECT(( encode_line(vec{1,1,1,2,3}, vec(5)) == vec{2,rep(0,1),1,sub(0,1),2,3} )); + }, + + "Substitute comand followed by repeat command", + [] { + EXPECT(( encode_line(vec{3,2,1,1,1}, vec(5)) == vec{2,sub(0,1),3,2,rep(0,1),1} )); + }, + + "Substitute with an unmodified byte in the middle", + [] { + EXPECT(( encode_line(vec{1,2,3,0,1,2,3}, vec(7)) == vec{1,sub(0,6),1,2,3,0,1,2,3} )); + }, + + "Substitue with two unmodified bytes in the middle", + [] { + EXPECT(( encode_line(vec{1,2,3,0,0,1,2,3}, vec(8)) == vec{2,sub(0,2),1,2,3,sub(2,2),1,2,3} )); + }, + + "Repeat with an unmodified byte in the middle", + [] { + EXPECT(( encode_line(vec{1,1,1,0,1,1,1}, vec(7)) == vec{2,rep(0,1),1,rep(1,1),1} )); + }, + + "254 edits needed for a single line", + [] { + vec line, result; + for (int i = 0; i < 254; ++i) + line.insert(line.end(), {0,0,1}); + result.push_back(254); + for (int i = 0; i < 254; ++i) + result.insert(result.end(), {sub(2,0),1}); + EXPECT(( encode_line(line, vec(line.size())) == result )); + }, + + "Give up if more than 254 edits needed...", + [] { + vec line, result; + for (int i = 0; i < 255; ++i) + line.insert(line.end(), {0,0,1}); + result.push_back(254); + for (int i = 0; i < 253; ++i) + result.insert(result.end(), {sub(2,0),1}); + result.insert(result.end(), {sub(2,3),1,0,0,1}); + EXPECT(( encode_line(line, vec(line.size())) == result )); + }, + + "Repeat command with overflow bytes", + [] { + vec line(3, 0); + line.insert(line.end(), 512, 1); + vec ref(line.size(), 0); + vec expected{1,rep(3,31),0,255,224,1}; + EXPECT(encode_line(line, ref) == expected); + }, + + "Substitute command with overflow bytes", + [] { + vec expected{1,sub(15,7),255,0,255,237}; + vec line(270, 0); + for (int i = 0; i < 250; ++i) { + expected.insert(expected.end(), {1,2}); + line.insert(line.end(), {1,2}); + } + vec ref(line.size(), 0); + EXPECT(encode_line(line, ref) == expected); + }, +}; + + +int main() { + return lest::run(specification); +}