KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pads_unit_converter.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
28
30
32
33#include <ostream>
34
35std::ostream& operator<<( std::ostream& os, PADS_UNIT_TYPE aType )
36{
37 switch( aType )
38 {
39 case PADS_UNIT_TYPE::MILS: os << "MILS"; break;
40 case PADS_UNIT_TYPE::METRIC: os << "METRIC"; break;
41 case PADS_UNIT_TYPE::INCHES: os << "INCHES"; break;
42 }
43
44 return os;
45}
46
51
52BOOST_FIXTURE_TEST_SUITE( PadsUnitConverter, PADS_UNIT_CONVERTER_FIXTURE )
53
54
55BOOST_AUTO_TEST_CASE( DefaultsToMils )
56{
57 PADS_UNIT_CONVERTER converter;
59}
60
61
62BOOST_AUTO_TEST_CASE( MilsConversion )
63{
64 PADS_UNIT_CONVERTER converter;
66
67 // 1000 mils = 1 inch = 25.4 mm = 25,400,000 nm
68 BOOST_CHECK_EQUAL( converter.ToNanometers( 1000.0 ), 25400000 );
69
70 // 1 mil = 25,400 nm
71 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 25400 );
72
73 // 100 mils = 2,540,000 nm
74 BOOST_CHECK_EQUAL( converter.ToNanometers( 100.0 ), 2540000 );
75
76 // Test size conversion (same as coordinate for now)
77 BOOST_CHECK_EQUAL( converter.ToNanometersSize( 1000.0 ), 25400000 );
78}
79
80
81BOOST_AUTO_TEST_CASE( MetricConversion )
82{
83 PADS_UNIT_CONVERTER converter;
85
86 // 1 mm = 1,000,000 nm
87 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 1000000 );
88
89 // 25.4 mm = 25,400,000 nm (1 inch)
90 BOOST_CHECK_EQUAL( converter.ToNanometers( 25.4 ), 25400000 );
91
92 // 0.1 mm = 100,000 nm
93 BOOST_CHECK_EQUAL( converter.ToNanometers( 0.1 ), 100000 );
94
95 // Test size conversion
96 BOOST_CHECK_EQUAL( converter.ToNanometersSize( 1.0 ), 1000000 );
97}
98
99
100BOOST_AUTO_TEST_CASE( InchesConversion )
101{
102 PADS_UNIT_CONVERTER converter;
104
105 // 1 inch = 25,400,000 nm
106 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 25400000 );
107
108 // 0.001 inch (1 mil) = 25,400 nm
109 BOOST_CHECK_EQUAL( converter.ToNanometers( 0.001 ), 25400 );
110
111 // 0.1 inch = 2,540,000 nm
112 BOOST_CHECK_EQUAL( converter.ToNanometers( 0.1 ), 2540000 );
113
114 // Test size conversion
115 BOOST_CHECK_EQUAL( converter.ToNanometersSize( 1.0 ), 25400000 );
116}
117
118
132
133
134BOOST_AUTO_TEST_CASE( ZeroConversion )
135{
136 PADS_UNIT_CONVERTER converter;
137
139 BOOST_CHECK_EQUAL( converter.ToNanometers( 0.0 ), 0 );
140
142 BOOST_CHECK_EQUAL( converter.ToNanometers( 0.0 ), 0 );
143
145 BOOST_CHECK_EQUAL( converter.ToNanometers( 0.0 ), 0 );
146}
147
148
149BOOST_AUTO_TEST_CASE( NegativeConversion )
150{
151 PADS_UNIT_CONVERTER converter;
153
154 // Negative values should convert correctly
155 BOOST_CHECK_EQUAL( converter.ToNanometers( -1000.0 ), -25400000 );
156 BOOST_CHECK_EQUAL( converter.ToNanometersSize( -100.0 ), -2540000 );
157}
158
159
160BOOST_AUTO_TEST_CASE( ConstantsAreCorrect )
161{
162 // Verify the conversion constants are accurate
166 BOOST_CHECK_CLOSE( PADS_UNIT_CONVERTER::BASIC_TO_NM,
167 PADS_UNIT_CONVERTER::MILS_TO_NM / 38100.0, 1e-10 );
168
169 // Cross-check: 1000 mils should equal 1 inch
172
173 // Cross-check: 25.4 mm should equal 1 inch
176
177 // Cross-check: 38100 BASIC units should equal 1 mil
178 BOOST_CHECK_CLOSE( 38100.0 * PADS_UNIT_CONVERTER::BASIC_TO_NM,
180}
181
182
183BOOST_AUTO_TEST_CASE( BasicUnitsMode )
184{
185 PADS_UNIT_CONVERTER converter;
186
187 // Default should not be in BASIC mode
188 BOOST_CHECK( !converter.IsBasicUnitsMode() );
189
190 // Enable BASIC mode
191 converter.SetBasicUnitsMode( true );
192 BOOST_CHECK( converter.IsBasicUnitsMode() );
193
194 // 38100 BASIC units = 1 mil = 25,400 nm
195 BOOST_CHECK_EQUAL( converter.ToNanometers( 38100.0 ), 25400 );
196
197 // 1 BASIC unit rounds to 1 nm
198 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 1 );
199
200 // 3 BASIC units = 2 nm (3 * 2/3 = 2)
201 BOOST_CHECK_EQUAL( converter.ToNanometers( 3.0 ), 2 );
202
203 // Disable BASIC mode should revert to base units
204 converter.SetBasicUnitsMode( false );
205 BOOST_CHECK( !converter.IsBasicUnitsMode() );
206 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 25400 ); // Back to MILS
207}
208
209
210BOOST_AUTO_TEST_CASE( BasicUnitsCustomScale )
211{
212 PADS_UNIT_CONVERTER converter;
213 converter.SetBasicUnitsMode( true );
214
215 // Default scale
217
218 // Set custom scale (e.g., 5.0 nm per unit)
219 converter.SetBasicUnitsScale( 5.0 );
220 BOOST_CHECK_EQUAL( converter.GetBasicUnitsScale(), 5.0 );
221
222 // 1000 units at 5.0 nm/unit = 5000 nm
223 BOOST_CHECK_EQUAL( converter.ToNanometers( 1000.0 ), 5000 );
224}
225
226
227BOOST_AUTO_TEST_CASE( ParseFileHeaderBasic )
228{
229 PADS_UNIT_CONVERTER converter;
230
231 // Test BASIC header detection
232 BOOST_CHECK( converter.ParseFileHeader( "!PADS-POWERPCB-V9.0-BASIC!" ) );
233 BOOST_CHECK( converter.IsBasicUnitsMode() );
234
235 // Reset
236 converter.SetBasicUnitsMode( false );
237
238 // Test lowercase basic
239 BOOST_CHECK( converter.ParseFileHeader( "!PADS-POWERPCB-V9.0-basic!" ) );
240 BOOST_CHECK( converter.IsBasicUnitsMode() );
241}
242
243
244BOOST_AUTO_TEST_CASE( ParseFileHeaderMils )
245{
246 PADS_UNIT_CONVERTER converter;
247
248 // Test MILS header detection
249 BOOST_CHECK( converter.ParseFileHeader( "!PADS-POWERPCB-V9.5-MILS!" ) );
250 BOOST_CHECK( !converter.IsBasicUnitsMode() );
252}
253
254
255BOOST_AUTO_TEST_CASE( ParseFileHeaderMetric )
256{
257 PADS_UNIT_CONVERTER converter;
258
259 // Test METRIC header detection
260 BOOST_CHECK( converter.ParseFileHeader( "!PADS-POWERPCB-V9.5-METRIC!" ) );
261 BOOST_CHECK( !converter.IsBasicUnitsMode() );
263}
264
265
266BOOST_AUTO_TEST_CASE( ParseFileHeaderInches )
267{
268 PADS_UNIT_CONVERTER converter;
269
270 // Test INCHES header detection
271 BOOST_CHECK( converter.ParseFileHeader( "!PADS-POWERPCB-V9.5-INCHES!" ) );
272 BOOST_CHECK( !converter.IsBasicUnitsMode() );
274}
275
276
277BOOST_AUTO_TEST_CASE( ParseFileHeaderUnknown )
278{
279 PADS_UNIT_CONVERTER converter;
280
281 // Unknown header should return false
282 BOOST_CHECK( !converter.ParseFileHeader( "!UNKNOWN-FORMAT!" ) );
283}
284
285
286BOOST_AUTO_TEST_CASE( BasicModeOverridesBaseUnits )
287{
288 PADS_UNIT_CONVERTER converter;
289
290 // Set to METRIC
292 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 1000000 ); // 1 mm
293
294 // Enable BASIC - should override METRIC
295 converter.SetBasicUnitsMode( true );
296 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 1 );
297
298 // Disable BASIC - should revert to METRIC
299 converter.SetBasicUnitsMode( false );
300 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 1000000 ); // Back to mm
301}
302
303
304BOOST_AUTO_TEST_CASE( ParseUnitCodeMils )
305{
306 // Test "M" -> MILS
308 BOOST_CHECK( result.has_value() );
310
311 // Test lowercase
313 BOOST_CHECK( result.has_value() );
315
316 // Test "D" (default) -> MILS
318 BOOST_CHECK( result.has_value() );
320
321 // Test long form
323 BOOST_CHECK( result.has_value() );
325}
326
327
328BOOST_AUTO_TEST_CASE( ParseUnitCodeMetric )
329{
330 // Test "MM" -> METRIC
332 BOOST_CHECK( result.has_value() );
334
335 // Test lowercase
337 BOOST_CHECK( result.has_value() );
339
340 // Test long form
342 BOOST_CHECK( result.has_value() );
344}
345
346
347BOOST_AUTO_TEST_CASE( ParseUnitCodeInches )
348{
349 // Test "I" -> INCHES
351 BOOST_CHECK( result.has_value() );
353
354 // Test lowercase
356 BOOST_CHECK( result.has_value() );
358
359 // Test long form
361 BOOST_CHECK( result.has_value() );
363}
364
365
366BOOST_AUTO_TEST_CASE( ParseUnitCodeNoOverride )
367{
368 // Test "N" -> no override (empty optional)
370 BOOST_CHECK( !result.has_value() );
371
372 // Test empty string
374 BOOST_CHECK( !result.has_value() );
375
376 // Test invalid code
378 BOOST_CHECK( !result.has_value() );
379
381 BOOST_CHECK( !result.has_value() );
382}
383
384
385BOOST_AUTO_TEST_CASE( PushPopUnitOverride )
386{
387 PADS_UNIT_CONVERTER converter;
388
389 // Start with MILS
391 BOOST_CHECK( !converter.HasUnitOverride() );
392 BOOST_CHECK_EQUAL( converter.GetOverrideDepth(), 0 );
393 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 25400 ); // 1 mil
394
395 // Push METRIC override
396 BOOST_CHECK( converter.PushUnitOverride( "MM" ) );
397 BOOST_CHECK( converter.HasUnitOverride() );
398 BOOST_CHECK_EQUAL( converter.GetOverrideDepth(), 1 );
399 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 1000000 ); // 1 mm
400
401 // Push INCHES override (nested)
402 BOOST_CHECK( converter.PushUnitOverride( "I" ) );
403 BOOST_CHECK_EQUAL( converter.GetOverrideDepth(), 2 );
404 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 25400000 ); // 1 inch
405
406 // Pop back to METRIC
407 converter.PopUnitOverride();
408 BOOST_CHECK_EQUAL( converter.GetOverrideDepth(), 1 );
409 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 1000000 ); // 1 mm
410
411 // Pop back to base MILS
412 converter.PopUnitOverride();
413 BOOST_CHECK( !converter.HasUnitOverride() );
414 BOOST_CHECK_EQUAL( converter.GetOverrideDepth(), 0 );
415 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 25400 ); // 1 mil
416}
417
418
419BOOST_AUTO_TEST_CASE( PushInvalidUnitCode )
420{
421 PADS_UNIT_CONVERTER converter;
422
423 // Invalid code should return false and not push
424 BOOST_CHECK( !converter.PushUnitOverride( "X" ) );
425 BOOST_CHECK( !converter.HasUnitOverride() );
426
427 // "N" (no override) should return false and not push
428 BOOST_CHECK( !converter.PushUnitOverride( "N" ) );
429 BOOST_CHECK( !converter.HasUnitOverride() );
430
431 // Empty string should return false and not push
432 BOOST_CHECK( !converter.PushUnitOverride( "" ) );
433 BOOST_CHECK( !converter.HasUnitOverride() );
434}
435
436
437BOOST_AUTO_TEST_CASE( PopEmptyStack )
438{
439 PADS_UNIT_CONVERTER converter;
440
441 // Popping empty stack should be safe (no-op)
442 BOOST_CHECK( !converter.HasUnitOverride() );
443 converter.PopUnitOverride();
444 BOOST_CHECK( !converter.HasUnitOverride() );
445
446 // Conversion should still work
447 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 25400 ); // Default MILS
448}
449
450
451BOOST_AUTO_TEST_CASE( OverrideDoesNotAffectBasicMode )
452{
453 PADS_UNIT_CONVERTER converter;
454
455 // Enable BASIC mode
456 converter.SetBasicUnitsMode( true );
457 BOOST_CHECK_EQUAL( converter.ToNanometers( 38100.0 ), 25400 ); // BASIC
458
459 // Push override - should have no effect in BASIC mode
460 BOOST_CHECK( converter.PushUnitOverride( "MM" ) );
461 BOOST_CHECK( converter.HasUnitOverride() );
462 BOOST_CHECK_EQUAL( converter.ToNanometers( 38100.0 ), 25400 ); // Still BASIC
463
464 // Disable BASIC - now override should take effect
465 converter.SetBasicUnitsMode( false );
466 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 1000000 ); // METRIC override
467
468 // Pop override
469 converter.PopUnitOverride();
470 BOOST_CHECK_EQUAL( converter.ToNanometers( 1.0 ), 25400 ); // Back to MILS
471}
472
473
Converts PADS file format units to KiCad internal units (nanometers).
static constexpr double MILS_TO_NM
double GetBasicUnitsScale() const
Get the current BASIC units scale.
static constexpr double INCHES_TO_NM
bool IsBasicUnitsMode() const
Check if BASIC units mode is enabled.
size_t GetOverrideDepth() const
Get the current override depth.
void SetBaseUnits(PADS_UNIT_TYPE aUnitType)
Set the base units for conversion.
bool PushUnitOverride(const std::string &aUnitCode)
Push a unit override onto the stack.
static constexpr double BASIC_TO_NM
void SetBasicUnitsMode(bool aEnabled)
Enable or disable BASIC units mode.
int64_t ToNanometersSize(double aValue) const
Convert a size value to nanometers.
bool ParseFileHeader(const std::string &aHeader)
Parse a PADS file header string and configure units accordingly.
static std::optional< PADS_UNIT_TYPE > ParseUnitCode(const std::string &aUnitCode)
Parse a PADS unit code and return the corresponding unit type.
PADS_UNIT_TYPE GetUnitType() const
Get the current unit type.
static constexpr double MM_TO_NM
void SetBasicUnitsScale(double aScale)
Set a custom scale for BASIC units.
void PopUnitOverride()
Pop the most recent unit override from the stack.
int64_t ToNanometers(double aValue) const
Convert a coordinate value to nanometers.
bool HasUnitOverride() const
Check if any unit overrides are currently active.
PADS_UNIT_TYPE
Unit types supported by PADS file formats.
@ MILS
Thousandths of an inch (1 mil = 0.001 inch)
@ METRIC
Millimeters.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE_END()
std::ostream & operator<<(std::ostream &os, PADS_UNIT_TYPE aType)
BOOST_AUTO_TEST_CASE(DefaultsToMils)
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")