KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_altium_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 <connection_graph.h>
24#include <erc/erc.h>
25#include <lib_id.h>
26#include <reporter.h>
27#include <schematic.h>
29#include <sch_label.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>
36
37#include <map>
38#include <optional>
39#include <set>
40#include <vector>
41
42
43namespace
44{
45
46struct ALTIUM_SCH_IMPORT_FIXTURE
47{
48 ALTIUM_SCH_IMPORT_FIXTURE() :
49 m_loadInfoScope( &NULL_REPORTER::GetInstance() ),
50 m_schematic( nullptr )
51 {
52 m_settingsManager.LoadProject( "" );
53 m_schematic.SetProject( &m_settingsManager.Prj() );
54 m_schematic.Reset();
55 }
56
57 ~ALTIUM_SCH_IMPORT_FIXTURE() { m_schematic.Reset(); }
58
59 wxString dataFile( const wxString& aName ) const
60 {
61 return wxString::FromUTF8( KI_TEST::GetEeschemaTestDataDir()
62 + "/plugins/altium/issue22943/" )
63 + aName;
64 }
65
66 wxString issue24861DataFile( const wxString& aName ) const
67 {
68 return wxString::FromUTF8( KI_TEST::GetEeschemaTestDataDir()
69 + "/plugins/altium/issue24861/" )
70 + aName;
71 }
72
73 wxString ticket1303DataFile( const wxString& aName ) const
74 {
75 return wxString::FromUTF8( KI_TEST::GetEeschemaTestDataDir()
76 + "/plugins/altium/ticket1303/" )
77 + aName;
78 }
79
80 wxString issue16903DataFile( const wxString& aName ) const
81 {
82 return wxString::FromUTF8( KI_TEST::GetEeschemaTestDataDir()
83 + "/plugins/altium/issue16903/" )
84 + aName;
85 }
86
87 wxString eDPAdapterDataFile( const wxString& aName ) const
88 {
89 return wxString::FromUTF8( KI_TEST::GetTestDataRootDir()
90 + "pcbnew/plugins/altium/eDP_adapter_dvt1_source/" )
91 + aName;
92 }
93
94 // The fixture project has no symbol libraries, so every symbol link fails by design
95 LOAD_INFO_REPORTER_SCOPE m_loadInfoScope;
96 SETTINGS_MANAGER m_settingsManager;
97 SCHEMATIC m_schematic;
98};
99
100} // namespace
101
102
103BOOST_FIXTURE_TEST_SUITE( AltiumSchImport, ALTIUM_SCH_IMPORT_FIXTURE )
104
105
106// https://gitlab.com/kicad/code/kicad/-/issues/22943
107// A component placed from an external Altium library must be addressed by a well-formed library
108// id (source library nickname + real library reference) so it resolves against the library that
109// project import registers, instead of the importer's internal per-placement name.
110BOOST_AUTO_TEST_CASE( Issue22943_SourceLibrarySymbolLibId )
111{
112 SCH_IO_ALTIUM plugin;
113
114 SCH_SHEET* rootSheet = plugin.LoadSchematicFile( dataFile( "1_cover.SchDoc" ), &m_schematic );
115 BOOST_REQUIRE( rootSheet );
116 BOOST_REQUIRE( rootSheet->GetScreen() );
117
118 // The real reference names of every symbol contained in the source library.
119 wxArrayString libNames;
120 plugin.EnumerateSymbolLib( libNames, dataFile( "mounting_holes.SchLib" ) );
121
122 std::set<wxString> libSymbolNames( libNames.begin(), libNames.end() );
123 BOOST_REQUIRE( libSymbolNames.count( wxT( "MH M3" ) ) );
124
125 std::vector<SCH_SYMBOL*> fromMountingHoles;
126
127 for( SCH_ITEM* item : rootSheet->GetScreen()->Items().OfType( SCH_SYMBOL_T ) )
128 {
129 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
130
131 if( sym->GetLibId().GetUniStringLibNickname() == wxT( "mounting_holes" ) )
132 fromMountingHoles.push_back( sym );
133 }
134
135 BOOST_REQUIRE( !fromMountingHoles.empty() );
136
137 // Every symbol drawn from the source library must name an item that actually exists there.
138 for( SCH_SYMBOL* sym : fromMountingHoles )
139 {
140 const LIB_ID& libId = sym->GetLibId();
141 BOOST_CHECK( libId.IsValid() );
142 BOOST_CHECK_MESSAGE( libSymbolNames.count( libId.GetUniStringLibItemName() ),
143 "Library id '" << libId.Format().wx_str()
144 << "' does not resolve in mounting_holes.SchLib" );
145 }
146}
147
148
149// https://gitlab.com/kicad/code/kicad/-/issues/24861
150BOOST_AUTO_TEST_CASE( Issue24861_RepeatedSchematicChannels )
151{
152 SCH_IO_ALTIUM plugin;
153
154 std::map<std::string, UTF8> properties;
155 properties.emplace( "project_file", UTF8( issue24861DataFile( wxT( "Repeated_Schematic.PrjPcb" ) ) ) );
156 properties.emplace( "sch0", UTF8( issue24861DataFile( wxT( "Repeated_Schematic.SchDoc" ) ) ) );
157 properties.emplace( "sch1", UTF8( issue24861DataFile( wxT( "Channel.SchDoc" ) ) ) );
158
159 SCH_SHEET* rootSheet = plugin.LoadSchematicFile( wxEmptyString, &m_schematic, nullptr, &properties );
160 BOOST_REQUIRE( rootSheet );
161
162 const std::vector<SCH_SHEET*> topLevelSheets = m_schematic.GetTopLevelSheets();
163 BOOST_REQUIRE_EQUAL( topLevelSheets.size(), 1 );
164 BOOST_CHECK_EQUAL( topLevelSheets.front()->GetName(), wxT( "Repeated_Schematic" ) );
165
166 std::optional<SCH_SHEET_PATH> topLevelPath;
167 std::map<wxString, SCH_SHEET_PATH> channelPaths;
168
169 for( const SCH_SHEET_PATH& sheetPath : m_schematic.Hierarchy() )
170 {
171 SCH_SHEET* sheet = sheetPath.Last();
172
173 if( sheet && sheet->GetName() == wxT( "Repeated_Schematic" ) )
174 topLevelPath = sheetPath;
175 else if( sheet && sheet->GetName().StartsWith( wxT( "CH" ) ) )
176 channelPaths.emplace( sheet->GetName(), sheetPath );
177 }
178
179 BOOST_REQUIRE( topLevelPath );
180 BOOST_CHECK_EQUAL( topLevelPath->GetPageNumber(), wxT( "1" ) );
181
182 BOOST_REQUIRE_EQUAL( channelPaths.size(), 3 );
183 BOOST_CHECK_EQUAL( channelPaths.at( wxT( "CH1" ) ).GetPageNumber(), wxT( "2" ) );
184 BOOST_CHECK_EQUAL( channelPaths.at( wxT( "CH2" ) ).GetPageNumber(), wxT( "3" ) );
185 BOOST_CHECK_EQUAL( channelPaths.at( wxT( "CH3" ) ).GetPageNumber(), wxT( "4" ) );
186
187 std::set<wxString> ledReferences;
188 std::set<wxString> resistorReferences;
189
190 for( const auto& [channelName, sheetPath] : channelPaths )
191 {
192 for( SCH_ITEM* item : sheetPath.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
193 {
194 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
195 wxString ref = symbol->GetRef( &sheetPath );
196
197 if( ref.StartsWith( wxT( "LED" ) ) )
198 ledReferences.insert( ref );
199 else if( ref.StartsWith( wxT( "R" ) ) )
200 resistorReferences.insert( ref );
201 }
202 }
203
204 BOOST_CHECK( ledReferences == std::set<wxString>( { wxT( "LED1_CH1" ), wxT( "LED1_CH2" ),
205 wxT( "LED1_CH3" ) } ) );
206 BOOST_CHECK( resistorReferences == std::set<wxString>( { wxT( "R1_CH1" ), wxT( "R1_CH2" ),
207 wxT( "R1_CH3" ) } ) );
208}
209
210
211// Support ticket #1303: an OrCad-derived Altium sheet symbol references several source files
212// through a single semicolon-separated filename ("pagea.SchDoc;pageb.SchDoc"), the two pages of one
213// multi-page block. The pages cross-reference each other (and one references itself). Every page
214// must be merged into the one sub-sheet screen, and the cyclic cross-references must not create a
215// recursive hierarchy that trips SCH_SHEET_LIST::BuildSheetList.
216BOOST_AUTO_TEST_CASE( Ticket1303_MultiPageBlock )
217{
218 SCH_IO_ALTIUM plugin;
219
220 SCH_SHEET* rootSheet = plugin.LoadSchematicFile( ticket1303DataFile( wxT( "overview.SchDoc" ) ),
221 &m_schematic, nullptr, nullptr );
222 BOOST_REQUIRE( rootSheet );
223 BOOST_REQUIRE( rootSheet->GetScreen() );
224
225 // Walking the hierarchy must not trip the recursion guard in SCH_SHEET_LIST::BuildSheetList.
226 // Before the fix this raised a wxASSERT (the pages resolved to sub-sheets whose remapped
227 // filenames collided in the ancestry).
228 SCH_SHEET_LIST hierarchy = m_schematic.Hierarchy();
229
230 // Both pages of the block land in a single merged sub-sheet screen; a regression drops one (or
231 // both), leaving the sheet empty. The screen also holds no residual sub-sheet symbols, since the
232 // cross-page references were pruned rather than descended into.
233 bool foundMergedScreen = false;
234
235 for( const SCH_SHEET_PATH& sheetPath : hierarchy )
236 {
237 SCH_SCREEN* screen = sheetPath.LastScreen();
238
239 if( !screen || screen == rootSheet->GetScreen() )
240 continue;
241
242 std::optional<VECTOR2I> posA;
243 std::optional<VECTOR2I> posB;
244
245 for( SCH_ITEM* item : screen->Items().OfType( SCH_LABEL_T ) )
246 {
247 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
248
249 if( label->GetText() == wxT( "ONLY_A" ) )
250 posA = label->GetPosition();
251 else if( label->GetText() == wxT( "ONLY_B" ) )
252 posB = label->GetPosition();
253 }
254
255 if( !posA || !posB )
256 continue;
257
258 foundMergedScreen = true;
259
260 // The cross-page references were pruned, not descended into: the merged screen holds no
261 // residual sub-sheet symbols.
262 int subSheetCount = 0;
263
264 for( SCH_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
265 {
266 (void) item;
267 subSheetCount++;
268 }
269
270 BOOST_CHECK_EQUAL( subSheetCount, 0 );
271
272 // Page B is tiled below page A rather than superimposed, even though both labels sit at the
273 // same coordinates in their source files.
274 BOOST_CHECK_MESSAGE( *posA != *posB, "Merged pages must be tiled, not overlapping" );
275
276 // The pruned cross-reference sheet's pin was converted to a hierarchical label so the wire
277 // that terminated on it still connects by name.
278 bool foundSigLabel = false;
279
280 for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
281 {
282 if( static_cast<SCH_HIERLABEL*>( item )->GetText() == wxT( "SIG" ) )
283 foundSigLabel = true;
284 }
285
286 BOOST_CHECK_MESSAGE( foundSigLabel,
287 "Pruned sheet pin must survive as a hierarchical label" );
288 }
289
290 BOOST_CHECK_MESSAGE( foundMergedScreen,
291 "Both pages of the multi-file sheet symbol must load into one screen" );
292}
293
294
295// https://gitlab.com/kicad/code/kicad/-/issues/24843
296// Altium encodes symbol rotation as quarter turns that map one-for-one onto KiCad's
297// SYM_ORIENT_* angles. The importer must store the same angle Altium shows so a later
298// "Update Symbols from Library" against a canonical upright symbol does not rotate the
299// placement. The reference symbols below are non-mirrored so the stored angle reads back
300// directly, without GetOrientation()'s mirror normalization.
301BOOST_AUTO_TEST_CASE( Issue24843_SymbolOrientationMatchesAltium )
302{
303 SCH_IO_ALTIUM plugin;
304
305 // The source embeds images by absolute Windows paths that cannot resolve here
307
308 SCH_SHEET* rootSheet = plugin.LoadSchematicFile( eDPAdapterDataFile( "power.SchDoc" ),
309 &m_schematic );
310 BOOST_REQUIRE( rootSheet );
311
312 m_schematic.RefreshHierarchy();
313
314 // Reference designator -> Altium orientation angle read straight from the source records.
315 const std::map<wxString, SYMBOL_ORIENTATION_PROP> expected = {
316 { wxT( "L10P" ), SYMBOL_ORIENTATION_PROP::SYMBOL_ANGLE_0 }, // Altium ORIENTATION 0
317 { wxT( "C10P" ), SYMBOL_ORIENTATION_PROP::SYMBOL_ANGLE_90 }, // Altium ORIENTATION 1
318 { wxT( "R10P" ), SYMBOL_ORIENTATION_PROP::SYMBOL_ANGLE_180 }, // Altium ORIENTATION 2
319 { wxT( "C13P" ), SYMBOL_ORIENTATION_PROP::SYMBOL_ANGLE_270 }, // Altium ORIENTATION 3
320 };
321
322 std::map<wxString, SYMBOL_ORIENTATION_PROP> actual;
323
324 for( const SCH_SHEET_PATH& sheetPath : m_schematic.Hierarchy() )
325 {
326 for( SCH_ITEM* item : sheetPath.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
327 {
328 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
329 wxString ref = symbol->GetRef( &sheetPath );
330
331 if( expected.count( ref ) )
332 actual[ref] = symbol->GetOrientationProp();
333 }
334 }
335
336 for( const auto& [ref, angle] : expected )
337 {
338 BOOST_REQUIRE_MESSAGE( actual.count( ref ), "Symbol '" << ref << "' not found on import" );
339 BOOST_CHECK_MESSAGE( actual.at( ref ) == angle,
340 "Symbol '" << ref << "' imported with orientation " << actual.at( ref )
341 << ", expected " << angle );
342 }
343}
344
345
346// https://gitlab.com/kicad/code/kicad/-/issues/16903
347// Every connectable coordinate in this design sits on a 50 mil grid in Altium's own frame, so the
348// importer's Y flip must preserve that grid phase rather than shift the sheet off grid.
349BOOST_AUTO_TEST_CASE( Issue16903_ImportKeepsGeometryOnGrid )
350{
351 SCH_IO_ALTIUM plugin;
352
353 SCH_SHEET* rootSheet =
354 plugin.LoadSchematicFile( issue16903DataFile( wxT( "hierarchical_schematic_top.SchDoc" ) ),
355 &m_schematic );
356 BOOST_REQUIRE( rootSheet );
357
358 m_schematic.RefreshHierarchy();
359
360 ERC_TESTER tester( &m_schematic );
362}
363
364
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
int TestOffGridEndpoints()
Test pins and wire ends for being off grid.
Definition erc.cpp:2497
virtual void SetReporter(REPORTER *aReporter)
Set an optional reporter for warnings/errors.
Definition io_base.h:89
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
UTF8 Format() const
Definition lib_id.cpp:132
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:108
const wxString GetUniStringLibNickname() const
Definition lib_id.h:84
static REPORTER & GetInstance()
Definition reporter.cpp:207
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,...
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.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:165
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:118
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
wxString GetName() const
Definition sch_sheet.h:142
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
Schematic symbol object.
Definition sch_symbol.h:75
SYMBOL_ORIENTATION_PROP GetOrientationProp() const
Definition sch_symbol.h:306
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:164
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
VECTOR2I GetPosition() const override
Definition sch_text.h:143
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition utf8.h:67
wxString wx_str() const
Definition utf8.cpp:41
std::string GetTestDataRootDir()
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
@ SYMBOL_ANGLE_180
Definition symbol.h:50
@ SYMBOL_ANGLE_0
Definition symbol.h:48
@ SYMBOL_ANGLE_90
Definition symbol.h:49
@ SYMBOL_ANGLE_270
Definition symbol.h:51
BOOST_AUTO_TEST_CASE(Issue22943_SourceLibrarySymbolLibId)
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
VECTOR3I expected(15, 30, 45)
int actual
BOOST_CHECK_EQUAL(result, "25.4")
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165