/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2022-2023 Roberto Fernandez Bautista * Copyright (C) 2022-2023 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 #include #include #include #include // Modules under test: #include #include BOOST_AUTO_TEST_SUITE( CadstarPartParser ); static std::string getCadstarTestFile( const std::string& aFile ) { return KI_TEST::GetEeschemaTestDataDir() + "/plugins/cadstar/" + aFile; } BOOST_AUTO_TEST_CASE( AnalyzeGrammar ) { // Verify the grammar has no loops without progress and other issues // See: https://github.com/taocpp/PEGTL/blob/3.2.7/doc/Grammar-Analysis.md const std::size_t grammarIssues = tao::pegtl::analyze(); BOOST_CHECK_EQUAL( grammarIssues, 0 ); const std::size_t headerIssues = tao::pegtl::analyze(); BOOST_CHECK_EQUAL( headerIssues, 0 ); } struct CHECK_HEADER_CASE { std::string m_CaseName; std::string m_Content; bool m_ExpectedResult; }; static const std::vector check_header_cases = { { "1: Normal header", "# Format 32\r\n", true }, { "2: Normal header, extra content", "# Format 32\r\nExtraUnrelatedContent", true }, { "3: Normal header extra spaces (1)", "# Format 32\r\n", true }, { "4: Normal header extra spaces (2)", "# FORMAT 32\r\n", true }, { "5: Normal header on 2nd line", "\r\n# Format 32\r\n", false }, { "6: Normal header prepended", "+# Format 32\r\n", false }, { "7: Normal header prepended spaces", " # Format 32\r\n", false }, // There appear to be some files on the internet that just don't have a header and // start straight away with the part definitions. { "8: No header", ".PART-NAME :1 ;Part Descr\r\n", true }, { "9: No header, extra content", ".PART-NAME :1 ;Part Descr\r\nExtra", true }, { "10: No header, on 2nd line", "\r\n.PART-NAME :1 ;Part Descr\r\n", true }, { "11: No header, on 3rd line", "\r\n\r\n.PART-NAME :1 ;Part Descr\r\n", true }, { "12: No header, on 4th line", "\r\n\r\n.PART-NAME :1 ;Part Descr\r\n", true }, { "13: No header, on 4th line", "\r\n\r\n\r\n\r\n.PART-NAME :1 ;P Descr\r\n", true }, { "14: No header, on 5th line", "\r\n\r\n\r\n\r\n\r\n.P-NAME :1 ;PDescr\r\n", true }, { "15: No header, on 6th line", "\r\n\r\n\r\n\r\n\r\n\r\n.P-NAM :1 ;PDes\r\n", false }, { "16: No header, space prepend", " .PART-NAME :1 ;Part Descr\r\n", false }, { "17: No header, spaces & 2nd line", " \r\n.PART-NAME :1 ;Part Descr\r\n", true }, { "18: No header, 2nd line & space", " \r\n .PART-NAME :1 ;Part Descr\r\n", false }, }; BOOST_AUTO_TEST_CASE( CheckHeader ) { for( const auto& c : check_header_cases ) { BOOST_TEST_INFO_SCOPE( c.m_CaseName ); CADSTAR_PARTS_LIB_PARSER p; BOOST_CHECK_EQUAL( p.CheckContentHeader( c.m_Content ), c.m_ExpectedResult ); } } BOOST_AUTO_TEST_CASE( ReadFile ) { CADSTAR_PARTS_LIB_PARSER p; // Test a programatically generated files (see writeCadstarFile.py) std::vector testFiles = { "dummycadstarlib.lib", "dummycadstarlibwithheader.lib" }; for( auto testFile : testFiles ) { auto ret = p.ReadFile( getCadstarTestFile( testFile ) ); KI_CHECK_OPT_EQUAL( ret.m_FormatNumber, 32 ); BOOST_CHECK_EQUAL( ret.m_PartEntries.size(), 100 ); int i = 0; for( CADSTAR_PART_ENTRY& partEntry : ret.m_PartEntries ) { // Part header KI_CHECK_OPT_EQUAL BOOST_CHECK_EQUAL( partEntry.m_Name, "PartName" + std::to_string( i ) ); KI_CHECK_OPT_EQUAL( partEntry.m_Number, std::to_string( i * 5 ) ); KI_CHECK_OPT_EQUAL( partEntry.m_Version, std::to_string( 2 ) ); KI_CHECK_OPT_EQUAL( partEntry.m_Description, "Part " + std::to_string( i ) + " Description" ); BOOST_CHECK_EQUAL( partEntry.m_Pcb_component, "FOOTPRINT" + std::to_string( i ) ); KI_CHECK_OPT_EQUAL( partEntry.m_Pcb_alternate, "variant" + std::to_string( i * 5 ) ); KI_CHECK_OPT_EQUAL( partEntry.m_Value, std::to_string( i ) + " uH" ); BOOST_CHECK_EQUAL( partEntry.m_ComponentStem, "L" ); KI_CHECK_OPT_EQUAL( partEntry.m_MaxPinCount, i + 10 ); BOOST_CHECK_EQUAL( partEntry.m_GateSwappingAllowed, i % 10 != 1 ); BOOST_CHECK_EQUAL( partEntry.m_PinsVisible, i % 5 != 1 ); KI_CHECK_OPT_EQUAL( partEntry.m_SpicePartName, "PartName" + std::to_string( i ) ); KI_CHECK_OPT_EQUAL( partEntry.m_SpiceModel, std::to_string( i ) + "uH" ); KI_CHECK_OPT_EQUAL( partEntry.m_AcceptancePartName, "PartName" + std::to_string( i ) ); KI_CHECK_OPT_EQUAL( partEntry.m_AcceptanceText, "Acceptance" + std::to_string( i ) ); // User part attributes (* lines) BOOST_CHECK_EQUAL( partEntry.m_UserAttributes["UserFieldpartNo"], std::to_string( i * 5 ) ); BOOST_CHECK_EQUAL( partEntry.m_UserAttributes["UserFieldpartNoCreated by"], "Person" + std::to_string( i ) ); // SCH attributes ($ lines) BOOST_CHECK_EQUAL( partEntry.m_SchAttributes["SCH val1"].m_ReadOnly, false ); BOOST_CHECK_EQUAL( partEntry.m_SchAttributes["SCH val1"].m_Value, "val" + std::to_string( i ) ); BOOST_CHECK_EQUAL( partEntry.m_SchAttributes["SCH val2"].m_ReadOnly, true ); BOOST_CHECK_EQUAL( partEntry.m_SchAttributes["SCH val2"].m_Value, "readOnly" + std::to_string( i ) ); // PCB attributes (% lines) BOOST_CHECK_EQUAL( partEntry.m_PcbAttributes["PCB val1"].m_ReadOnly, false ); BOOST_CHECK_EQUAL( partEntry.m_PcbAttributes["PCB val1"].m_Value, "val" + std::to_string( i ) ); BOOST_CHECK_EQUAL( partEntry.m_PcbAttributes["PCB val2"].m_ReadOnly, true ); BOOST_CHECK_EQUAL( partEntry.m_PcbAttributes["PCB val2"].m_Value, "readOnly" + std::to_string( i ) ); // Parts attributes (~ lines) BOOST_CHECK_EQUAL( partEntry.m_PartAttributes["Part val1"].m_ReadOnly, false ); BOOST_CHECK_EQUAL( partEntry.m_PartAttributes["Part val1"].m_Value, "val" + std::to_string( i ) ); BOOST_CHECK_EQUAL( partEntry.m_PartAttributes["Part val2"].m_ReadOnly, true ); BOOST_CHECK_EQUAL( partEntry.m_PartAttributes["Part val2"].m_Value, "readOnly" + std::to_string( i ) ); // PCB and SCH attributes (@ lines) BOOST_CHECK_EQUAL( partEntry.m_SchAndPcbAttributes["SCH and PCB val1"].m_ReadOnly, false ); BOOST_CHECK_EQUAL( partEntry.m_SchAndPcbAttributes["SCH and PCB val1"].m_Value, "val" + std::to_string( i ) ); BOOST_CHECK_EQUAL( partEntry.m_SchAndPcbAttributes["SCH and PCB val2"].m_ReadOnly, true ); BOOST_CHECK_EQUAL( partEntry.m_SchAndPcbAttributes["SCH and PCB val2"].m_Value, "readOnly" + std::to_string( i ) ); // Check symbol name and pins BOOST_REQUIRE_EQUAL( partEntry.m_Symbols.size(), 1 ); BOOST_CHECK_EQUAL( partEntry.m_Symbols[0].m_SymbolName, "Symbol" + std::to_string( i ) ); KI_CHECK_OPT_EQUAL( partEntry.m_Symbols[0].m_SymbolAlternateName, std::optional() ); BOOST_REQUIRE_EQUAL( partEntry.m_Symbols[0].m_Pins.size(), 2 ); BOOST_CHECK_EQUAL( partEntry.m_Symbols[0].m_Pins[0].m_Identifier, 1 ); BOOST_CHECK_EQUAL( partEntry.m_Symbols[0].m_Pins[1].m_Identifier, 2 ); // Check hidden pins BOOST_REQUIRE_EQUAL( partEntry.m_HiddenPins.size(), 1 ); BOOST_CHECK_EQUAL( partEntry.m_HiddenPins.count( "GND" ), 1 ); i++; } } } BOOST_AUTO_TEST_CASE( ReadContent ) { std::string test = "# Format 32\r\n" "\r\n" "\r\n" "+N0 'root' &\r\n" "'part1' &\r\n" "'part2'\r\n" "+N1 N0 'subnode1' &\r\n" "'part3' &\r\n" "'part4'\r\n" "\r\n" " \r\n" "\r\n" ". ():;\r\n" " ()\r\n" "*VALUE \r\n" "*PNM 1=A1 2=A2 3=B1 4=B2 5=C1 6=C2\r\n" // = = etc "*PLB 1=\"VCC\" 2=\"GND\" 3=\"'EN\" 4=\"OUT\" 5=\"OUT\" 6=\"IN\"\r\n" // =