KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_history_autosave.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 */
20
22
23#include <board.h>
24#include <local_history.h>
25#include <pgm_base.h>
26#include <project.h>
29
30#include <git2.h>
31
32#include <vector>
33
34#include <wx/dir.h>
35#include <wx/ffile.h>
36#include <wx/filefn.h>
37#include <wx/filename.h>
38#include <wx/stdpaths.h>
39
40
41namespace
42{
43// single_top handles libgit2 init in production; the QA harness does not, so tests driving
44// LOCAL_HISTORY must manage it themselves.
45struct LIBGIT2_SCOPE
46{
47 LIBGIT2_SCOPE() { git_libgit2_init(); }
48 ~LIBGIT2_SCOPE() { git_libgit2_shutdown(); }
49};
50
51
52struct SCOPED_BOOL_OVERRIDE
53{
54 explicit SCOPED_BOOL_OVERRIDE( bool& aFlag ) : m_flag( aFlag ), m_original( aFlag ) {}
55 ~SCOPED_BOOL_OVERRIDE() { m_flag = m_original; }
56
57 bool& m_flag;
58 bool m_original;
59};
60
61
62// Recursively remove the directory on destruction so test failures (which throw out of
63// BOOST_REQUIRE) do not leak temp directories.
64struct SCOPED_TEMP_DIR
65{
66 explicit SCOPED_TEMP_DIR( const wxString& aPrefix )
67 {
68 wxString base = wxStandardPaths::Get().GetTempDir();
69 m_path = base + wxFileName::GetPathSeparator() + aPrefix
70 + wxString::Format( wxS( "_%lu_%ld" ),
71 static_cast<unsigned long>( ::wxGetProcessId() ),
72 static_cast<long>( wxDateTime::UNow().GetTicks() ) );
73 wxFileName::Mkdir( m_path, 0777, wxPATH_MKDIR_FULL );
74 }
75
76 ~SCOPED_TEMP_DIR()
77 {
78 if( !m_path.IsEmpty() && wxDirExists( m_path ) )
79 wxFileName::Rmdir( m_path, wxPATH_RMDIR_RECURSIVE );
80 }
81
82 const wxString& Path() const { return m_path; }
83
84 wxString m_path;
85};
86} // namespace
87
88
89BOOST_AUTO_TEST_SUITE( PcbHistoryAutosave )
90
91
92
101BOOST_AUTO_TEST_CASE( SaveToHistoryWithNullProjectDoesNotCrash )
102{
103 BOARD board;
104 std::vector<HISTORY_FILE_DATA> fileData;
105
106 BOOST_REQUIRE( board.GetProject() == nullptr );
107
108 BOOST_CHECK_NO_THROW( board.SaveToHistory( wxS( "/tmp/anywhere" ), fileData ) );
109 BOOST_CHECK( fileData.empty() );
110}
111
112
117BOOST_AUTO_TEST_CASE( SaveToHistoryUnsavedBoardProducesNothing )
118{
120
121 wxString tempDir = wxStandardPaths::Get().GetTempDir();
122 wxString projectPath = tempDir + wxFileName::GetPathSeparator() + wxS( "pcb_autosave.kicad_pro" );
123
124 mgr.LoadProject( projectPath.ToStdString() );
125
126 BOARD board;
127 board.SetProject( &mgr.Prj() );
128
129 std::vector<HISTORY_FILE_DATA> fileData;
130
131 BOOST_REQUIRE( board.GetFileName().IsEmpty() );
132 BOOST_CHECK_NO_THROW( board.SaveToHistory( mgr.Prj().GetProjectPath(), fileData ) );
133 BOOST_CHECK( fileData.empty() );
134
135 // Detach project before BOARD destruction so design settings ownership unwinds cleanly
136 board.ClearProject();
137 mgr.UnloadProject( &mgr.Prj(), false );
138}
139
140
141// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/24016
142BOOST_AUTO_TEST_CASE( RestoreCommitPreservesZipBackupsDirectory )
143{
144 LIBGIT2_SCOPE libgit;
145
146 // LOCAL_HISTORY early-exits when backups are disabled.
147 bool& backupEnabled = Pgm().GetCommonSettings()->m_Backup.enabled;
148 SCOPED_BOOL_OVERRIDE restoreBackupFlag( backupEnabled );
149 backupEnabled = true;
150
151 SCOPED_TEMP_DIR tempProject( wxS( "kicad_qa_issue24016" ) );
152 const wxString& projectPath = tempProject.Path();
153
154 auto writeFile = []( const wxString& aPath, const wxString& aContents )
155 {
156 wxFFile f( aPath, wxT( "w" ) );
157 BOOST_REQUIRE( f.IsOpened() );
158 f.Write( aContents );
159 f.Close();
160 };
161
162 wxString boardPath =
163 projectPath + wxFileName::GetPathSeparator() + wxS( "issue24016.kicad_pcb" );
164 writeFile( boardPath, wxS( "(kicad_pcb (version 20240108))\n" ) );
165
166 LOCAL_HISTORY history;
167 BOOST_REQUIRE( history.Init( projectPath ) );
168
169 wxString headHash = history.GetHeadHash( projectPath );
170 BOOST_REQUIRE( !headHash.IsEmpty() );
171
172 // Mirror SETTINGS_MANAGER::BackupProject output: a sibling "<name>-backups" directory
173 // containing one or more .zip archives.
174 wxString backupsDir =
175 projectPath + wxFileName::GetPathSeparator() + wxS( "issue24016-backups" );
176 BOOST_REQUIRE( wxFileName::Mkdir( backupsDir, 0777, wxPATH_MKDIR_FULL ) );
177
178 wxString zipPath = backupsDir + wxFileName::GetPathSeparator()
179 + wxS( "issue24016-2026-04-22_120000.zip" );
180 writeFile( zipPath, wxS( "pretend-zip-contents" ) );
181
182 writeFile( boardPath, wxS( "(kicad_pcb (version 20240108) (dirty yes))\n" ) );
183
184 BOOST_REQUIRE( history.RestoreCommit( projectPath, headHash, nullptr ) );
185
186 BOOST_CHECK_MESSAGE( wxDirExists( backupsDir ),
187 "zip backups directory must survive RestoreCommit" );
188 BOOST_CHECK_MESSAGE( wxFileExists( zipPath ),
189 ".zip archive inside the backups directory must survive RestoreCommit" );
190}
191
192
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
void SaveToHistory(const wxString &aProjectPath, std::vector< HISTORY_FILE_DATA > &aFileData)
Serialize board into HISTORY_FILE_DATA for non-blocking history commit.
Definition board.cpp:3924
void SetProject(PROJECT *aProject, bool aReferenceOnly=false)
Link a board to a given project.
Definition board.cpp:213
const wxString & GetFileName() const
Definition board.h:360
void ClearProject()
Definition board.cpp:254
PROJECT * GetProject() const
Definition board.h:587
AUTO_BACKUP m_Backup
Simple local history manager built on libgit2.
wxString GetHeadHash(const wxString &aProjectPath)
Return the current head commit hash.
bool RestoreCommit(const wxString &aProjectPath, const wxString &aHash, wxWindow *aParent=nullptr)
Restore the project files to the state recorded by the given commit hash.
bool Init(const wxString &aProjectPath)
Initialize the local history repository for the given project path.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:541
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:187
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
bool enabled
Automatically back up the project when files are saved.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(SaveToHistoryWithNullProjectDoesNotCrash)
Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23737.
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")