KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_lib_table_diff_merge.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 3
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/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 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
26
31
32#include <board.h>
35
36#include <wx/file.h>
37#include <wx/filename.h>
38#include <wx/stdpaths.h>
39
40
41using namespace KICAD_DIFF;
42
43
44namespace
45{
46
47wxString uniqueSuffix()
48{
49 static int counter = 0;
50 return wxString::Format( wxS( "%d_%d" ),
51 static_cast<int>( wxGetUTCTimeMillis().GetValue() & 0xffffff ),
52 ++counter );
53}
54
55
56wxString writeProjectFile( BOARD& aBoard, const std::string& aFileName,
57 const wxString& aContent )
58{
59 wxFileName fn( aBoard.GetFileName() );
60 fn.SetFullName( wxString::FromUTF8( aFileName ) );
61
62 wxFile f;
63 BOOST_REQUIRE( f.Open( fn.GetFullPath(), wxFile::write ) );
64 BOOST_REQUIRE( f.Write( aContent ) );
65 f.Close();
66 return fn.GetFullPath();
67}
68
69
70const ITEM_CHANGE* findDocLevelChange( const DOCUMENT_DIFF& aDiff )
71{
72 for( const ITEM_CHANGE& c : aDiff.changes )
73 {
74 if( c.id.empty() )
75 return &c;
76 }
77
78 return nullptr;
79}
80
81
82const PROPERTY_DELTA* findProperty( const ITEM_CHANGE& aChange, const wxString& aName )
83{
84 for( const PROPERTY_DELTA& p : aChange.properties )
85 {
86 if( p.name == aName )
87 return &p;
88 }
89
90 return nullptr;
91}
92
93} // namespace
94
95
97{
99 {
100 KI_TEST::LoadBoard( m_settingsAnc, "complex_hierarchy", m_ancestor );
101 KI_TEST::LoadBoard( m_settingsOurs, "complex_hierarchy", m_ours );
102 KI_TEST::LoadBoard( m_settingsTheirs, "complex_hierarchy", m_theirs );
103
107
108 wxString tmp = wxStandardPaths::Get().GetTempDir();
109 m_ancDir = tmp + wxFILE_SEP_PATH + wxS( "kicad_libtbl_anc_" ) + uniqueSuffix();
110 m_oursDir = tmp + wxFILE_SEP_PATH + wxS( "kicad_libtbl_ours_" ) + uniqueSuffix();
111 m_theirsDir = tmp + wxFILE_SEP_PATH + wxS( "kicad_libtbl_theirs_" ) + uniqueSuffix();
112
113 for( const wxString& d : { m_ancDir, m_oursDir, m_theirsDir } )
114 BOOST_REQUIRE( wxFileName::Mkdir( d, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
115
116 m_ancestor->SetFileName( m_ancDir + wxFILE_SEP_PATH + wxS( "board.kicad_pcb" ) );
117 m_ours ->SetFileName( m_oursDir + wxFILE_SEP_PATH + wxS( "board.kicad_pcb" ) );
118 m_theirs ->SetFileName( m_theirsDir + wxFILE_SEP_PATH + wxS( "board.kicad_pcb" ) );
119 }
120
122 {
123 for( const wxString& d : { m_ancDir, m_oursDir, m_theirsDir } )
124 {
125 if( !d.IsEmpty() && wxFileName::DirExists( d ) )
126 wxFileName::Rmdir( d, wxPATH_RMDIR_RECURSIVE );
127 }
128 }
129
133 std::unique_ptr<BOARD> m_ancestor;
134 std::unique_ptr<BOARD> m_ours;
135 std::unique_ptr<BOARD> m_theirs;
136 wxString m_ancDir;
137 wxString m_oursDir;
138 wxString m_theirsDir;
139};
140
141
142BOOST_FIXTURE_TEST_SUITE( LibTableDiffMerge, LIB_TABLE_DIFF_MERGE_FIXTURE )
143
144
145BOOST_AUTO_TEST_CASE( NoSiblingFilesEmitsNoDelta )
146{
147 PCB_DIFFER differ( m_ancestor.get(), m_ours.get(), wxS( "complex_hierarchy.kicad_pcb" ) );
148 DOCUMENT_DIFF result = differ.Diff();
149
150 if( const ITEM_CHANGE* docChange = findDocLevelChange( result ) )
151 {
152 BOOST_CHECK( findProperty( *docChange, DOC_PROP_FP_LIB_TABLE ) == nullptr );
153 BOOST_CHECK( findProperty( *docChange, DOC_PROP_SYM_LIB_TABLE ) == nullptr );
154 }
155}
156
157
158BOOST_AUTO_TEST_CASE( OursAddsFpLibTableEmitsDelta )
159{
160 writeProjectFile( *m_ours, FILEEXT::FootprintLibraryTableFileName,
161 wxS( "(fp_lib_table\n(version 7)\n)\n" ) );
162
163 PCB_DIFFER differ( m_ancestor.get(), m_ours.get(), wxS( "complex_hierarchy.kicad_pcb" ) );
164 DOCUMENT_DIFF result = differ.Diff();
165
166 const ITEM_CHANGE* docChange = findDocLevelChange( result );
167 BOOST_REQUIRE( docChange );
168
171 BOOST_CHECK_EQUAL( delta->before.ToDisplayString(), "(no fp-lib-table)" );
172 BOOST_CHECK( delta->after.ToDisplayString() != "(no fp-lib-table)" );
173}
174
175
176BOOST_AUTO_TEST_CASE( OursAddsSymLibTableEmitsDelta )
177{
178 writeProjectFile( *m_ours, FILEEXT::SymbolLibraryTableFileName,
179 wxS( "(sym_lib_table\n(version 7)\n)\n" ) );
180
181 PCB_DIFFER differ( m_ancestor.get(), m_ours.get(), wxS( "complex_hierarchy.kicad_pcb" ) );
182 DOCUMENT_DIFF result = differ.Diff();
183
184 const ITEM_CHANGE* docChange = findDocLevelChange( result );
185 BOOST_REQUIRE( docChange );
186
189 BOOST_CHECK_EQUAL( delta->before.ToDisplayString(), "(no sym-lib-table)" );
190 BOOST_CHECK( delta->after.ToDisplayString() != "(no sym-lib-table)" );
191}
192
193
194BOOST_AUTO_TEST_CASE( DivergentFpLibTableEmitsDelta )
195{
196 writeProjectFile( *m_ancestor, FILEEXT::FootprintLibraryTableFileName,
197 wxS( "(fp_lib_table\n(version 7)\n(lib (name old))\n)\n" ) );
198 writeProjectFile( *m_ours, FILEEXT::FootprintLibraryTableFileName,
199 wxS( "(fp_lib_table\n(version 7)\n(lib (name new))\n)\n" ) );
200
201 PCB_DIFFER differ( m_ancestor.get(), m_ours.get(), wxS( "complex_hierarchy.kicad_pcb" ) );
202 DOCUMENT_DIFF result = differ.Diff();
203
204 const ITEM_CHANGE* docChange = findDocLevelChange( result );
205 BOOST_REQUIRE( docChange );
206
209 BOOST_CHECK( delta->before.ToDisplayString() != delta->after.ToDisplayString() );
210}
211
212
213BOOST_AUTO_TEST_CASE( ApplierStagesOursFpLibTable )
214{
215 const wxString oursFp =
216 wxS( "(fp_lib_table\n(version 7)\n(lib (name FromOurs))\n)\n" );
217 writeProjectFile( *m_ours, FILEEXT::FootprintLibraryTableFileName, oursFp );
218
219 PCB_DIFFER diffAO( m_ancestor.get(), m_ours.get(), wxS( "complex_hierarchy.kicad_pcb" ) );
220 PCB_DIFFER diffAT( m_ancestor.get(), m_theirs.get(), wxS( "complex_hierarchy.kicad_pcb" ) );
221 DOCUMENT_DIFF docAO = diffAO.Diff();
222 DOCUMENT_DIFF docAT = diffAT.Diff();
223
224 KICAD_MERGE_ENGINE engine;
225 MERGE_PLAN plan = engine.Plan( docAO, docAT );
226
227 PCB_MERGE_APPLIER applier( m_ancestor.get(), m_ours.get(), m_theirs.get(),
228 std::move( plan ) );
229 std::unique_ptr<BOARD> merged = applier.Apply();
230
231 BOOST_REQUIRE( merged );
232 BOOST_CHECK( applier.GetReport().projectFileTouched );
233 BOOST_CHECK( applier.GetReport().fpLibTableSet );
234 BOOST_CHECK_EQUAL( applier.GetReport().fpLibTable, oursFp );
235}
236
237
238BOOST_AUTO_TEST_CASE( ApplierStagesOursSymLibTable )
239{
240 const wxString oursSym =
241 wxS( "(sym_lib_table\n(version 7)\n(lib (name FromOurs))\n)\n" );
242 writeProjectFile( *m_ours, FILEEXT::SymbolLibraryTableFileName, oursSym );
243
244 PCB_DIFFER diffAO( m_ancestor.get(), m_ours.get(), wxS( "complex_hierarchy.kicad_pcb" ) );
245 PCB_DIFFER diffAT( m_ancestor.get(), m_theirs.get(), wxS( "complex_hierarchy.kicad_pcb" ) );
246 DOCUMENT_DIFF docAO = diffAO.Diff();
247 DOCUMENT_DIFF docAT = diffAT.Diff();
248
249 KICAD_MERGE_ENGINE engine;
250 MERGE_PLAN plan = engine.Plan( docAO, docAT );
251
252 PCB_MERGE_APPLIER applier( m_ancestor.get(), m_ours.get(), m_theirs.get(),
253 std::move( plan ) );
254 std::unique_ptr<BOARD> merged = applier.Apply();
255
256 BOOST_REQUIRE( merged );
257 BOOST_CHECK( applier.GetReport().projectFileTouched );
258 BOOST_CHECK( applier.GetReport().symLibTableSet );
259 BOOST_CHECK_EQUAL( applier.GetReport().symLibTable, oursSym );
260}
261
262
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:320
const wxString & GetFileName() const
Definition board.h:357
Three-way merge plan generator.
MERGE_PLAN Plan(const DOCUMENT_DIFF &aAncestorOurs, const DOCUMENT_DIFF &aAncestorTheirs) const
Plan the merge given the canonical pair of diffs.
Diff two already-parsed BOARDs and produce a DOCUMENT_DIFF.
Definition pcb_differ.h:52
DOCUMENT_DIFF Diff() override
Produce a DOCUMENT_DIFF of the inputs the concrete differ was constructed with.
Materialize a MERGE_PLAN into a real merged BOARD.
std::unique_ptr< BOARD > Apply()
Produce the merged board.
const REPORT & GetReport() const
static const std::string SymbolLibraryTableFileName
static const std::string FootprintLibraryTableFileName
const wxString DOC_PROP_SYM_LIB_TABLE
const wxString DOC_PROP_FP_LIB_TABLE
void LoadBoard(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< BOARD > &aBoard)
The full set of changes between two parsed documents of one type.
std::vector< ITEM_CHANGE > changes
One change record on a single item.
std::vector< PROPERTY_DELTA > properties
Result of planning a 3-way merge.
wxString fpLibTable
fp-lib-table content the applier resolved.
bool projectFileTouched
True iff the applier resolved state that lives in the .kicad_pro or a project sibling file.
wxString symLibTable
sym-lib-table content the applier resolved.
Single (name, before, after) triple for one mutated property on an item.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(NoSiblingFilesEmitsNoDelta)
static const ITEM_CHANGE * findDocLevelChange(const DOCUMENT_DIFF &aDiff)
Find the synthetic doc-level ITEM_CHANGE (empty KIID_PATH) in a diff result, returning nullptr if non...
static const PROPERTY_DELTA * findProperty(const ITEM_CHANGE &aChange, const wxString &aName)
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
int delta
Definition of file extensions used in Kicad.