From f7578eb038dc6ec3370699b2c4d413f0cf655de9 Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Mon, 18 May 2020 22:40:13 -0400 Subject: [PATCH] Implement new native netlist QA test --- eeschema/sch_screen.h | 5 + eeschema/sch_sheet_path.cpp | 4 +- eeschema/sch_sheet_path.h | 2 +- pcbnew/netlist_reader/pcb_netlist.cpp | 2 +- pcbnew/netlist_reader/pcb_netlist.h | 2 +- qa/eeschema/CMakeLists.txt | 19 +- .../test_global_promotion_2.net | 38 ++- qa/eeschema/mocks_eeschema.cpp | 2 - qa/eeschema/test_netlists.cpp | 241 ++++++++++++++++++ 9 files changed, 276 insertions(+), 39 deletions(-) create mode 100644 qa/eeschema/test_netlists.cpp diff --git a/eeschema/sch_screen.h b/eeschema/sch_screen.h index a55a0d3dc2..3979848836 100644 --- a/eeschema/sch_screen.h +++ b/eeschema/sch_screen.h @@ -536,6 +536,11 @@ public: return m_aliases; } + const std::vector& GetSymbolInstances() const + { + return m_symbolInstances; + } + #if defined(DEBUG) void Show( int nestLevel, std::ostream& os ) const override; #endif diff --git a/eeschema/sch_sheet_path.cpp b/eeschema/sch_sheet_path.cpp index fe0c7732b0..41aba7df40 100644 --- a/eeschema/sch_sheet_path.cpp +++ b/eeschema/sch_sheet_path.cpp @@ -696,7 +696,7 @@ SCH_SHEET_PATH* SCH_SHEET_LIST::FindSheetForScreen( SCH_SCREEN* aScreen ) void SCH_SHEET_LIST::UpdateSymbolInstances( - std::vector& aSymbolInstances ) + const std::vector& aSymbolInstances ) { SCH_REFERENCE_LIST symbolInstances; @@ -709,7 +709,7 @@ void SCH_SHEET_LIST::UpdateSymbolInstances( wxString path = symbolInstances[i].GetPath(); auto it = std::find_if( aSymbolInstances.begin(), aSymbolInstances.end(), - [ path ]( COMPONENT_INSTANCE_REFERENCE& r )->bool + [ path ]( const COMPONENT_INSTANCE_REFERENCE& r ) -> bool { return path == r.m_Path.AsString(); } diff --git a/eeschema/sch_sheet_path.h b/eeschema/sch_sheet_path.h index 3cce4ba6e5..20b1fbda06 100644 --- a/eeschema/sch_sheet_path.h +++ b/eeschema/sch_sheet_path.h @@ -453,7 +453,7 @@ public: * WARNING: Do not call this on anything other than the full hierarchy. * @param aSymbolInstances is the symbol path information loaded from the root schematic. */ - void UpdateSymbolInstances( std::vector& aSymbolInstances ); + void UpdateSymbolInstances( const std::vector& aSymbolInstances ); std::vector GetPaths() const; diff --git a/pcbnew/netlist_reader/pcb_netlist.cpp b/pcbnew/netlist_reader/pcb_netlist.cpp index fb16583f55..0ce5898ee6 100644 --- a/pcbnew/netlist_reader/pcb_netlist.cpp +++ b/pcbnew/netlist_reader/pcb_netlist.cpp @@ -61,7 +61,7 @@ void COMPONENT::SetModule( MODULE* aModule ) COMPONENT_NET COMPONENT::m_emptyNet; -const COMPONENT_NET& COMPONENT::GetNet( const wxString& aPinName ) +const COMPONENT_NET& COMPONENT::GetNet( const wxString& aPinName ) const { for( unsigned i = 0; i < m_nets.size(); i++ ) { diff --git a/pcbnew/netlist_reader/pcb_netlist.h b/pcbnew/netlist_reader/pcb_netlist.h index 11218dbea7..92d50eaf99 100644 --- a/pcbnew/netlist_reader/pcb_netlist.h +++ b/pcbnew/netlist_reader/pcb_netlist.h @@ -136,7 +136,7 @@ public: const COMPONENT_NET& GetNet( unsigned aIndex ) const { return m_nets[aIndex]; } - const COMPONENT_NET& GetNet( const wxString& aPinName ); + const COMPONENT_NET& GetNet( const wxString& aPinName ) const; void SortPins() { sort( m_nets.begin(), m_nets.end() ); } diff --git a/qa/eeschema/CMakeLists.txt b/qa/eeschema/CMakeLists.txt index 90f099e84a..957cfbf2c1 100644 --- a/qa/eeschema/CMakeLists.txt +++ b/qa/eeschema/CMakeLists.txt @@ -24,6 +24,13 @@ include_directories( BEFORE ${INC_BEFORE} ) +include_directories( + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/pcbnew + ${INC_AFTER} + ) + set( QA_EESCHEMA_SRCS # stuff from common which is needed...why? ${CMAKE_SOURCE_DIR}/common/colors.cpp @@ -44,6 +51,7 @@ set( QA_EESCHEMA_SRCS test_eagle_plugin.cpp test_lib_arc.cpp test_lib_part.cpp + test_netlists.cpp test_sch_pin.cpp test_sch_rtree.cpp test_sch_sheet.cpp @@ -76,6 +84,7 @@ add_dependencies( qa_eeschema eeschema ) target_link_libraries( qa_eeschema common + pcbcommon kimath qa_utils unit_test_utils @@ -102,13 +111,3 @@ set_source_files_properties( eeschema_test_utils.cpp PROPERTIES kicad_add_boost_test( qa_eeschema eeschema ) -# eeschema netlist tests -# technically this doesn't depend on KICAD_SCRIPTING but if we have that, we know we have Python. -if( KICAD_SCRIPTING_MODULES AND KICAD_NETLIST_QA ) - - add_test( NAME qa_netlist - COMMAND ${PYTHON_EXECUTABLE} test_netlists.py ${CMAKE_BINARY_DIR} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ) - -endif() diff --git a/qa/eeschema/data/netlists/test_global_promotion_2/test_global_promotion_2.net b/qa/eeschema/data/netlists/test_global_promotion_2/test_global_promotion_2.net index fc3165f030..f98560d427 100644 --- a/qa/eeschema/data/netlists/test_global_promotion_2/test_global_promotion_2.net +++ b/qa/eeschema/data/netlists/test_global_promotion_2/test_global_promotion_2.net @@ -1,15 +1,15 @@ (export (version "D") (design - (source "/home/jon/work/kicad-master/qa/eeschema/data/netlists/test_global_promotion_2/test_global_promotion_2.sch") - (date "Sun 19 Apr 2020 18:19:54 EDT") - (tool "Eeschema (5.99.0-1348-g327eb1c7c-dirty)") + (source "test_global_promotion_2.kicad_sch") + (date "Sat 23 May 2020 16:07:49 EDT") + (tool "Eeschema (5.99.0-1765-g3487670f2-dirty)") (sheet (number "1") (name "/") (tstamps "/") (title_block (title) (company) (rev) (date) - (source "test_global_promotion_2.sch") + (source "test_global_promotion_2.kicad_sch") (comment (number "1") (value "")) (comment (number "2") (value "")) (comment (number "3") (value "")) @@ -25,7 +25,7 @@ (company) (rev) (date) - (source "subsheet.sch") + (source "subsheet.kicad_sch") (comment (number "1") (value "")) (comment (number "2") (value "")) (comment (number "3") (value "")) @@ -41,7 +41,7 @@ (company) (rev) (date) - (source "subsheet.sch") + (source "subsheet.kicad_sch") (comment (number "1") (value "")) (comment (number "2") (value "")) (comment (number "3") (value "")) @@ -102,8 +102,7 @@ (fp "Connector*:*_1x??_*")) (fields (field (name "Reference") "J") - (field (name "Value") "Conn_01x03_Male") - (field (name "Datasheet") "~")) + (field (name "Value") "Conn_01x03_Male")) (pins (pin (num "1") (name "Pin_1") (type "passive")) (pin (num "2") (name "Pin_2") (type "passive")) @@ -115,28 +114,23 @@ (fp "R_*")) (fields (field (name "Reference") "R") - (field (name "Value") "R") - (field (name "Datasheet") "~")) + (field (name "Value") "R")) (pins (pin (num "1") (name "~") (type "passive")) (pin (num "2") (name "~") (type "passive"))))) - (libraries - (library (logical "Connector") - (uri "/home/jon/kicad-library/kicad-symbols//Connector.lib")) - (library (logical "Device") - (uri "/home/jon/kicad-library/kicad-symbols//Device.lib"))) + (libraries) (nets (net (code "1") (name "/LIVE") - (node (ref "J2") (pin "1") (pinfunction "Pin_1")) - (node (ref "R1") (pin "1")) - (node (ref "R2") (pin "1"))) + (node (ref "J2") (pin "2") (pinfunction "Pin_2")) + (node (ref "R3") (pin "1")) + (node (ref "R4") (pin "1"))) (net (code "2") (name "/LIVE_1") (node (ref "J1") (pin "2") (pinfunction "Pin_2")) (node (ref "R4") (pin "2"))) (net (code "3") (name "/LIVE_2") - (node (ref "J2") (pin "2") (pinfunction "Pin_2")) - (node (ref "R3") (pin "1")) - (node (ref "R4") (pin "1"))) + (node (ref "J2") (pin "1") (pinfunction "Pin_1")) + (node (ref "R1") (pin "1")) + (node (ref "R2") (pin "1"))) (net (code "4") (name "/NEUTRAL") (node (ref "J2") (pin "3") (pinfunction "Pin_3")) (node (ref "R1") (pin "2")) @@ -145,4 +139,4 @@ (node (ref "J1") (pin "1") (pinfunction "Pin_1")) (node (ref "R2") (pin "2"))) (net (code "6") (name "Net-(J1-Pad3)") - (node (ref "J1") (pin "3") (pinfunction "Pin_3"))))) \ No newline at end of file + (node (ref "J1") (pin "3") (pinfunction "Pin_3"))))) diff --git a/qa/eeschema/mocks_eeschema.cpp b/qa/eeschema/mocks_eeschema.cpp index 5aa9bf1b5c..9d5f0ee314 100644 --- a/qa/eeschema/mocks_eeschema.cpp +++ b/qa/eeschema/mocks_eeschema.cpp @@ -28,8 +28,6 @@ #include #include -// The main sheet of the project -SCH_SHEET* g_RootSheet = nullptr; // a transform matrix, to display components in lib editor TRANSFORM DefaultTransform = TRANSFORM( 1, 0, 0, -1 ); diff --git a/qa/eeschema/test_netlists.cpp b/qa/eeschema/test_netlists.cpp new file mode 100644 index 0000000000..655381a60b --- /dev/null +++ b/qa/eeschema/test_netlists.cpp @@ -0,0 +1,241 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program 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 3 of the License, or (at your + * option) any later version. + * + * This program 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 this program. If not, see . + */ + +#include +#include "eeschema_test_utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class TEST_NETLISTS_FIXTURE +{ +public: + TEST_NETLISTS_FIXTURE() : + m_schematic( &m_project ) + { + m_pi = SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ); + } + + void loadSchematic( const wxString& aBaseName ); + + wxString getNetlistFileName( bool aTest = false ); + + void writeNetlist(); + + void compareNetlists(); + + void cleanup(); + + void doNetlistTest( const wxString& aBaseName ); + + ///> Schematic to load + SCHEMATIC m_schematic; + + ///> Dummy project + PROJECT m_project; + + SCH_PLUGIN* m_pi; +}; + + +static wxString getSchematicFile( const wxString& aBaseName ) +{ + wxFileName fn = KI_TEST::GetEeschemaTestDataDir(); + fn.AppendDir( "netlists" ); + fn.AppendDir( aBaseName ); + fn.SetName( aBaseName ); + fn.SetExt( KiCadSchematicFileExtension ); + + return fn.GetFullPath(); +} + + +void TEST_NETLISTS_FIXTURE::loadSchematic( const wxString& aBaseName ) +{ + wxString fn = getSchematicFile( aBaseName ); + + BOOST_TEST_MESSAGE( fn ); + + wxFileName pro( fn ); + pro.SetExt( ProjectFileExtension ); + + m_project.SetProjectFullName( pro.GetFullPath() ); + m_project.SetElem( PROJECT::ELEM_SCH_PART_LIBS, nullptr ); + + m_schematic.Reset(); + m_schematic.SetRoot( m_pi->Load( fn, &m_schematic ) ); + + BOOST_REQUIRE_EQUAL( m_pi->GetError().IsEmpty(), true ); + + m_schematic.CurrentSheet().push_back( &m_schematic.Root() ); + + SCH_SCREENS screens( m_schematic.Root() ); + + for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() ) + screen->UpdateLocalLibSymbolLinks(); + + SCH_SHEET_LIST sheets = m_schematic.GetSheets(); + + // Restore all of the loaded symbol instances from the root sheet screen. + sheets.UpdateSymbolInstances( m_schematic.RootScreen()->GetSymbolInstances() ); + + sheets.AnnotatePowerSymbols(); + + // NOTE: This is required for multi-unit symbols to be correct + // Normally called from SCH_EDIT_FRAME::FixupJunctions() but could be refactored + for( SCH_SHEET_PATH& sheet : sheets ) + sheet.UpdateAllScreenReferences(); + + // NOTE: SchematicCleanUp is not called; QA schematics must already be clean or else + // SchematicCleanUp must be freed from its UI dependencies. + + m_schematic.ConnectionGraph()->Recalculate( sheets, true ); +} + + +wxString TEST_NETLISTS_FIXTURE::getNetlistFileName( bool aTest ) +{ + wxFileName netFile = m_schematic.Prj().GetProjectFullName(); + + if( aTest ) + netFile.SetName( netFile.GetName() + "_test" ); + + netFile.SetExt( NetlistFileExtension ); + + return netFile.GetFullPath(); +} + + +void TEST_NETLISTS_FIXTURE::writeNetlist() +{ + auto exporter = std::make_unique( &m_schematic ); + BOOST_REQUIRE_EQUAL( exporter->WriteNetlist( getNetlistFileName( true ), 0 ), true ); +} + + +void TEST_NETLISTS_FIXTURE::compareNetlists() +{ + NETLIST golden; + NETLIST test; + + { + std::unique_ptr netlistReader( + NETLIST_READER::GetNetlistReader( &golden, getNetlistFileName(), wxEmptyString ) ); + + BOOST_REQUIRE_NO_THROW( netlistReader->LoadNetlist() ); + } + + { + std::unique_ptr netlistReader( NETLIST_READER::GetNetlistReader( + &test, getNetlistFileName( true ), wxEmptyString ) ); + + BOOST_REQUIRE_NO_THROW( netlistReader->LoadNetlist() ); + } + + // Number of components should match + BOOST_REQUIRE_EQUAL( golden.GetCount(), test.GetCount() ); + + for( unsigned i = 0; i < golden.GetCount(); i++ ) + { + COMPONENT* goldenComp = golden.GetComponent( i ); + COMPONENT* refComp = test.GetComponentByReference( goldenComp->GetReference() ); + + // Retrieval by reference + BOOST_REQUIRE_NE( refComp, nullptr ); + + // Retrieval by KIID + COMPONENT* pathComp = test.GetComponentByPath( goldenComp->GetPath() ); + BOOST_REQUIRE_NE( pathComp, nullptr ); + + // We should have found the same component + BOOST_REQUIRE_EQUAL( refComp->GetReference(), pathComp->GetReference() ); + + // And that component should have the same number of attached nets + BOOST_REQUIRE_EQUAL( goldenComp->GetNetCount(), refComp->GetNetCount() ); + + for( unsigned net = 0; net < goldenComp->GetNetCount(); net++ ) + { + const COMPONENT_NET& goldenNet = goldenComp->GetNet( net ); + const COMPONENT_NET& testNet = refComp->GetNet( net ); + + // The two nets at the same index should be identical + BOOST_REQUIRE_EQUAL( goldenNet.GetPinName(), testNet.GetPinName() ); + BOOST_REQUIRE_EQUAL( goldenNet.GetNetName(), testNet.GetNetName() ); + } + } +} + + +void TEST_NETLISTS_FIXTURE::cleanup() +{ + wxRemoveFile( getNetlistFileName( true ) ); +} + + +void TEST_NETLISTS_FIXTURE::doNetlistTest( const wxString& aBaseName ) +{ + loadSchematic( aBaseName ); + writeNetlist(); + compareNetlists(); + cleanup(); +} + + +BOOST_FIXTURE_TEST_SUITE( Netlists, TEST_NETLISTS_FIXTURE ) + + +BOOST_AUTO_TEST_CASE( FindPlugin ) +{ + BOOST_CHECK_NE( m_pi, nullptr ); +} + + +BOOST_AUTO_TEST_CASE( GlobalPromotion ) +{ + doNetlistTest( "test_global_promotion" ); +} + + +BOOST_AUTO_TEST_CASE( GlobalPromotion2 ) +{ + doNetlistTest( "test_global_promotion_2" ); +} + + +BOOST_AUTO_TEST_CASE( Video ) +{ + doNetlistTest( "video" ); +} + + +BOOST_AUTO_TEST_CASE( ComplexHierarchy ) +{ + doNetlistTest( "complex_hierarchy" ); +} + + +BOOST_AUTO_TEST_SUITE_END()