KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_allegro_boards.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
26#include "allegro_block_tests.h"
27#include "allegro_test_utils.h"
28
29#include <filesystem>
30#include <fstream>
31
32#include <boost/test/data/test_case.hpp>
33
34#include <json_common.h>
35
36#include <board.h>
37#include <reporter.h>
39
42
44
55
56using namespace ALLEGRO;
57using namespace KI_TEST;
58
59
60static std::vector<uint8_t> loadDataByUri( const std::string& aDataSource )
61{
62 // For now, we only support loading from files, but in the future we could also support
63 // loading from other sources (e.g. from compiled-in data, base64, etc.)
64
65 // Split off the protocol (e.g. "file://") if present, to get the actual path
66 std::string path, protocol;
67 const std::string sep = "://";
68
69 size_t sepPos = aDataSource.find( sep );
70 if( sepPos != std::string::npos )
71 {
72 protocol = aDataSource.substr( 0, sepPos );
73 path = aDataSource.substr( sepPos + sep.size() );
74 }
75 else
76 {
77 // No protocol
78 throw std::runtime_error( "Unsupported data source URI (missing protocol): " + aDataSource );
79 }
80
81 if( protocol == "file" )
82 {
83 // This means get it from a file in the QD data
84 return KI_TEST::LoadBinaryData( path, std::nullopt );
85 }
86
87 throw std::runtime_error( "Unsupported data source protocol: " + protocol );
88}
89
90
91ALLEGRO::FMT_VER getFormatVersionFromStr( const std::string& aFmtVerStr )
92{
93 // clang-format off
94 static const std::map<std::string, ALLEGRO::FMT_VER> fmtVerStrMap{
95 { "16.0", FMT_VER::V_160 },
96 { "16.2", FMT_VER::V_162 },
97 { "16.4", FMT_VER::V_164 },
98 { "16.5", FMT_VER::V_165 },
99 { "16.6", FMT_VER::V_166 },
100 { "17.2", FMT_VER::V_172 },
101 { "17.4", FMT_VER::V_174 },
102 { "17.5", FMT_VER::V_175 },
103 { "18.0", FMT_VER::V_180 },
104 };
105 // clang-format on
106
107 auto it = fmtVerStrMap.find( aFmtVerStr );
108 if( it != fmtVerStrMap.end() )
109 {
110 return it->second;
111 }
112
113 return FMT_VER::V_UNKNOWN;
114}
115
116
117static std::unique_ptr<HEADER_TEST_INFO> createHeaderTestEntry( const std::string& boardDir,
118 const nlohmann::json& headerTestEntry )
119{
120 std::unique_ptr<HEADER_TEST_INFO> headerTest = nullptr;
121
122 if( !headerTestEntry.is_object() )
123 {
124 throw std::runtime_error( "Header test entry is not a valid JSON object" );
125 }
126
127 // Default header location
128 const std::string headerDataUri = std::string( "file://" ) + boardDir + "header.bin";
129
130 return std::make_unique<HEADER_TEST_INFO>( headerDataUri, headerTestEntry.value( "skip", false ) );
131}
132
133
134static BLK_TEST_INFO createBlockTestEntry( const std::string& boardDir, const nlohmann::json& blockTestEntry )
135{
136 if( !blockTestEntry.contains( "type" ) || !blockTestEntry["type"].is_string() )
137 {
138 throw std::runtime_error( "Block test entry is missing a valid 'type' field" );
139 }
140
141 const std::string blockTypeStr = blockTestEntry["type"];
142 const uint8_t blockType = std::stoul( blockTypeStr, nullptr, 0 );
143
144 const std::string block_offset = blockTestEntry["offset"];
145 const size_t blockOffset = std::stoul( block_offset, nullptr, 0 );
146
147 const bool skipBlock = blockTestEntry.value( "skip", false );
148
149 // Default block data location is a file with a name based on the block type and offset
150 wxString blockDataLoc = wxString::Format( "0x%02x_0x%08zx.bin", blockType, blockOffset );
151
152 const std::string blockDataUri = std::string( "file://" ) + boardDir + blockDataLoc.ToStdString();
153
154 const auto blockTestCallback = [&]( const ALLEGRO::BLOCK_BASE& aBlock )
155 {
156 // For now, we don't have any specific expectations for the contents of the block, but we could add some if needed.
157 // For example, commonly used fields.
158 // Often just parsing successfully is enough.
159 };
160
161 return BLK_TEST_INFO{
162 blockType,
163 blockOffset,
164 skipBlock,
165 blockDataUri,
166 blockTestCallback,
167 };
168}
169
170
177
178
179static BOARD_TEST_INFO createBoardTestInfo( const std::string& aBrdName, const nlohmann::json& boardTestEntry )
180{
181 const std::string boardDir = AllegroBoardDataDir( aBrdName );
182
183 const ALLEGRO::FMT_VER formatVersion =
184 getFormatVersionFromStr( boardTestEntry.value( "formatVersion", "unknown" ) );
185
186 return BOARD_TEST_INFO{ aBrdName, boardDir, formatVersion };
187}
188
189
191{
192public:
193 void RunHeaderTest( const std::string& aBrdName, const nlohmann::json& aBoardTestJson )
194 {
195 BOOST_TEST_CONTEXT( wxString::Format( "Testing header from board %s", aBrdName ) )
196 {
197 BOARD_TEST_INFO brdDef = createBoardTestInfo( aBrdName, aBoardTestJson );
198
199 const std::string boardDir = AllegroBoardDataDir( aBrdName );
200
201 std::unique_ptr<HEADER_TEST_INFO> headerTest = nullptr;
202
203 if( aBoardTestJson.contains( "header" ) && aBoardTestJson["header"].is_object() )
204 {
205 headerTest = createHeaderTestEntry( boardDir, aBoardTestJson["header"] );
206 }
207
208 BOOST_REQUIRE( headerTest != nullptr );
209
210 if( headerTest->m_Skip )
211 {
212 BOOST_TEST_MESSAGE( "Skipping test for this header" );
213 return;
214 }
215
216 // Read in the header data
217 std::vector<uint8_t> headerData = loadDataByUri( headerTest->m_HeaderDataSource );
218 FILE_STREAM fileStream( headerData.data(), headerData.size() );
219 HEADER_PARSER parser( fileStream );
220
221 std::unique_ptr<FILE_HEADER> header = parser.ParseHeader();
222
223 BOOST_REQUIRE( header != nullptr );
224
225 // We don't expect any known header to be in an unknown format
227
228 // Make sure the format version matches what the board is expected to be
229 if( brdDef.m_FormatVersion != FMT_VER::V_UNKNOWN )
230 {
231 BOOST_TEST( parser.GetFormatVersion() == brdDef.m_FormatVersion );
232 }
233
234 // Other header tests - validate some commonly used fields?
235
236 // The parser should have consumed all the data in the header
237 BOOST_TEST( fileStream.Position() == headerData.size() );
238 }
239 }
240
241
242 void RunBlockTest( const std::string& aBrdName, const nlohmann::json& aBoardTestJson,
243 const nlohmann::json& aBlockTestJson )
244 {
245 const BOARD_TEST_INFO brdDef = createBoardTestInfo( aBrdName, aBoardTestJson );
246 const BLK_TEST_INFO blockTest = createBlockTestEntry( AllegroBoardDataDir( aBrdName ), aBlockTestJson );
247
248 BOOST_TEST_CONTEXT( wxString::Format( "Testing block type %#02x at offset %#010zx from board %s",
249 blockTest.m_BlockType, blockTest.m_BlockOffset, aBrdName ) )
250 {
251 if( blockTest.m_Skip )
252 {
253 BOOST_TEST_MESSAGE( "Skipping test for this block" );
254 return;
255 }
256
257 // Read in the block data, and create a FILE_STREAM for it
258 std::vector<uint8_t> data = loadDataByUri( blockTest.m_DataSource );
259 FILE_STREAM fileStream( data.data(), data.size() );
260
261 BLOCK_PARSER parser( fileStream, brdDef.m_FormatVersion );
262
263 bool endOfObjectsMarker = false;
264 std::unique_ptr<BLOCK_BASE> block = parser.ParseBlock( endOfObjectsMarker );
265
266 // Compare the result to the expected results
267 BOOST_REQUIRE( block != nullptr );
268
269 if( blockTest.m_ValidateFunc )
270 {
271 BOOST_TEST_CONTEXT( "Validating block contents" )
272 {
273 blockTest.m_ValidateFunc( *block );
274 }
275 }
276
277 // The parser should have consumed all the data in the block
278 BOOST_TEST( fileStream.Position() == data.size() );
279
280 // Look up and run additional ad-hoc block-level tests
281 KI_TEST::RunAdditionalBlockTest( aBrdName, blockTest.m_BlockOffset, *block );
282
283 // Now try to convert the block into a DB_OBJ
284 ALLEGRO::BRD_DB brd;
285 ALLEGRO::BRD_DB::OBJ_FACTORY objFactory( brd );
286 std::unique_ptr<ALLEGRO::DB_OBJ> dbObj = objFactory.CreateObject( *block );
287
288 // Not all blocks convert to DB_OBJ yet, but at least it mustn't crash.
289 // Eventually, this should always succeed.
290 if( dbObj )
291 {
292 KI_TEST::RunAdditionalObjectTest( aBrdName, blockTest.m_BlockOffset, *dbObj );
293 }
294 }
295 }
296};
297
298
299void RunBoardLoad( const std::string& aBrdName, const nlohmann::json& aBoardTestJson )
300{
301 BOARD* board = nullptr;
302
303 BOOST_TEST_CONTEXT( wxString::Format( "Testing load from board %s", aBrdName ) )
304 {
305 if( aBoardTestJson.contains( "boardFile" ) )
306 {
307 const std::string boardFilePath =
308 KI_TEST::AllegroBoardDataDir( aBrdName ) + aBoardTestJson["boardFile"].get<std::string>();
309
310 if( !std::filesystem::exists( boardFilePath ) )
311 {
312 BOOST_FAIL( "Board file does not exist: " + boardFilePath );
313 return;
314 }
315
316 CAPTURING_REPORTER reporter;
317 board = KI_TEST::ALLEGRO_CACHED_LOADER::GetInstance().LoadAndCache( boardFilePath, &reporter );
318
319 const bool expectLoadFailure = aBoardTestJson.value( "expectLoadFailure", false );
320
321 if( expectLoadFailure )
322 {
323 BOOST_CHECK( board == nullptr );
324 BOOST_TEST_MESSAGE( "Board load was expected to fail, and it did." );
325 return;
326 }
327 else
328 {
329 BOOST_CHECK( board != nullptr );
330 BOOST_TEST_MESSAGE( "Board load was expected to succeed, and it did." );
331
332 // Can allow a certain number of warnings, but no errors
333 BOOST_TEST( reporter.GetErrorCount() == 0 );
334
335 // Can check max warnings here if we want
336 if( reporter.GetWarningCount() > 0 )
337 {
338 std::ostringstream ss;
339 ss << aBrdName << ": " << reporter.GetWarningCount() << " warnings";
340
341 for( const auto& msg : reporter.GetMessages() )
342 {
343 if( msg.severity == RPT_SEVERITY_WARNING )
344 {
345 ss << "\n " << msg.text;
346 }
347 }
348
349 BOOST_TEST_MESSAGE( ss.str() );
350 }
351
352 KI_TEST::PrintBoardStats( board, aBrdName );
353 }
354 }
355 }
356}
357
358
359void RunBoardExpectations( const std::string& aBrdName, const nlohmann::json& aBoardTestJson )
360{
361 if( !aBoardTestJson.contains( "boardFile" ) )
362 {
363 BOOST_FAIL( "Board test JSON does not contain a 'boardFile' entry" );
364 return;
365 }
366
367 const std::string boardFilePath =
368 KI_TEST::AllegroBoardDataDir( aBrdName ) + aBoardTestJson["boardFile"].get<std::string>();
369
371
372 if( !board )
373 {
374 BOOST_FAIL( "Failed to load cached board for expectations test: " + boardFilePath );
375 return;
376 }
377
378 BOOST_TEST_CONTEXT( "Running board expectations" )
379 {
380 std::unique_ptr<BOARD_EXPECTATION_TEST> boardExpectationTest = nullptr;
381
382 // There are default expectations that apply unless otherwise specified
383 {
384 bool hasContent = board->GetNetCount() > 0 || board->Footprints().size() > 0;
385 BOOST_CHECK( hasContent ); // The board should have some content (e.g. nets or footprints)
386 }
387
388 // Load board expectations from the JSON file and create expectation objects
389 if( aBoardTestJson.contains( "boardExpectations" ) )
390 {
391 boardExpectationTest = BOARD_EXPECTATION_TEST::CreateFromJson( aBrdName, aBoardTestJson["boardExpectations"] );
392 }
393
394 if( boardExpectationTest )
395 {
396 boardExpectationTest->RunTest( *board );
397 }
398 }
399}
400
401
407{
408 uint8_t m_BlockType;
411 const nlohmann::json& m_BlockTestJson;
412};
413
419{
420 // The board name in the JSON registry
421 std::string m_BrdName;
422
423 // Handy ref to the JSON entry for this board test
424 const nlohmann::json& m_BrdTestJson;
425
427 std::vector<ALLEGRO_BLOCK_TEST> m_BlockTests;
430};
431
432
441{
442 nlohmann::json m_Json;
443 std::vector<ALLEGRO_BOARD_TEST_REF> m_BoardTests;
444
445 const nlohmann::json& GetBoardJson( const std::string& aBoardName ) const
446 {
447 return m_Json["boards"][aBoardName];
448 }
449};
450
451
459static std::vector<ALLEGRO_BOARD_TEST_REF> getBoardTestDefinitions( const nlohmann::json& aJson )
460{
461 // For now, we just return a hardcoded list of the boards we have test data for, but in the future
462 // we could make this dynamic by e.g. scanning the data dir or having a registry file.
463
464 if( !aJson.contains( "boards" ) || !aJson["boards"].is_object() )
465 {
466 throw std::runtime_error( "Test register JSON file does not contain a valid 'boards' object." );
467 }
468
469 // Iterate the "boards" dict
470 std::vector<ALLEGRO_BOARD_TEST_REF> boardTests;
471
472 // Build only the very basic test definitions here, just enough to construct the test cases metadata
473 for( auto& [boardName, boardEntry] : aJson["boards"].items() )
474 {
475 ALLEGRO_BOARD_TEST_REF boardTestRef{
476 .m_BrdName = boardName,
477 .m_BrdTestJson = boardEntry,
478 .m_HasHeaderTest = boardEntry.contains( "header" ),
479 .m_BlockTests = {},
480 .m_HasBoardFile = boardEntry.contains( "boardFile" ),
481 .m_HasExpectations = boardEntry.contains( "boardExpectations" ),
482 };
483
484 if( boardEntry.contains( "blocks" ) && boardEntry["blocks"].is_array() )
485 {
486 for( const auto& blockTestEntry : boardEntry["blocks"] )
487 {
488 if( !blockTestEntry.contains( "type" ) || !blockTestEntry["type"].is_string() )
489 {
490 throw std::runtime_error( "Block test entry is missing a valid 'type' field" );
491 }
492
493 if( !blockTestEntry.contains( "offset" ) || !blockTestEntry["offset"].is_string() )
494 {
495 throw std::runtime_error( "Block test entry is missing a valid 'offset' field" );
496 }
497
498 const std::string blockTypeStr = blockTestEntry["type"];
499 const uint8_t blockType = std::stoul( blockTypeStr, nullptr, 0 );
500
501 const std::string block_offset = blockTestEntry["offset"];
502 const size_t blockOffset = std::stoul( block_offset, nullptr, 0 );
503
504 boardTestRef.m_BlockTests.push_back( ALLEGRO_BLOCK_TEST{
505 blockType,
506 blockOffset,
507 blockTestEntry,
508 } );
509 }
510 }
511
512 boardTests.push_back( std::move( boardTestRef ) );
513 }
514
515 return boardTests;
516}
517
518
520{
521 static ALLEGRO_BLOCK_TEST_REGISTRY registry = []()
522 {
524
525 // There is currently one registry file, but we could have more
526 const std::filesystem::path testRegisterJsonPath( AllegroBoardDataDir( "" ) + "board_data_registry.json" );
527 std::ifstream jsonFileStream( testRegisterJsonPath );
528
529 reg.m_Json = nlohmann::json::parse( jsonFileStream, nullptr, false, true );
531
532 return reg;
533 }();
534
535 return registry;
536}
537
538
542static std::vector<std::string> getBoardTestLabels( const nlohmann::json& boardTestJson )
543{
544 std::vector<std::string> labels;
545
546 if( boardTestJson.contains( "testLabels" ) && boardTestJson["testLabels"].is_array() )
547 {
548 for( const auto& label : boardTestJson["testLabels"] )
549 {
550 if( label.is_string() )
551 {
552 labels.push_back( label.get<std::string>() );
553 }
554 }
555 }
556
557 return labels;
558}
559
560
570static std::vector<boost::unit_test::test_suite*> buildAllegroBoardSuites()
571{
572 using BTS = boost::unit_test::test_suite;
573 std::vector<BTS*> suites;
574 BTS* blockSuite = suites.emplace_back( BOOST_TEST_SUITE( "AllegroBlocks" ) );
575 const ALLEGRO_BLOCK_TEST_REGISTRY* registry = nullptr;
576
577 try
578 {
579 registry = &buildTestRegistry();
580 }
581 catch( const std::exception& ex )
582 {
583 std::string msg = "Failed to load Allegro block test definitions: " + std::string( ex.what() );
584
585 // Register one failing test to report the error, so that we don't just silently skip all the tests
586 const auto failingTestFunction = [=]()
587 {
588 BOOST_FAIL( msg );
589 };
590
591 blockSuite->add(
592 boost::unit_test::make_test_case(
593 failingTestFunction,
594 "FailureToLoadTestDefinitions",
595 __FILE__,
596 __LINE__
597 )
598 );
599
600 return suites;
601 }
602
603 for( const ALLEGRO_BOARD_TEST_REF& boardTest : registry->m_BoardTests )
604 {
605 BTS* boardSuite = BOOST_TEST_SUITE( boardTest.m_BrdName );
606 blockSuite->add( boardSuite );
607
608 const nlohmann::json& boardTestJson = registry->GetBoardJson( boardTest.m_BrdName );
609
610 if( boardTest.m_HasHeaderTest )
611 {
612 // Note: captures ref-to-static
613 const auto testRunFunction = [&]()
614 {
616 fixture.RunHeaderTest( boardTest.m_BrdName, boardTestJson );
617 };
618
619 boardSuite->add(
620 boost::unit_test::make_test_case(
621 testRunFunction,
622 "Header",
623 __FILE__,
624 __LINE__
625 )
626 );
627 }
628
629 for( const auto& blockTest : boardTest.m_BlockTests )
630 {
631 const auto testRunFunction = [&]()
632 {
634 fixture.RunBlockTest( boardTest.m_BrdName, boardTestJson, blockTest.m_BlockTestJson );
635 };
636
637 wxString testName =
638 wxString::Format( "Block_0x%02x_offset_0x%010zx", blockTest.m_BlockType, blockTest.m_BlockOffset );
639
640 boardSuite->add(
641 boost::unit_test::make_test_case(
642 testRunFunction,
643 testName.ToStdString(),
644 __FILE__,
645 __LINE__
646 )
647 );
648 }
649 }
650
651 // Separate test suite for board expectations, which will run after the header and block parsing tests,
652 // and will have access to the fully parsed board (and obviously be much slower)
653 BTS* boardSuites = suites.emplace_back( BOOST_TEST_SUITE( "AllegroBoards" ) );
654
655 for( const ALLEGRO_BOARD_TEST_REF& boardTest : registry->m_BoardTests )
656 {
657 if( !boardTest.m_HasBoardFile )
658 {
659 continue;
660 }
661
662 // Board-level suite
663 BTS* boardSuite = BOOST_TEST_SUITE( boardTest.m_BrdName );
664 boardSuites->add( boardSuite );
665
666 const nlohmann::json& boardTestData = registry->GetBoardJson( boardTest.m_BrdName );
667
668 const std::vector<std::string> testLabels = getBoardTestLabels( boardTestData );
669 boost::unit_test::test_case* loadingTestCase = nullptr;
670
671 if( boardTest.m_HasBoardFile )
672 {
673 const auto testLoadFunction = [&]()
674 {
675 RunBoardLoad( boardTest.m_BrdName, boardTestData );
676 };
677
678 // The first test unit loads (and caches) the board.
679 loadingTestCase = boost::unit_test::make_test_case(
680 testLoadFunction,
681 "Import",
682 __FILE__,
683 __LINE__
684 );
685
686 for( const std::string& label : testLabels )
687 {
688 loadingTestCase->add_label( label );
689 }
690
691 boardSuite->add( loadingTestCase );
692 }
693
694 if( boardTest.m_HasExpectations )
695 {
696 const auto testRunFunction = [&]()
697 {
698 RunBoardExpectations( boardTest.m_BrdName, boardTestData );
699 };
700
701 boost::unit_test::test_case* expectationTestCase = boost::unit_test::make_test_case(
702 testRunFunction,
703 "Expectations",
704 __FILE__,
705 __LINE__
706 );
707
708 // We can only run the expectations test after the board has been loaded
709 // and that test passes.
710 BOOST_REQUIRE( loadingTestCase != nullptr );
711 expectationTestCase->depends_on( loadingTestCase );
712 boardSuite->add( expectationTestCase );
713 }
714 }
715
716 return suites;
717}
718
719
725{
727 {
728 std::vector<boost::unit_test::test_suite*> suites = buildAllegroBoardSuites();
729
730 for( boost::unit_test::test_suite* suite : suites )
731 {
732 boost::unit_test::framework::master_test_suite().add( suite );
733 }
734 }
General utilities for PCB file IO for QA programs.
The base class for all blocks in the main body of an Allegro file.
The block parser is responsible for parsing individual blocks of data from the file stream.
std::unique_ptr< BLOCK_BASE > ParseBlock(bool &aEndOfObjectsMarker)
Parse one block from the stream, returning a BLOCK_BASE representing the raw data of the block.
Converts blocks of "raw" binary-ish data into a DB_OBJ of the appropriate type to be stored in the DB...
Definition allegro_db.h:861
std::unique_ptr< DB_OBJ > CreateObject(const BLOCK_BASE &aBlock) const
An Allegro database that represents a .brd file (amd presumably .dra)
Definition allegro_db.h:849
Stream that reads primitive types from a memory buffer containing Allegro .brd (or ....
size_t Position() const
Parses a .brd header, taking care of any version-specific differences.
std::unique_ptr< FILE_HEADER > ParseHeader()
FMT_VER GetFormatVersion() const
Get the parsed format version.
void RunHeaderTest(const std::string &aBrdName, const nlohmann::json &aBoardTestJson)
void RunBlockTest(const std::string &aBrdName, const nlohmann::json &aBoardTestJson, const nlohmann::json &aBlockTestJson)
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
static ALLEGRO_CACHED_LOADER & GetInstance()
Get the singleton instance of the Allegro board cache loader.
static std::unique_ptr< BOARD_EXPECTATION_TEST > CreateFromJson(const std::string &aBrdName, const nlohmann::json &aBrdExpectations)
BOARD * LoadAndCache(const std::string &aFilePath, REPORTER *aReporter)
Load (or reload) board for the given file path and send the load messages to the given reporter.
BOARD * GetCachedBoard(const std::string &aFilePath)
Get a cached board for the given file path, or load it if not already cached, without forcing a reloa...
Custom REPORTER that captures all messages for later analysis in the unit test framework.
const std::vector< MESSAGE > & GetMessages() const
FMT_VER
The format of an Allego file.
void RunAdditionalObjectTest(const std::string &aBoardName, size_t aBlockOffset, const ALLEGRO::DB_OBJ &aDbObj)
Look up and run any additional ad-hoc tests for a DB_OBJ (parsed and converted block)
std::string AllegroBoardDataDir(const std::string &aBoardName)
void RunAdditionalBlockTest(const std::string &aBoardName, size_t aBlockOffset, const ALLEGRO::BLOCK_BASE &aBlock)
Look up and run any additional ad-hoc tests for a block.
std::vector< uint8_t > LoadBinaryData(const std::string &aFilePath, std::optional< size_t > aLoadBytes=std::nullopt)
Load the contents of a file into a vector of bytes.
void PrintBoardStats(const BOARD *aBoard, const std::string &aBoardName)
Print detailed board statistics for debugging using test-framework logging.
@ RPT_SEVERITY_WARNING
The registry of known Allegro board and block tests, populated at static init time by reading the JSO...
std::vector< ALLEGRO_BOARD_TEST_REF > m_BoardTests
const nlohmann::json & GetBoardJson(const std::string &aBoardName) const
Just enough information about the board and block tests to be able to name and register the test case...
const nlohmann::json & m_BlockTestJson
Handy ref to the JSON entry for this block test.
Just enough information about the board test to be able to name and register any tests for this board...
std::vector< ALLEGRO_BLOCK_TEST > m_BlockTests
const nlohmann::json & m_BrdTestJson
ALLEGRO::FMT_VER m_FormatVersion
A single block of test data, along with the expected result of parsing it.
std::function< void(const ALLEGRO::BLOCK_BASE &)> m_ValidateFunc
An optional function to validate the contents of the parsed block if parsing is expected to succeed.
std::string m_DataSource
The raw bytes of the block, as copied from the file.
size_t m_BlockOffset
The offset within the board file where this block is located (used for error messages)
uint8_t m_BlockType
The type of the block, as in the first byte.
bool m_Skip
Whether to skip this test while parsers don't support a certain format.
static struct RegisterBlockSuites s_registerHeaderBlockSuites
static BLK_TEST_INFO createBlockTestEntry(const std::string &boardDir, const nlohmann::json &blockTestEntry)
static std::unique_ptr< HEADER_TEST_INFO > createHeaderTestEntry(const std::string &boardDir, const nlohmann::json &headerTestEntry)
static BOARD_TEST_INFO createBoardTestInfo(const std::string &aBrdName, const nlohmann::json &boardTestEntry)
static std::vector< uint8_t > loadDataByUri(const std::string &aDataSource)
static std::vector< ALLEGRO_BOARD_TEST_REF > getBoardTestDefinitions(const nlohmann::json &aJson)
Construct a list of test definitions for the boards we have test data for, by reading the registry JS...
static std::vector< boost::unit_test::test_suite * > buildAllegroBoardSuites()
This function initializes the test suites for Allegro block and board parsing.
void RunBoardLoad(const std::string &aBrdName, const nlohmann::json &aBoardTestJson)
static std::vector< std::string > getBoardTestLabels(const nlohmann::json &boardTestJson)
Get the labels associated with a board test, which can be used to e.g.
void RunBoardExpectations(const std::string &aBrdName, const nlohmann::json &aBoardTestJson)
static const ALLEGRO_BLOCK_TEST_REGISTRY & buildTestRegistry()
ALLEGRO::FMT_VER getFormatVersionFromStr(const std::string &aFmtVerStr)
BOOST_TEST(contains==c.ExpectedContains)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
std::string path
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")