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 const bool extraDbObjTest = blockTestJson.value( "extraDbObjTest", false );
199
200 return BLOCK_TEST_INFO{
201 blockType,
202 blockOffset,
203 skipBlock,
204 extraBlockTest,
205 extraDbObjTest,
206 blockDataUri,
207 };
208}
209
210
216
217
218static BOARD_TEST_INFO createBoardTestInfo( const std::string& aBrdName, const nlohmann::json& boardTestEntry )
219{
220 const ALLEGRO::FMT_VER formatVersion =
221 getFormatVersionFromStr( boardTestEntry.value( "formatVersion", "unknown" ) );
222
223 return BOARD_TEST_INFO{ aBrdName, formatVersion };
224}
225
226
228{
229public:
230 void RunHeaderTest( const std::string& aBrdName, const nlohmann::json& aBoardTestJson )
231 {
232 BOOST_TEST_CONTEXT( wxString::Format( "Testing header from board %s", aBrdName ) )
233 {
234 BOARD_TEST_INFO brdDef = createBoardTestInfo( aBrdName, aBoardTestJson );
235
236 const std::string boardDir = AllegroBoardDataDir( aBrdName );
237
238 std::unique_ptr<HEADER_TEST_INFO> headerTest = nullptr;
239
240 if( aBoardTestJson.contains( "header" ) && aBoardTestJson["header"].is_object() )
241 {
242 headerTest = createHeaderTestEntry( boardDir, aBoardTestJson["header"] );
243 }
244
245 BOOST_REQUIRE( headerTest != nullptr );
246
247 if( headerTest->m_Skip )
248 {
249 BOOST_TEST_MESSAGE( "Skipping test for this header" );
250 return;
251 }
252
253 // Read in the header data
254 std::vector<uint8_t> headerData = loadDataByUri( headerTest->m_HeaderDataSource );
255 FILE_STREAM fileStream( headerData.data(), headerData.size() );
256 HEADER_PARSER parser( fileStream );
257
258 std::unique_ptr<FILE_HEADER> header = parser.ParseHeader();
259
260 BOOST_REQUIRE( header != nullptr );
261
262 // We don't expect any known header to be in an unknown format
264
265 // Make sure the format version matches what the board is expected to be
266 if( brdDef.m_FormatVersion != FMT_VER::V_UNKNOWN )
267 {
268 BOOST_TEST( parser.GetFormatVersion() == brdDef.m_FormatVersion );
269 }
270
271 // Other header tests - validate some commonly used fields?
272
273 // The parser should have consumed all the data in the header
274 BOOST_TEST( fileStream.Position() == headerData.size() );
275 }
276 }
277
278
279 void RunBlockTest( const std::string& aBrdName, const nlohmann::json& aBoardTestJson,
280 const ALLEGRO_BLOCK_TEST_DESCRIPTOR& aBlockTest )
281 {
282 const BOARD_TEST_INFO brdDef = createBoardTestInfo( aBrdName, aBoardTestJson );
283 const BLOCK_TEST_INFO blockTest = createBlockTestEntry( AllegroBoardDataDir( aBrdName ), aBlockTest );
284
285 BOOST_TEST_CONTEXT( wxString::Format( "Testing block type %#02x at offset %#010zx from board %s",
286 blockTest.m_BlockType, blockTest.m_BlockOffset, aBrdName ) )
287 {
288 if( blockTest.m_Skip )
289 {
290 BOOST_TEST_MESSAGE( "Skipping test for this block" );
291 return;
292 }
293
294 // Read in the block data, and create a FILE_STREAM for it
295 std::vector<uint8_t> data = loadDataByUri( blockTest.m_DataSource );
296 FILE_STREAM fileStream( data.data(), data.size() );
297
298 BLOCK_PARSER parser( fileStream, brdDef.m_FormatVersion );
299
300 bool endOfObjectsMarker = false;
301 std::unique_ptr<BLOCK_BASE> block = parser.ParseBlock( endOfObjectsMarker );
302
303 // Compare the result to the expected results
304 BOOST_REQUIRE( block != nullptr );
305
306 // The parser should have consumed all the data in the block
307 BOOST_TEST( fileStream.Position() == data.size() );
308
309 // Look up and run additional ad-hoc block-level tests
310 if( blockTest.m_ExtraBlockTest )
311 {
312 KI_TEST::RunAdditionalBlockTest( aBrdName, blockTest.m_BlockOffset, *block );
313 }
314
315 // Now try to convert the block into a DB_OBJ
316 ALLEGRO::BRD_DB brd;
317 ALLEGRO::BRD_DB::OBJ_FACTORY objFactory( brd );
318 // Not all blocks convert to DB_OBJ yet, but at least it mustn't crash.
319 // Eventually, this should always succeed.
320 std::unique_ptr<ALLEGRO::DB_OBJ> dbObj = objFactory.CreateObject( *block );
321
322 if( blockTest.m_ExtraDbObjTest )
323 {
324 BOOST_REQUIRE( dbObj != nullptr );
325 KI_TEST::RunAdditionalObjectTest( aBrdName, blockTest.m_BlockOffset, *dbObj );
326 }
327 }
328 }
329};
330
331
332static void AssertNoErrors( const CAPTURING_REPORTER& aReporter )
333{
334 if( aReporter.GetErrorCount() > 0 )
335 {
336 std::ostringstream ss;
337 ss << "Expected no errors, but found " << aReporter.GetErrorCount() << " errors:";
338
339 for( const auto& msg : aReporter.GetMessages() )
340 {
341 if( msg.severity == RPT_SEVERITY_ERROR )
342 {
343 ss << "\n " << msg.text;
344 }
345 }
346 BOOST_TEST_MESSAGE( ss.str() );
347 }
348 BOOST_TEST( aReporter.GetErrorCount() == 0 );
349}
350
351
352void RunBoardLoad( const std::string& aBrdName, const nlohmann::json& aBoardTestJson )
353{
354 BOARD* board = nullptr;
355
356 BOOST_TEST_CONTEXT( "Testing load from board: " << aBrdName )
357 {
358 if( aBoardTestJson.contains( "boardFile" ) )
359 {
360 const std::string boardFilePath =
361 KI_TEST::AllegroBoardDataDir( aBrdName ) + aBoardTestJson["boardFile"].get<std::string>();
362
363 if( !std::filesystem::exists( boardFilePath ) )
364 {
365 BOOST_FAIL( "Board file does not exist: " + boardFilePath );
366 return;
367 }
368
369 CAPTURING_REPORTER reporter;
370 board = KI_TEST::ALLEGRO_CACHED_LOADER::GetInstance().LoadAndCache( boardFilePath, &reporter );
371
372 const bool expectLoadFailure = aBoardTestJson.value( "expectLoadFailure", false );
373
374 if( expectLoadFailure )
375 {
376 BOOST_CHECK( board == nullptr );
377 BOOST_TEST_MESSAGE( "Board load was expected to fail, and it did." );
378 return;
379 }
380 else
381 {
382 BOOST_CHECK_MESSAGE( board != nullptr, "Board load was expected to succeed, but it failed." );
383
384 // Can allow a certain number of warnings, but no errors
385 AssertNoErrors( reporter );
386
387 // Can check max warnings here if we want
388 if( reporter.GetWarningCount() > 0 )
389 {
390 std::ostringstream ss;
391 ss << aBrdName << ": " << reporter.GetWarningCount() << " warnings";
392
393 for( const auto& msg : reporter.GetMessages() )
394 {
395 if( msg.severity == RPT_SEVERITY_WARNING )
396 {
397 ss << "\n " << msg.text;
398 }
399 }
400
401 BOOST_TEST_MESSAGE( ss.str() );
402 }
403
404 KI_TEST::PrintBoardStats( board, aBrdName );
405 }
406 }
407 }
408}
409
410
411static uint8_t getByteFromHexStr( const nlohmann::json& aJsonEntry, const std::string& aFieldName )
412{
413 if( !aJsonEntry.contains( aFieldName ) )
414 {
415 throw std::runtime_error( "Block test entry is missing the '" + aFieldName + "' field" );
416 }
417
418 if( aJsonEntry[aFieldName].is_number_unsigned() )
419 {
420 const unsigned long value = aJsonEntry[aFieldName].get<unsigned long>();
421 if( value > 0xFF )
422 {
423 throw std::runtime_error( "Value is too large to fit in a byte: " + std::to_string( value ) );
424 }
425 return static_cast<uint8_t>( value );
426 }
427 else if( aJsonEntry[aFieldName].is_string() )
428 {
429 const std::string aHexStr = aJsonEntry[aFieldName];
430
431 const unsigned long value = std::stoul( aHexStr, nullptr, 0 );
432 if( value > 0xFF )
433 {
434 throw std::runtime_error( "Value is too large to fit in a byte: " + aHexStr );
435 }
436 return static_cast<uint8_t>( value );
437 }
438
439 throw std::runtime_error( "Block test entry has an invalid '" + aFieldName
440 + "' field (must be a byte value as a number or hex string)" );
441}
442
443
444static ALLEGRO_BLOCK_TEST_DESCRIPTOR createBlockTestDescriptor( const nlohmann::json& aBlockTestJson )
445{
446 const uint8_t blockType = getByteFromHexStr( aBlockTestJson, "type" );
447
448 if( !aBlockTestJson.contains( "offset" ) || !aBlockTestJson["offset"].is_string() )
449 {
450 throw std::runtime_error( "Block test entry is missing a valid 'offset' field" );
451 }
452
453 const std::string block_offset = aBlockTestJson["offset"];
454 const size_t blockOffset = std::stoul( block_offset, nullptr, 0 );
455
457 blockType,
458 blockOffset,
459 aBlockTestJson,
460 };
461}
462
463
471static std::vector<ALLEGRO_BOARD_TEST_DESCRIPTOR> getBoardTestDefinitions( const nlohmann::json& aJson )
472{
473 if( !aJson.contains( "boards" ) || !aJson["boards"].is_object() )
474 {
475 throw std::runtime_error( "Test register JSON file does not contain a valid 'boards' object." );
476 }
477
478 std::vector<ALLEGRO_BOARD_TEST_DESCRIPTOR> boardTests;
479
480 for( auto& [boardName, boardEntry] : aJson["boards"].items() )
481 {
483 .m_BrdName = boardName,
484 .m_HasHeaderTest = boardEntry.contains( "header" ),
485 .m_BlockTests = {},
486 .m_HasBoardFile = boardEntry.contains( "boardFile" ),
487 .m_ExpectationTests = {},
488 };
489
490 if( boardEntry.contains( "blocks" ) && boardEntry["blocks"].is_array() )
491 {
492 for( const auto& blockTestEntry : boardEntry["blocks"] )
493 {
494 boardTestRef.m_BlockTests.emplace_back( createBlockTestDescriptor( blockTestEntry ) );
495 }
496 }
497
498 if( boardEntry.contains( "boardExpectations" ) )
499 {
500 const auto& expectationsEntry = boardEntry["boardExpectations"];
501
502 boardTestRef.m_ExpectationTests =
504 }
505
506 boardTests.push_back( std::move( boardTestRef ) );
507 }
508
509 return boardTests;
510}
511
512
514{
515 static ALLEGRO_BLOCK_TEST_REGISTRY registry = []()
516 {
518
519 // There is currently one registry file, but we could have more, e.g. if we
520 // stored the test definitions on a per-board basis in the board data dirs
521 const std::filesystem::path testRegisterJsonPath( AllegroBoardDataDir( "" ) + "board_data_registry.json" );
522 std::ifstream jsonFileStream( testRegisterJsonPath );
523
524 reg.m_Json = nlohmann::json::parse( jsonFileStream, nullptr, true, true );
526
527 return reg;
528 }();
529
530 return registry;
531}
532
533
537static std::vector<std::string> getBoardTestLabels( const nlohmann::json& boardTestJson )
538{
539 std::vector<std::string> labels;
540
541 if( boardTestJson.contains( "testLabels" ) && boardTestJson["testLabels"].is_array() )
542 {
543 for( const auto& label : boardTestJson["testLabels"] )
544 {
545 if( label.is_string() )
546 {
547 labels.push_back( label.get<std::string>() );
548 }
549 }
550 }
551
552 return labels;
553}
554
555
565static std::vector<boost::unit_test::test_suite*> buildAllegroBoardSuites()
566{
567 using BTS = boost::unit_test::test_suite;
568 std::vector<BTS*> suites;
569 BTS* blockSuite = suites.emplace_back( BOOST_TEST_SUITE( "AllegroBlocks" ) );
570 const ALLEGRO_BLOCK_TEST_REGISTRY* registry = nullptr;
571
572 try
573 {
574 registry = &buildTestRegistry();
575 }
576 catch( const std::exception& ex )
577 {
578 std::string msg = "Failed to load Allegro block test definitions: " + std::string( ex.what() );
579
580 // Register one failing test to report the error, so that we don't just silently skip all the tests
581 const auto failingTestFunction = [=]()
582 {
583 BOOST_TEST( false, msg );
584 };
585
586 blockSuite->add(
587 boost::unit_test::make_test_case(
588 failingTestFunction,
589 "FailureToLoadTestDefinitions",
590 __FILE__,
591 __LINE__
592 )
593 );
594
595 return suites;
596 }
597
598 for( const ALLEGRO_BOARD_TEST_DESCRIPTOR& boardTest : registry->m_BoardTests )
599 {
600 BTS* boardSuite = BOOST_TEST_SUITE( boardTest.m_BrdName );
601 blockSuite->add( boardSuite );
602
603 const nlohmann::json& boardTestJson = registry->GetBoardJson( boardTest.m_BrdName );
604
605 if( boardTest.m_HasHeaderTest )
606 {
607 // Note: captures ref-to-static
608 const auto testRunFunction = [&]()
609 {
611 fixture.RunHeaderTest( boardTest.m_BrdName, boardTestJson );
612 };
613
614 boardSuite->add(
615 boost::unit_test::make_test_case(
616 testRunFunction,
617 "Header",
618 __FILE__,
619 __LINE__
620 )
621 );
622 }
623
624 for( const auto& blockTest : boardTest.m_BlockTests )
625 {
626 // All these captures come from data stored in a static variable, so we can take them
627 // by reference here.
628 const auto testRunFunction = [&boardTest, &boardTestJson, &blockTest]()
629 {
631 fixture.RunBlockTest( boardTest.m_BrdName, boardTestJson, blockTest );
632 };
633
634 wxString testName =
635 wxString::Format( "Block_0x%02x_offset_0x%010zx", blockTest.m_BlockType, blockTest.m_BlockOffset );
636
637 boardSuite->add(
638 boost::unit_test::make_test_case(
639 testRunFunction,
640 testName.ToStdString(),
641 __FILE__,
642 __LINE__
643 )
644 );
645 }
646 }
647
648 // Separate test suite for board expectations, which will run after the header and block parsing tests,
649 // and will have access to the fully parsed board (and obviously be much slower)
650 BTS* boardSuites = suites.emplace_back( BOOST_TEST_SUITE( "AllegroBoards" ) );
651
652 for( const ALLEGRO_BOARD_TEST_DESCRIPTOR& boardTest : registry->m_BoardTests )
653 {
654 if( !boardTest.m_HasBoardFile )
655 {
656 continue;
657 }
658
659 // Board-level suite
660 BTS* boardSuite = BOOST_TEST_SUITE( boardTest.m_BrdName );
661 boardSuites->add( boardSuite );
662
663 const nlohmann::json& boardTestData = registry->GetBoardJson( boardTest.m_BrdName );
664
665 const std::vector<std::string> testLabels = getBoardTestLabels( boardTestData );
666 boost::unit_test::test_case* loadingTestCase = nullptr;
667
668 if( boardTest.m_HasBoardFile )
669 {
670 const auto testLoadFunction = [&boardTest, &boardTestData]()
671 {
672 RunBoardLoad( boardTest.m_BrdName, boardTestData );
673 };
674
675 // The first test unit loads (and caches) the board.
676 loadingTestCase = boost::unit_test::make_test_case(
677 testLoadFunction,
678 "Import",
679 __FILE__,
680 __LINE__
681 );
682
683 for( const std::string& label : testLabels )
684 {
685 loadingTestCase->add_label( label );
686 }
687
688 boardSuite->add( loadingTestCase );
689 }
690
691 if( boardTest.m_ExpectationTests.size() > 0 )
692 {
693 BTS* expectationsSuite = BOOST_TEST_SUITE( "Expectations" );
694 boardSuite->add( expectationsSuite );
695
696 for( const BOARD_EXPECTATION_TEST::DESCRIPTOR& expectationTestRef : boardTest.m_ExpectationTests )
697 {
698 const auto testRunFunction = [&boardTest, &boardTestData, &expectationTestRef]()
699 {
700 if( !boardTestData.contains( "boardFile" ) )
701 {
702 BOOST_FAIL(
703 "Board test JSON does not contain a 'boardFile' entry - cannot run expectations test" );
704 return;
705 }
706
707 const std::string boardFilePath = KI_TEST::AllegroBoardDataDir( boardTest.m_BrdName )
708 + boardTestData["boardFile"].get<std::string>();
709
710 const BOARD* board = KI_TEST::ALLEGRO_CACHED_LOADER::GetInstance().GetCachedBoard( boardFilePath );
711
712 if( !board )
713 {
714 BOOST_FAIL( "Failed to load cached board for expectations test: " + boardFilePath );
715 return;
716 }
717
718 KI_TEST::BOARD_EXPECTATION_TEST::RunFromRef( boardTest.m_BrdName, *board, expectationTestRef );
719 };
720
721 boost::unit_test::test_case* expectationTestCase = boost::unit_test::make_test_case(
722 testRunFunction,
723 expectationTestRef.m_TestName,
724 __FILE__,
725 __LINE__
726 );
727
728 // Label the expectations test case with any tags from the JSON
729 for( const std::string& label : expectationTestRef.m_Tags )
730 {
731 expectationTestCase->add_label( label );
732 }
733
734 expectationsSuite->add( expectationTestCase );
735 }
736
737 // We can only run the expectations test after the board has been loaded
738 // and that test passes.
739 BOOST_REQUIRE( loadingTestCase != nullptr );
740 expectationsSuite->depends_on( loadingTestCase );
741 }
742 }
743
744 return suites;
745}
746
747
753{
755 {
756 std::vector<boost::unit_test::test_suite*> suites = buildAllegroBoardSuites();
757
758 for( boost::unit_test::test_suite* suite : suites )
759 {
760 boost::unit_test::framework::master_test_suite().add( suite );
761 }
762 }
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.
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:961
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:949
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 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
@ 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.
bool m_ExtraDbObjTest
Do we have an additional DB_OBJ-level test to run for this block?
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")