KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pads_sch_import.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * 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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
22
23#include <base_units.h>
24#include <lib_symbol.h>
25#include <sch_field.h>
26#include <sch_label.h>
27#include <schematic.h>
29#include <sch_io/sch_io_mgr.h>
30#include <sch_line.h>
31#include <sch_screen.h>
32#include <sch_sheet.h>
33#include <sch_sheet_path.h>
34#include <sch_symbol.h>
35#include <sch_text.h>
37
38#include <algorithm>
39#include <map>
40#include <vector>
41
42
43namespace
44{
45
46struct PADS_SCH_IMPORT_FIXTURE
47{
48 PADS_SCH_IMPORT_FIXTURE() : m_schematic( nullptr )
49 {
50 m_settingsManager.LoadProject( "" );
51 m_schematic.SetProject( &m_settingsManager.Prj() );
52 m_schematic.Reset();
53 }
54
55 ~PADS_SCH_IMPORT_FIXTURE()
56 {
57 m_schematic.Reset();
58 }
59
60 SETTINGS_MANAGER m_settingsManager;
61 SCHEMATIC m_schematic;
62};
63
64} // namespace
65
66
67BOOST_FIXTURE_TEST_SUITE( PadsSchImport, PADS_SCH_IMPORT_FIXTURE )
68
69
70BOOST_AUTO_TEST_CASE( CanReadSchematicFile )
71{
72 SCH_IO_PADS plugin;
73
74 wxString padsFile = wxString::FromUTF8(
75 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt" );
76
77 BOOST_CHECK( plugin.CanReadSchematicFile( padsFile ) );
78}
79
80
81BOOST_AUTO_TEST_CASE( CanReadSchematicFile_RejectNonPads )
82{
83 SCH_IO_PADS plugin;
84
85 wxString kicadFile = wxString::FromUTF8(
86 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/simple_schematic.txt" );
87
88 BOOST_CHECK( plugin.CanReadSchematicFile( kicadFile ) );
89}
90
91
93{
94 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_PADS ) );
95 BOOST_CHECK_NE( pi.get(), nullptr );
96}
97
98
99BOOST_AUTO_TEST_CASE( MultiGateImport )
100{
101 SCH_IO_PADS plugin;
102
103 wxString padsFile = wxString::FromUTF8(
104 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/multigate_schematic.txt" );
105
106 SCH_SHEET* rootSheet = plugin.LoadSchematicFile( padsFile, &m_schematic );
107 BOOST_REQUIRE( rootSheet );
108 BOOST_REQUIRE( rootSheet->GetScreen() );
109
110 SCH_SCREEN* screen = rootSheet->GetScreen();
111
112 // Collect U1 symbols
113 std::vector<SCH_SYMBOL*> u1Symbols;
114 SCH_SHEET_PATH rootPath;
115 rootPath.push_back( rootSheet );
116
117 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
118 {
119 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
120
121 if( sym->GetRef( &rootPath ) == wxT( "U1" ) )
122 u1Symbols.push_back( sym );
123 }
124
125 BOOST_REQUIRE_EQUAL( u1Symbols.size(), 2u );
126
127 // Sort by unit number for deterministic checks
128 std::sort( u1Symbols.begin(), u1Symbols.end(),
129 []( const SCH_SYMBOL* a, const SCH_SYMBOL* b )
130 {
131 return a->GetUnit() < b->GetUnit();
132 } );
133
134 // Unit 1 (gate A with TL082A decal) should have 5 pins
135 BOOST_CHECK_EQUAL( u1Symbols[0]->GetUnit(), 1 );
136 BOOST_CHECK_EQUAL( u1Symbols[0]->GetLibPins().size(), 5u );
137
138 // Unit 2 (gate B with TL082 decal) should have 3 pins
139 BOOST_CHECK_EQUAL( u1Symbols[1]->GetUnit(), 2 );
140 BOOST_CHECK_EQUAL( u1Symbols[1]->GetLibPins().size(), 3u );
141
142 // Both should share the same multi-unit LIB_SYMBOL with 2 units
143 BOOST_CHECK( u1Symbols[0]->IsMultiUnit() );
144 BOOST_CHECK_EQUAL( u1Symbols[0]->GetUnitCount(), 2 );
145
146 // Both references should be "U1" (not "U1-A" or "U1-B")
147 BOOST_CHECK_EQUAL( u1Symbols[0]->GetRef( &rootPath ), wxT( "U1" ) );
148 BOOST_CHECK_EQUAL( u1Symbols[1]->GetRef( &rootPath ), wxT( "U1" ) );
149}
150
151
152BOOST_AUTO_TEST_CASE( Issue23420_HeaderWithCodePageSuffix )
153{
154 // Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23420
155 // PADS Logic schematics exported with a code page suffix in the header
156 // (e.g. *PADS-LOGIC-V9.0-CP1250*) must be detected and parsed.
157 SCH_IO_PADS plugin;
158
159 wxString padsFile = wxString::FromUTF8(
161 + "/plugins/pads/issue23420_codepage_schematic.txt" );
162
163 BOOST_CHECK( plugin.CanReadSchematicFile( padsFile ) );
164
165 SCH_SHEET* rootSheet = plugin.LoadSchematicFile( padsFile, &m_schematic );
166
167 BOOST_REQUIRE( rootSheet );
168 BOOST_REQUIRE( rootSheet->GetScreen() );
169}
170
171
172BOOST_AUTO_TEST_CASE( CanReadLibrary )
173{
174 SCH_IO_PADS plugin;
175
176 wxString padsFile = wxString::FromUTF8(
177 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt" );
178
179 BOOST_CHECK( plugin.CanReadLibrary( padsFile ) );
180}
181
182
183BOOST_AUTO_TEST_CASE( EnumerateSymbolLib_NamesFromSchematic )
184{
185 SCH_IO_PADS plugin;
186
187 wxString padsFile = wxString::FromUTF8(
188 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt" );
189
190 wxArrayString names;
191 BOOST_CHECK_NO_THROW( plugin.EnumerateSymbolLib( names, padsFile ) );
192 BOOST_CHECK_GT( names.GetCount(), 0u );
193}
194
195
196BOOST_AUTO_TEST_CASE( EnumerateSymbolLib_ReturnsLibSymbols )
197{
198 SCH_IO_PADS plugin;
199
200 wxString padsFile = wxString::FromUTF8(
201 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt" );
202
203 std::vector<LIB_SYMBOL*> symbols;
204 BOOST_CHECK_NO_THROW( plugin.EnumerateSymbolLib( symbols, padsFile ) );
205 BOOST_CHECK_GT( symbols.size(), 0u );
206
207 for( LIB_SYMBOL* sym : symbols )
208 BOOST_REQUIRE( sym != nullptr );
209}
210
211
212BOOST_AUTO_TEST_CASE( LoadSymbol_ByName )
213{
214 SCH_IO_PADS plugin;
215
216 wxString padsFile = wxString::FromUTF8(
217 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt" );
218
219 wxArrayString names;
220 plugin.EnumerateSymbolLib( names, padsFile );
221
222 BOOST_REQUIRE_GT( names.GetCount(), 0u );
223
224 LIB_SYMBOL* sym = plugin.LoadSymbol( padsFile, names.Item( 0 ) );
225 BOOST_REQUIRE( sym != nullptr );
226 BOOST_CHECK_EQUAL( sym->GetName(), names.Item( 0 ) );
227}
228
229
230BOOST_AUTO_TEST_CASE( LoadSymbol_UnknownReturnsNull )
231{
232 SCH_IO_PADS plugin;
233
234 wxString padsFile = wxString::FromUTF8(
235 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt" );
236
237 LIB_SYMBOL* sym = plugin.LoadSymbol( padsFile, wxT( "NO_SUCH_SYMBOL_12345" ) );
238 BOOST_CHECK( sym == nullptr );
239}
240
241
242BOOST_AUTO_TEST_CASE( MultiGatePartTypeBecomesMultiUnitLibSymbol )
243{
244 SCH_IO_PADS plugin;
245
246 wxString padsFile = wxString::FromUTF8(
247 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/multigate_schematic.txt" );
248
249 std::vector<LIB_SYMBOL*> symbols;
250 BOOST_CHECK_NO_THROW( plugin.EnumerateSymbolLib( symbols, padsFile ) );
251
252 bool foundMultiUnit = false;
253
254 for( LIB_SYMBOL* sym : symbols )
255 {
256 if( sym && sym->GetUnitCount() > 1 )
257 {
258 foundMultiUnit = true;
259 break;
260 }
261 }
262
263 BOOST_CHECK( foundMultiUnit );
264}
265
266
267BOOST_AUTO_TEST_CASE( IsLibraryNotWritable )
268{
269 SCH_IO_PADS plugin;
270
271 wxString padsFile = wxString::FromUTF8(
272 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/symbols_schematic.txt" );
273
274 BOOST_CHECK( !plugin.IsLibraryWritable( padsFile ) );
275}
276
277
278BOOST_AUTO_TEST_CASE( Issue24284_TextItemsPlacedOnCorrectSheet )
279{
280 // Regression test for https://gitlab.com/kicad/code/kicad/-/issues/24284
281 // Multi-sheet PADS Logic schematics have one *TEXT* and *LINES* block per
282 // *SHT*. Before the fix every text/line item was placed on the first
283 // sheet, causing page-number text from all sheets to stack on top of each
284 // other and border graphics to overlap.
285 SCH_IO_PADS plugin;
286
287 wxString padsFile = wxString::FromUTF8(
289 + "/plugins/pads/issue24284_multisheet_text.txt" );
290
291 BOOST_REQUIRE( plugin.CanReadSchematicFile( padsFile ) );
292
293 SCH_SHEET* rootSheet = plugin.LoadSchematicFile( padsFile, &m_schematic );
294 BOOST_REQUIRE( rootSheet );
295 BOOST_REQUIRE( rootSheet->GetScreen() );
296
297 // Collect text and line content keyed by hierarchical sheet name.
298 std::map<wxString, std::vector<wxString>> textBySheet;
299 std::map<wxString, int> lineCountBySheet;
300
301 for( SCH_ITEM* item : rootSheet->GetScreen()->Items().OfType( SCH_SHEET_T ) )
302 {
303 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
304 wxString sheetName = sheet->GetField( FIELD_T::SHEET_NAME )->GetText();
305
306 for( SCH_ITEM* screenItem : sheet->GetScreen()->Items().OfType( SCH_TEXT_T ) )
307 {
308 SCH_TEXT* txt = static_cast<SCH_TEXT*>( screenItem );
309 textBySheet[sheetName].push_back( txt->GetText() );
310 }
311
312 for( SCH_ITEM* screenItem : sheet->GetScreen()->Items().OfType( SCH_LINE_T ) )
313 {
314 (void) screenItem;
315 lineCountBySheet[sheetName]++;
316 }
317 }
318
319 for( int sheetNum = 1; sheetNum <= 3; ++sheetNum )
320 {
321 wxString sheetName = wxString::Format( wxT( "Page%d" ), sheetNum );
322 wxString pageText = wxString::Format( wxT( "PAGE %d OF 3" ), sheetNum );
323 wxString bodyText = wxString::Format( wxT( "TEXT ON SHEET %d" ), sheetNum );
324
325 BOOST_REQUIRE_EQUAL( textBySheet.count( sheetName ), 1u );
326 BOOST_CHECK_EQUAL( textBySheet[sheetName].size(), 2u );
327 BOOST_CHECK( std::find( textBySheet[sheetName].begin(), textBySheet[sheetName].end(),
328 pageText ) != textBySheet[sheetName].end() );
329 BOOST_CHECK( std::find( textBySheet[sheetName].begin(), textBySheet[sheetName].end(),
330 bodyText ) != textBySheet[sheetName].end() );
331 BOOST_CHECK_EQUAL( lineCountBySheet[sheetName], 1 );
332 }
333}
334
335
336// Issue 23855 (#1): an off-page connector whose stub wire is zero-length must take its
337// global-label orientation from the authoritative *NETNAMES* offset, not from the
338// degenerate wire direction. The two SP1 anchors carry opposite X offsets and must yield
339// opposite spin styles.
340BOOST_AUTO_TEST_CASE( Issue23855_GlobalLabelOrientationFromNetNames )
341{
342 SCH_IO_PADS plugin;
343
344 wxString padsFile = wxString::FromUTF8(
345 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/issue23855_schematic.txt" );
346
347 SCH_SHEET* rootSheet = plugin.LoadSchematicFile( padsFile, &m_schematic );
348 BOOST_REQUIRE( rootSheet );
349 BOOST_REQUIRE( rootSheet->GetScreen() );
350
351 SCH_SCREEN* screen = rootSheet->GetScreen();
352
353 // PADS anchor positions in mils -> KiCad screen X (Y-up flipped on import).
354 const int milToIU = schIUScale.MilsToIU( 1 );
355 const int cnSideX = 1400 * milToIU; // @@@O0, x_offset +350 -> text reads right
356 const int r1SideX = 2800 * milToIU; // @@@O1, x_offset -360 -> text reads left
357
360 bool foundCn = false;
361 bool foundR1 = false;
362
363 for( SCH_ITEM* item : screen->Items().OfType( SCH_GLOBAL_LABEL_T ) )
364 {
365 SCH_LABEL_BASE* lbl = static_cast<SCH_LABEL_BASE*>( item );
366
367 if( lbl->GetText() != wxT( "SP1" ) )
368 continue;
369
370 if( lbl->GetPosition().x == cnSideX )
371 {
372 foundCn = true;
373 cnSpin = lbl->GetSpinStyle();
374 }
375 else if( lbl->GetPosition().x == r1SideX )
376 {
377 foundR1 = true;
378 r1Spin = lbl->GetSpinStyle();
379 }
380 }
381
382 BOOST_REQUIRE( foundCn );
383 BOOST_REQUIRE( foundR1 );
384
385 // The CN1-side label extends to the right; the R1-side label (degenerate wire)
386 // extends to the left thanks to the NETNAMES override.
387 BOOST_CHECK( cnSpin == SPIN_STYLE::RIGHT );
388 BOOST_CHECK( r1Spin == SPIN_STYLE::LEFT );
389}
390
391
392// Issue 23855 (#5): a 90 degree rotated part must place its reference and value fields at
393// the absolute coordinates authored in PADS. PADS stores attribute offsets in the placed
394// (post-rotation) frame, so the importer applies the offset directly without re-rotating.
395BOOST_AUTO_TEST_CASE( Issue23855_RotatedPartFieldPositions )
396{
397 SCH_IO_PADS plugin;
398
399 wxString padsFile = wxString::FromUTF8(
400 KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/issue23855_schematic.txt" );
401
402 SCH_SHEET* rootSheet = plugin.LoadSchematicFile( padsFile, &m_schematic );
403 BOOST_REQUIRE( rootSheet );
404 BOOST_REQUIRE( rootSheet->GetScreen() );
405
406 SCH_SCREEN* screen = rootSheet->GetScreen();
407 SCH_SHEET_PATH rootPath;
408 rootPath.push_back( rootSheet );
409
410 SCH_SYMBOL* d5 = nullptr;
411
412 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
413 {
414 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
415
416 if( sym->GetRef( &rootPath ) == wxT( "D5" ) )
417 d5 = sym;
418 }
419
420 BOOST_REQUIRE( d5 != nullptr );
421
423 SCH_FIELD* valF = d5->GetField( FIELD_T::VALUE );
424
425 VECTOR2I symPos = d5->GetPosition();
426 VECTOR2I refRel = refF->GetPosition() - symPos;
427 VECTOR2I valRel = valF->GetPosition() - symPos;
428
429 // REF-DES PADS offset (210, 230); PART-TYPE/value PADS offset (-70, 520). PADS Y is up,
430 // so the screen Y offset is negated.
431 BOOST_CHECK_EQUAL( refRel.x, schIUScale.MilsToIU( 210 ) );
432 BOOST_CHECK_EQUAL( refRel.y, -schIUScale.MilsToIU( 230 ) );
433 BOOST_CHECK_EQUAL( valRel.x, schIUScale.MilsToIU( -70 ) );
434 BOOST_CHECK_EQUAL( valRel.y, -schIUScale.MilsToIU( 520 ) );
435
436 // Rotated attribute text keeps the PADS text angle and the authored justification
437 // (codes 4 and 5 both decode to top-left in the text's reading frame).
438 BOOST_CHECK_EQUAL( refF->GetTextAngle().AsDegrees(), 90.0 );
439 BOOST_CHECK_EQUAL( valF->GetTextAngle().AsDegrees(), 90.0 );
444}
445
446
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
double AsDegrees() const
Definition eda_angle.h:116
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:110
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:221
virtual EDA_ANGLE GetTextAngle() const
Definition eda_text.h:168
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:224
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:221
Define a library symbol object.
Definition lib_symbol.h:79
wxString GetName() const override
Definition lib_symbol.h:141
VECTOR2I GetPosition() const override
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:128
A SCH_IO derivation for loading PADS Logic schematic files.
Definition sch_io_pads.h:42
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aPartName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this IO object can read the specified library file/directory.
bool IsLibraryWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
Definition sch_io_pads.h:75
bool CanReadSchematicFile(const wxString &aFileName) const override
Checks if this SCH_IO can read the specified schematic file.
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load information from some input file format that this SCH_IO implementation knows about,...
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
SPIN_STYLE GetSpinStyle() const
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:139
Schematic symbol object.
Definition sch_symbol.h:69
VECTOR2I GetPosition() const override
Definition sch_symbol.h:885
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
VECTOR2I GetPosition() const override
Definition sch_text.h:146
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(CanReadSchematicFile)
VECTOR2I end
BOOST_CHECK_EQUAL(result, "25.4")
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_TOP
@ SCH_LINE_T
Definition typeinfo.h:160
@ SCH_SYMBOL_T
Definition typeinfo.h:169
@ SCH_SHEET_T
Definition typeinfo.h:172
@ SCH_TEXT_T
Definition typeinfo.h:148
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:165
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683