KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_job_export_pcb_3d.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, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <base_units.h>
24#include <jobs/jobset.h>
25#include <kiid.h>
26#include <richio.h>
27
28#include <filesystem>
29#include <system_error>
30#include <utility>
31#include <vector>
32
33namespace fs = std::filesystem;
34
35
44{
45public:
47 {
48 m_tempDir = fs::temp_directory_path()
49 / ( "kicad_job_export_pcb_3d_test_" + KIID().AsString().ToStdString() );
50 BOOST_REQUIRE( fs::create_directories( m_tempDir ) );
51
52 fs::path source = fs::path( KI_TEST::GetTestDataRootDir() ) / "pcbnew" / "issue25107"
53 / "issue25107.kicad_jobset";
54
55 m_jobsetPath = m_tempDir / "issue25107.kicad_jobset";
56 fs::copy_file( source, m_jobsetPath );
57 }
58
60 {
61 std::error_code ec;
62 fs::remove_all( m_tempDir, ec );
63 }
64
67 {
68 BOOST_REQUIRE( aJobset.LoadFromFile() );
69 BOOST_REQUIRE_EQUAL( aJobset.GetJobs().size(), 2 );
70
71 JOB_EXPORT_PCB_3D* job = dynamic_cast<JOB_EXPORT_PCB_3D*>( aJobset.GetJobs()[1].m_job.get() );
72 BOOST_REQUIRE( job );
73
74 return job;
75 }
76
77 wxString jobsetPath() const { return wxString( m_jobsetPath.string() ); }
78
79 wxString jobsetContents() const { return SafeReadFile( jobsetPath(), wxS( "rt" ) ); }
80
81 fs::path m_tempDir;
82 fs::path m_jobsetPath;
83};
84
85
88static std::vector<std::pair<const char*, bool*>> boolSettings( JOB_EXPORT_PCB_3D& aJob )
89{
91
92 return { { "overwrite", &p.m_Overwrite },
93 { "use_grid_origin", &p.m_UseGridOrigin },
94 { "use_drill_origin", &p.m_UseDrillOrigin },
95 { "use_defined_origin", &p.m_UseDefinedOrigin },
96 { "use_pcb_center_origin", &p.m_UsePcbCenterOrigin },
97 { "board_only", &p.m_BoardOnly },
98 { "include_unspecified", &p.m_IncludeUnspecified },
99 { "include_dnp", &p.m_IncludeDNP },
100 { "subst_models", &p.m_SubstModels },
101 { "optimize_step", &p.m_OptimizeStep },
102 { "cut_vias_in_body", &p.m_CutViasInBody },
103 { "export_board_body", &p.m_ExportBoardBody },
104 { "export_components", &p.m_ExportComponents },
105 { "export_tracks", &p.m_ExportTracksVias },
106 { "export_pads", &p.m_ExportPads },
107 { "export_zones", &p.m_ExportZones },
108 { "export_inner_copper", &p.m_ExportInnerCopper },
109 { "export_silkscreen", &p.m_ExportSilkscreen },
110 { "export_soldermask", &p.m_ExportSoldermask },
111 { "fuse_shapes", &p.m_FuseShapes },
112 { "fill_all_vias", &p.m_FillAllVias },
113 { "extra_pad_thickness", &p.m_ExtraPadThickness },
114 { "has_user_origin", &aJob.m_hasUserOrigin },
115 { "vrml_relative_paths", &aJob.m_vrmlRelativePaths } };
116}
117
118
119BOOST_FIXTURE_TEST_SUITE( JobExportPcb3d, JOB_EXPORT_PCB_3D_TEST_FIXTURE )
120
121
122
126BOOST_AUTO_TEST_CASE( FiltersSurviveDiskRoundTrip )
127{
128 {
129 JOBSET jobset( jobsetPath() );
130 JOB_EXPORT_PCB_3D* job = loadFilteredJob( jobset );
131
132 job->m_3dparams.m_ComponentFilter = wxS( "R1,R2,R3" );
133 job->m_3dparams.m_NetFilter = wxS( "GND,+3V3" );
134
135 BOOST_REQUIRE( jobset.SaveToFile( wxEmptyString, true ) );
136 }
137
138 wxString contents = jobsetContents();
139
140 BOOST_CHECK( contents.Contains( wxS( "component_filter" ) ) );
141 BOOST_CHECK( contents.Contains( wxS( "net_filter" ) ) );
142
143 JOBSET reloaded( jobsetPath() );
144 JOB_EXPORT_PCB_3D* job = loadFilteredJob( reloaded );
145
146 BOOST_CHECK_EQUAL( job->m_3dparams.m_ComponentFilter, wxS( "R1,R2,R3" ) );
147 BOOST_CHECK_EQUAL( job->m_3dparams.m_NetFilter, wxS( "GND,+3V3" ) );
148}
149
150
151BOOST_AUTO_TEST_CASE( BooleanSettingsSurviveDiskRoundTrip )
152{
153 std::vector<bool> expected;
154
155 {
156 JOBSET jobset( jobsetPath() );
157 JOB_EXPORT_PCB_3D* job = loadFilteredJob( jobset );
158
159 // Invert each flag so that a dropped mapping cannot pass by landing back on its default
160 for( const auto& [key, flag] : boolSettings( *job ) )
161 {
162 *flag = !*flag;
163 expected.push_back( *flag );
164 }
165
166 BOOST_REQUIRE( jobset.SaveToFile( wxEmptyString, true ) );
167 }
168
169 JOBSET reloaded( jobsetPath() );
170 JOB_EXPORT_PCB_3D* job = loadFilteredJob( reloaded );
171 std::vector<std::pair<const char*, bool*>> settings = boolSettings( *job );
172
173 for( size_t ii = 0; ii < settings.size(); ++ii )
174 {
175 BOOST_TEST_CONTEXT( settings[ii].first )
176 {
177 BOOST_CHECK_EQUAL( *settings[ii].second, expected[ii] );
178 }
179 }
180}
181
182
184BOOST_AUTO_TEST_CASE( ValueSettingsSurviveDiskRoundTrip )
185{
186 {
187 JOBSET jobset( jobsetPath() );
188 JOB_EXPORT_PCB_3D* job = loadFilteredJob( jobset );
189
191 job->m_3dparams.m_Origin = VECTOR2D( pcbIUScale.mmToIU( 12.5 ), pcbIUScale.mmToIU( -7.25 ) );
193 job->m_variant = wxS( "assembly" );
195 job->m_vrmlModelDir = wxS( "shapes3D" );
196 job->SetConfiguredOutputPath( wxS( "out/model.glb" ) );
197
198 BOOST_REQUIRE( jobset.SaveToFile( wxEmptyString, true ) );
199 }
200
201 JOBSET reloaded( jobsetPath() );
202 JOB_EXPORT_PCB_3D* job = loadFilteredJob( reloaded );
203
204 BOOST_CHECK( job->m_format == JOB_EXPORT_PCB_3D::FORMAT::GLB );
206 // Held in internal units, but the jobset has always stored millimetres
207 BOOST_CHECK_EQUAL( job->m_3dparams.m_Origin.x, pcbIUScale.mmToIU( 12.5 ) );
208 BOOST_CHECK_EQUAL( job->m_3dparams.m_Origin.y, pcbIUScale.mmToIU( -7.25 ) );
209 BOOST_CHECK( jobsetContents().Contains( wxS( "\"user_origin.x\": 12.5" ) ) );
211 BOOST_CHECK_EQUAL( job->m_variant, wxS( "assembly" ) );
213 BOOST_CHECK_EQUAL( job->m_vrmlModelDir, wxS( "shapes3D" ) );
214 BOOST_CHECK_EQUAL( job->GetConfiguredOutputPath(), wxS( "out/model.glb" ) );
215}
216
217
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
std::vector< JOBSET_JOB > & GetJobs()
Definition jobset.h:154
The 3D model export job settings must survive a real save/load cycle through a .kicad_jobset on disk.
JOB_EXPORT_PCB_3D * loadFilteredJob(JOBSET &aJobset)
The reporter's second job, "3D model export (resistors only)".
JOB_EXPORT_PCB_3D::FORMAT m_format
void SetStepFormat(EXPORTER_STEP_PARAMS::FORMAT aFormat)
EXPORTER_STEP_PARAMS m_3dparams
Despite the name; also used for other formats.
void SetConfiguredOutputPath(const wxString &aPath)
Sets the configured output path for the job, this path is always saved to file.
Definition job.cpp:157
wxString GetConfiguredOutputPath() const
Returns the configured output path for the job.
Definition job.h:235
virtual bool LoadFromFile(const wxString &aDirectory="")
Loads the backing file from disk and then calls Load()
Definition kiid.h:46
wxString AsString() const
Definition kiid.cpp:264
std::string GetTestDataRootDir()
wxString SafeReadFile(const wxString &aFilePath, const wxString &aReadType)
Nominally opens a file and reads it into a string.
Definition richio.cpp:51
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
static std::vector< std::pair< const char *, bool * > > boolSettings(JOB_EXPORT_PCB_3D &aJob)
Every boolean setting of the job, so that adding a field without a JSON mapping fails the round trip ...
BOOST_AUTO_TEST_CASE(FiltersSurviveDiskRoundTrip)
The component filter and the net filter are the two fields called out in the issue.
VECTOR3I expected(15, 30, 45)
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< double > VECTOR2D
Definition vector2d.h:682