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
65{
66 uint8_t m_BlockType;
69 const nlohmann::json& m_BlockTestJson;
70};
71
77{
78 // The board name in the JSON registry
79 std::string m_BrdName;
80
82 std::vector<ALLEGRO_BLOCK_TEST_DESCRIPTOR> m_BlockTests;
84
86 std::vector<BOARD_EXPECTATION_TEST::DESCRIPTOR> m_ExpectationTests;
87};
88
89
98{
99 nlohmann::json m_Json;
100 std::vector<ALLEGRO_BOARD_TEST_DESCRIPTOR> m_BoardTests;
101
102 const nlohmann::json& GetBoardJson( const std::string& aBoardName ) const
103 {
104 return m_Json["boards"][aBoardName];
105 }
106};
107
108
109static std::vector<uint8_t> loadDataByUri( const std::string& aDataSource )
110{
111 // For now, we only support loading from files, but in the future we could also support
112 // loading from other sources (e.g. from compiled-in data, base64, etc.)
113
114 // Split off the protocol (e.g. "file://") if present, to get the actual path
115 std::string path, protocol;
116 const std::string sep = "://";
117
118 size_t sepPos = aDataSource.find( sep );
119 if( sepPos != std::string::npos )
120 {
121 protocol = aDataSource.substr( 0, sepPos );
122 path = aDataSource.substr( sepPos + sep.size() );
123 }
124 else
125 {
126 // No protocol
127 throw std::runtime_error( "Unsupported data source URI (missing protocol): " + aDataSource );
128 }
129
130 if( protocol == "file" )
131 {
132 // This means get it from a file in the QD data
133 return KI_TEST::LoadBinaryData( path, std::nullopt );
134 }
135
136 throw std::runtime_error( "Unsupported data source protocol: " + protocol );
137}
138
139
140ALLEGRO::FMT_VER getFormatVersionFromStr( const std::string& aFmtVerStr )
141{
142 // clang-format off
143 static const std::map<std::string, ALLEGRO::FMT_VER> fmtVerStrMap{
144 { "16.0", FMT_VER::V_160 },
145 { "16.2", FMT_VER::V_162 },
146 { "16.4", FMT_VER::V_164 },
147 { "16.5", FMT_VER::V_165 },
148 { "16.6", FMT_VER::V_166 },
149 { "17.2", FMT_VER::V_172 },
150 { "17.4", FMT_VER::V_174 },
151 { "17.5", FMT_VER::V_175 },
152 { "18.0", FMT_VER::V_180 },
153 };
154 // clang-format on
155
156 auto it = fmtVerStrMap.find( aFmtVerStr );
157 if( it != fmtVerStrMap.end() )
158 {
159 return it->second;
160 }
161
162 return FMT_VER::V_UNKNOWN;
163}
164
165
166static std::unique_ptr<HEADER_TEST_INFO> createHeaderTestEntry( const std::string& boardDir,
167 const nlohmann::json& headerTestEntry )
168{
169 std::unique_ptr<HEADER_TEST_INFO> headerTest;
170
171 if( !headerTestEntry.is_object() )
172 {
173 throw std::runtime_error( "Header test entry is not a valid JSON object" );
174 }
175
176 // Default header location
177 const std::string headerDataUri = std::string( "file://" ) + boardDir + "header.bin";
178
179 return std::make_unique<HEADER_TEST_INFO>( headerDataUri, headerTestEntry.value( "skip", false ) );
180}
181
182
183static BLOCK_TEST_INFO createBlockTestEntry( const std::string& boardDir,
184 const ALLEGRO_BLOCK_TEST_DESCRIPTOR& aBlockTestDescriptor )
185{
186 const nlohmann::json& blockTestJson = aBlockTestDescriptor.m_BlockTestJson;
187 const bool skipBlock = blockTestJson.value( "skip", false );
188
189 const uint8_t& blockType = aBlockTestDescriptor.m_BlockType;
190 const size_t& blockOffset = aBlockTestDescriptor.m_BlockOffset;
191
192 // Default block data location is a file with a name based on the block type and offset
193 wxString blockDataLoc = wxString::Format( "0x%02x_0x%08zx.bin", blockType, blockOffset );
194
195 const std::string blockDataUri = std::string( "file://" ) + boardDir + blockDataLoc.ToStdString();
196
197 const bool extraBlockTest = blockTestJson.value( "extraBlockTest", false );
198
199 return BLOCK_TEST_INFO{
200 blockType,
201 blockOffset,
202 skipBlock,
203 extraBlockTest,
204 blockDataUri,
205 };
206}
207
208
214
215
216static BOARD_TEST_INFO createBoardTestInfo( const std::string& aBrdName, const nlohmann::json& boardTestEntry )
217{
218 const ALLEGRO::FMT_VER formatVersion =
219 getFormatVersionFromStr( boardTestEntry.value( "formatVersion", "unknown" ) );
220
221 return BOARD_TEST_INFO{ aBrdName, formatVersion };
222}
223
224
226{
227public:
228 void RunHeaderTest( const std::string& aBrdName, const nlohmann::json& aBoardTestJson )
229 {
230 BOOST_TEST_CONTEXT( wxString::Format( "Testing header from board %s", aBrdName ) )
231 {
232 BOARD_TEST_INFO brdDef = createBoardTestInfo( aBrdName, aBoardTestJson );
233
234 const std::string boardDir = AllegroBoardDataDir( aBrdName );
235
236 std::unique_ptr<HEADER_TEST_INFO> headerTest = nullptr;
237
238 if( aBoardTestJson.contains( "header" ) && aBoardTestJson["header"].is_object() )
239 {
240 headerTest = createHeaderTestEntry( boardDir, aBoardTestJson["header"] );
241 }
242
243 BOOST_REQUIRE( headerTest != nullptr );
244
245 if( headerTest->m_Skip )
246 {
247 BOOST_TEST_MESSAGE( "Skipping test for this header" );
248 return;
249 }
250
251 // Read in the header data
252 std::vector<uint8_t> headerData = loadDataByUri( headerTest->m_HeaderDataSource );
253 FILE_STREAM fileStream( headerData.data(), headerData.size() );
254 HEADER_PARSER parser( fileStream );
255
256 std::unique_ptr<FILE_HEADER> header = parser.ParseHeader();
257
258 BOOST_REQUIRE( header != nullptr );
259
260 // We don't expect any known header to be in an unknown format
262
263 // Make sure the format version matches what the board is expected to be
264 if( brdDef.m_FormatVersion != FMT_VER::V_UNKNOWN )
265 {
266 BOOST_TEST( parser.GetFormatVersion() == brdDef.m_FormatVersion );
267 }
268
269 // Other header tests - validate some commonly used fields?
270
271 // The parser should have consumed all the data in the header
272 BOOST_TEST( fileStream.Position() == headerData.size() );
273 }
274 }
275
276
277 void RunBlockTest( const std::string& aBrdName, const nlohmann::json& aBoardTestJson,
278 const ALLEGRO_BLOCK_TEST_DESCRIPTOR& aBlockTest )
279 {
280 const BOARD_TEST_INFO brdDef = createBoardTestInfo( aBrdName, aBoardTestJson );
281 const BLOCK_TEST_INFO blockTest = createBlockTestEntry( AllegroBoardDataDir( aBrdName ), aBlockTest );
282
283 BOOST_TEST_CONTEXT( wxString::Format( "Testing block type %#02x at offset %#010zx from board %s",
284 blockTest.m_BlockType, blockTest.m_BlockOffset, aBrdName ) )
285 {
286 if( blockTest.m_Skip )
287 {
288 BOOST_TEST_MESSAGE( "Skipping test for this block" );
289 return;
290 }
291
292 // Read in the block data, and create a FILE_STREAM for it
293 std::vector<uint8_t> data = loadDataByUri( blockTest.m_DataSource );
294 FILE_STREAM fileStream( data.data(), data.size() );
295
296 BLOCK_PARSER parser( fileStream, brdDef.m_FormatVersion );
297
298 bool endOfObjectsMarker = false;
299 std::unique_ptr<BLOCK_BASE> block = parser.ParseBlock( endOfObjectsMarker );
300
301 // Compare the result to the expected results
302 BOOST_REQUIRE( block != nullptr );
303
304 // The parser should have consumed all the data in the block
305 BOOST_TEST( fileStream.Position() == data.size() );
306
307 // Look up and run additional ad-hoc block-level tests
308 if( blockTest.m_ExtraBlockTest )
309 {
310 KI_TEST::RunAdditionalBlockTest( aBrdName, blockTest.m_BlockOffset, *block );
311 }
312 }
313 }
314};
315
316
317static void AssertNoErrors( const CAPTURING_REPORTER& aReporter )
318{
319 if( aReporter.GetErrorCount() > 0 )
320 {
321 std::ostringstream ss;
322 ss << "Expected no errors, but found " << aReporter.GetErrorCount() << " errors:";
323
324 for( const auto& msg : aReporter.GetMessages() )
325 {
326 if( msg.severity == RPT_SEVERITY_ERROR )
327 {
328 ss << "\n " << msg.text;
329 }
330 }
331 BOOST_TEST_MESSAGE( ss.str() );
332 }
333 BOOST_TEST( aReporter.GetErrorCount() == 0 );
334}
335
336
337void RunBoardLoad( const std::string& aBrdName, const nlohmann::json& aBoardTestJson )
338{
339 BOARD* board = nullptr;
340
341 BOOST_TEST_CONTEXT( "Testing load from board: " << aBrdName )
342 {
343 if( aBoardTestJson.contains( "boardFile" ) )
344 {
345 const std::string boardFilePath =
346 KI_TEST::AllegroBoardDataDir( aBrdName ) + aBoardTestJson["boardFile"].get<std::string>();
347
348 if( !std::filesystem::exists( boardFilePath ) )
349 {
350 BOOST_FAIL( "Board file does not exist: " + boardFilePath );
351 return;
352 }
353
354 CAPTURING_REPORTER reporter;
355 board = KI_TEST::ALLEGRO_CACHED_LOADER::GetInstance().LoadAndCache( boardFilePath, &reporter );
356
357 const bool expectLoadFailure = aBoardTestJson.value( "expectLoadFailure", false );
358
359 if( expectLoadFailure )
360 {
361 BOOST_CHECK( board == nullptr );
362 BOOST_TEST_MESSAGE( "Board load was expected to fail, and it did." );
363 return;
364 }
365 else
366 {
367 BOOST_CHECK_MESSAGE( board != nullptr, "Board load was expected to succeed, but it failed." );
368
369 // Can allow a certain number of warnings, but no errors
370 AssertNoErrors( reporter );
371
372 // Can check max warnings here if we want
373 if( reporter.GetWarningCount() > 0 )
374 {
375 std::ostringstream ss;
376 ss << aBrdName << ": " << reporter.GetWarningCount() << " warnings";
377
378 for( const auto& msg : reporter.GetMessages() )
379 {
380 if( msg.severity == RPT_SEVERITY_WARNING )
381 {
382 ss << "\n " << msg.text;
383 }
384 }
385
386 BOOST_TEST_MESSAGE( ss.str() );
387 }
388
389 KI_TEST::PrintBoardStats( board, aBrdName );
390 }
391 }
392 }
393}
394
395
396static uint8_t getByteFromHexStr( const nlohmann::json& aJsonEntry, const std::string& aFieldName )
397{
398 if( !aJsonEntry.contains( aFieldName ) )
399 {
400 throw std::runtime_error( "Block test entry is missing the '" + aFieldName + "' field" );
401 }
402
403 if( aJsonEntry[aFieldName].is_number_unsigned() )
404 {
405 const unsigned long value = aJsonEntry[aFieldName].get<unsigned long>();
406 if( value > 0xFF )
407 {
408 throw std::runtime_error( "Value is too large to fit in a byte: " + std::to_string( value ) );
409 }
410 return static_cast<uint8_t>( value );
411 }
412 else if( aJsonEntry[aFieldName].is_string() )
413 {
414 const std::string aHexStr = aJsonEntry[aFieldName];
415
416 const unsigned long value = std::stoul( aHexStr, nullptr, 0 );
417 if( value > 0xFF )
418 {
419 throw std::runtime_error( "Value is too large to fit in a byte: " + aHexStr );
420 }
421 return static_cast<uint8_t>( value );
422 }
423
424 throw std::runtime_error( "Block test entry has an invalid '" + aFieldName
425 + "' field (must be a byte value as a number or hex string)" );
426}
427
428
429static ALLEGRO_BLOCK_TEST_DESCRIPTOR createBlockTestDescriptor( const nlohmann::json& aBlockTestJson )
430{
431 const uint8_t blockType = getByteFromHexStr( aBlockTestJson, "type" );
432
433 if( !aBlockTestJson.contains( "offset" ) || !aBlockTestJson["offset"].is_string() )
434 {
435 throw std::runtime_error( "Block test entry is missing a valid 'offset' field" );
436 }
437
438 const std::string block_offset = aBlockTestJson["offset"];
439 const size_t blockOffset = std::stoul( block_offset, nullptr, 0 );
440
442 blockType,
443 blockOffset,
444 aBlockTestJson,
445 };
446}
447
448
456static std::vector<ALLEGRO_BOARD_TEST_DESCRIPTOR> getBoardTestDefinitions( const nlohmann::json& aJson )
457{
458 if( !aJson.contains( "boards" ) || !aJson["boards"].is_object() )
459 {
460 throw std::runtime_error( "Test register JSON file does not contain a valid 'boards' object." );
461 }
462
463 std::vector<ALLEGRO_BOARD_TEST_DESCRIPTOR> boardTests;
464
465 for( auto& [boardName, boardEntry] : aJson["boards"].items() )
466 {
468 .m_BrdName = boardName,
469 .m_HasHeaderTest = boardEntry.contains( "header" ),
470 .m_BlockTests = {},
471 .m_HasBoardFile = boardEntry.contains( "boardFile" ),
472 .m_ExpectationTests = {},
473 };
474
475 if( boardEntry.contains( "blocks" ) && boardEntry["blocks"].is_array() )
476 {
477 for( const auto& blockTestEntry : boardEntry["blocks"] )
478 {
479 boardTestRef.m_BlockTests.emplace_back( createBlockTestDescriptor( blockTestEntry ) );
480 }
481 }
482
483 if( boardEntry.contains( "boardExpectations" ) )
484 {
485 const auto& expectationsEntry = boardEntry["boardExpectations"];
486
487 boardTestRef.m_ExpectationTests =
489 }
490
491 boardTests.push_back( std::move( boardTestRef ) );
492 }
493
494 return boardTests;
495}
496
497
499{
500 static ALLEGRO_BLOCK_TEST_REGISTRY registry = []()
501 {
503
504 // There is currently one registry file, but we could have more, e.g. if we
505 // stored the test definitions on a per-board basis in the board data dirs
506 const std::filesystem::path testRegisterJsonPath( AllegroBoardDataDir( "" ) + "board_data_registry.json" );
507 std::ifstream jsonFileStream( testRegisterJsonPath );
508
509 reg.m_Json = nlohmann::json::parse( jsonFileStream, nullptr, true, true );
511
512 return reg;
513 }();
514
515 return registry;
516}
517
518
522static std::vector<std::string> getBoardTestLabels( const nlohmann::json& boardTestJson )
523{
524 std::vector<std::string> labels;
525
526 if( boardTestJson.contains( "testLabels" ) && boardTestJson["testLabels"].is_array() )
527 {
528 for( const auto& label : boardTestJson["testLabels"] )
529 {
530 if( label.is_string() )
531 {
532 labels.push_back( label.get<std::string>() );
533 }
534 }
535 }
536
537 return labels;
538}
539
540
550static std::vector<boost::unit_test::test_suite*> buildAllegroBoardSuites()
551{
552 using BTS = boost::unit_test::test_suite;
553 std::vector<BTS*> suites;
554 BTS* blockSuite = suites.emplace_back( BOOST_TEST_SUITE( "AllegroBlocks" ) );
555 const ALLEGRO_BLOCK_TEST_REGISTRY* registry = nullptr;
556
557 try
558 {
559 registry = &buildTestRegistry();
560 }
561 catch( const std::exception& ex )
562 {
563 std::string msg = "Failed to load Allegro block test definitions: " + std::string( ex.what() );
564
565 // Register one failing test to report the error, so that we don't just silently skip all the tests
566 const auto failingTestFunction = [=]()
567 {
568 BOOST_TEST( false, msg );
569 };
570
571 blockSuite->add(
572 boost::unit_test::make_test_case(
573 failingTestFunction,
574 "FailureToLoadTestDefinitions",
575 __FILE__,
576 __LINE__
577 )
578 );
579
580 return suites;
581 }
582
583 for( const ALLEGRO_BOARD_TEST_DESCRIPTOR& boardTest : registry->m_BoardTests )
584 {
585 BTS* boardSuite = BOOST_TEST_SUITE( boardTest.m_BrdName );
586 blockSuite->add( boardSuite );
587
588 const nlohmann::json& boardTestJson = registry->GetBoardJson( boardTest.m_BrdName );
589
590 if( boardTest.m_HasHeaderTest )
591 {
592 // Note: captures ref-to-static
593 const auto testRunFunction = [&]()
594 {
596 fixture.RunHeaderTest( boardTest.m_BrdName, boardTestJson );
597 };
598
599 boardSuite->add(
600 boost::unit_test::make_test_case(
601 testRunFunction,
602 "Header",
603 __FILE__,
604 __LINE__
605 )
606 );
607 }
608
609 for( const auto& blockTest : boardTest.m_BlockTests )
610 {
611 // All these captures come from data stored in a static variable, so we can take them
612 // by reference here.
613 const auto testRunFunction = [&boardTest, &boardTestJson, &blockTest]()
614 {
616 fixture.RunBlockTest( boardTest.m_BrdName, boardTestJson, blockTest );
617 };
618
619 wxString testName =
620 wxString::Format( "Block_0x%02x_offset_0x%010zx", blockTest.m_BlockType, blockTest.m_BlockOffset );
621
622 boardSuite->add(
623 boost::unit_test::make_test_case(
624 testRunFunction,
625 testName.ToStdString(),
626 __FILE__,
627 __LINE__
628 )
629 );
630 }
631 }
632
633 // Separate test suite for board expectations, which will run after the header and block parsing tests,
634 // and will have access to the fully parsed board (and obviously be much slower)
635 BTS* boardSuites = suites.emplace_back( BOOST_TEST_SUITE( "AllegroBoards" ) );
636
637 for( const ALLEGRO_BOARD_TEST_DESCRIPTOR& boardTest : registry->m_BoardTests )
638 {
639 if( !boardTest.m_HasBoardFile )
640 {
641 continue;
642 }
643
644 // Board-level suite
645 BTS* boardSuite = BOOST_TEST_SUITE( boardTest.m_BrdName );
646 boardSuites->add( boardSuite );
647
648 const nlohmann::json& boardTestData = registry->GetBoardJson( boardTest.m_BrdName );
649
650 const std::vector<std::string> testLabels = getBoardTestLabels( boardTestData );
651 boost::unit_test::test_case* loadingTestCase = nullptr;
652
653 if( boardTest.m_HasBoardFile )
654 {
655 const auto testLoadFunction = [&boardTest, &boardTestData]()
656 {
657 RunBoardLoad( boardTest.m_BrdName, boardTestData );
658 };
659
660 // The first test unit loads (and caches) the board.
661 loadingTestCase = boost::unit_test::make_test_case(
662 testLoadFunction,
663 "Import",
664 __FILE__,
665 __LINE__
666 );
667
668 for( const std::string& label : testLabels )
669 {
670 loadingTestCase->add_label( label );
671 }
672
673 boardSuite->add( loadingTestCase );
674 }
675
676 if( boardTest.m_ExpectationTests.size() > 0 )
677 {
678 BTS* expectationsSuite = BOOST_TEST_SUITE( "Expectations" );
679 boardSuite->add( expectationsSuite );
680
681 for( const BOARD_EXPECTATION_TEST::DESCRIPTOR& expectationTestRef : boardTest.m_ExpectationTests )
682 {
683 const auto testRunFunction = [&boardTest, &boardTestData, &expectationTestRef]()
684 {
685 if( !boardTestData.contains( "boardFile" ) )
686 {
687 BOOST_FAIL(
688 "Board test JSON does not contain a 'boardFile' entry - cannot run expectations test" );
689 return;
690 }
691
692 const std::string boardFilePath = KI_TEST::AllegroBoardDataDir( boardTest.m_BrdName )
693 + boardTestData["boardFile"].get<std::string>();
694
695 const BOARD* board = KI_TEST::ALLEGRO_CACHED_LOADER::GetInstance().GetCachedBoard( boardFilePath );
696
697 if( !board )
698 {
699 BOOST_FAIL( "Failed to load cached board for expectations test: " + boardFilePath );
700 return;
701 }
702
703 KI_TEST::BOARD_EXPECTATION_TEST::RunFromRef( boardTest.m_BrdName, *board, expectationTestRef );
704 };
705
706 boost::unit_test::test_case* expectationTestCase = boost::unit_test::make_test_case(
707 testRunFunction,
708 expectationTestRef.m_TestName,
709 __FILE__,
710 __LINE__
711 );
712
713 // Label the expectations test case with any tags from the JSON
714 for( const std::string& label : expectationTestRef.m_Tags )
715 {
716 expectationTestCase->add_label( label );
717 }
718
719 expectationsSuite->add( expectationTestCase );
720 }
721
722 // We can only run the expectations test after the board has been loaded
723 // and that test passes.
724 BOOST_REQUIRE( loadingTestCase != nullptr );
725 expectationsSuite->depends_on( loadingTestCase );
726 }
727 }
728
729 return suites;
730}
731
732
738{
740 {
741 std::vector<boost::unit_test::test_suite*> suites = buildAllegroBoardSuites();
742
743 for( boost::unit_test::test_suite* suite : suites )
744 {
745 boost::unit_test::framework::master_test_suite().add( suite );
746 }
747 }
General utilities for PCB file IO for QA programs.
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.
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 RunBlockTest(const std::string &aBrdName, const nlohmann::json &aBoardTestJson, const ALLEGRO_BLOCK_TEST_DESCRIPTOR &aBlockTest)
void RunHeaderTest(const std::string &aBrdName, const nlohmann::json &aBoardTestJson)
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
static ALLEGRO_CACHED_LOADER & GetInstance()
Get the singleton instance of the Allegro board cache loader.
static std::vector< DESCRIPTOR > ExtractExpectationTestsFromJson(const nlohmann::json &aExpectationArray)
Extracts expectation tests from the given JSON array and returns a list of test references that can b...
static void RunFromRef(const std::string &aBrdName, const BOARD &aBoard, const BOARD_EXPECTATION_TEST::DESCRIPTOR &aExpectationTestRef)
Constructs a BOARD_EXPECTATION_TEST from the given JSON definition, and runs it on the given board.
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 Allegro file.
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
@ RPT_SEVERITY_ERROR
Just enough information about a block-level test to be able to name and register it with the test run...
const nlohmann::json & m_BlockTestJson
Handy ref to the JSON entry for this block test.
The registry of known Allegro board and block tests, populated at static init time by reading the JSO...
const nlohmann::json & GetBoardJson(const std::string &aBoardName) const
std::vector< ALLEGRO_BOARD_TEST_DESCRIPTOR > m_BoardTests
Just enough information about the board test to be able to name and register any tests for this board...
std::vector< BOARD_EXPECTATION_TEST::DESCRIPTOR > m_ExpectationTests
List of expectation tests found in the JSON for this board.
std::vector< ALLEGRO_BLOCK_TEST_DESCRIPTOR > m_BlockTests
ALLEGRO::FMT_VER m_FormatVersion
A complete description of a block test.
uint8_t m_BlockType
The type of the block, as in the first byte.
std::string m_DataSource
The the source of the block data (probably a filename)
bool m_ExtraBlockTest
Do we have an additional block-level test to run for this block?
size_t m_BlockOffset
The offset within the board file where this block is located (used for error messages)
bool m_Skip
Whether to skip this test while parsers don't support a certain format.
Lightweight descriptor for a BOARD_EXPECTATION_TEST, which can be used to refer to the test unambiguo...
std::string m_TestName
If the test has a name, it's that, else an index - this is for naming the test for filtering.
std::vector< std::string > m_Tags
Tags associated with the test, which can be used for filtering.
static struct RegisterBlockSuites s_registerHeaderBlockSuites
static void AssertNoErrors(const CAPTURING_REPORTER &aReporter)
static std::unique_ptr< HEADER_TEST_INFO > createHeaderTestEntry(const std::string &boardDir, const nlohmann::json &headerTestEntry)
static uint8_t getByteFromHexStr(const nlohmann::json &aJsonEntry, const std::string &aFieldName)
static BOARD_TEST_INFO createBoardTestInfo(const std::string &aBrdName, const nlohmann::json &boardTestEntry)
static BLOCK_TEST_INFO createBlockTestEntry(const std::string &boardDir, const ALLEGRO_BLOCK_TEST_DESCRIPTOR &aBlockTestDescriptor)
static std::vector< uint8_t > loadDataByUri(const std::string &aDataSource)
static ALLEGRO_BLOCK_TEST_DESCRIPTOR createBlockTestDescriptor(const nlohmann::json &aBlockTestJson)
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.
static std::vector< ALLEGRO_BOARD_TEST_DESCRIPTOR > getBoardTestDefinitions(const nlohmann::json &aJson)
Construct a list of test descriptrs (lightweight objects) for the boards we have test data for,...
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
std::vector< std::string > header
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
BOOST_TEST_CONTEXT("Test Clearance")