1 /*
2  * LibrePCB - Professional EDA for everyone!
3  * Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
4  * https://librepcb.org/
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (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 <http://www.gnu.org/licenses/>.
18  */
19 
20 /*******************************************************************************
21  *  Includes
22  ******************************************************************************/
23 #include <gtest/gtest.h>
24 #include <librepcb/common/fileio/fileutils.h>
25 #include <librepcb/common/fileio/transactionalfilesystem.h>
26 #include <librepcb/library/sym/symbol.h>
27 #include <librepcb/project/library/projectlibrary.h>
28 
29 #include <QtCore>
30 
31 /*******************************************************************************
32  *  Namespace
33  ******************************************************************************/
34 namespace librepcb {
35 namespace project {
36 namespace tests {
37 
38 /*******************************************************************************
39  *  Test Class
40  ******************************************************************************/
41 
42 class ProjectLibraryTest : public ::testing::Test {
43 protected:
44   FilePath mTempDir;
45   FilePath mLibDir;
46   std::shared_ptr<TransactionalFileSystem> mTempFs;
47   std::shared_ptr<TransactionalFileSystem> mLibFs;
48   QFileInfo mExistingSymbolFile;
49   qint64 mExistingSymbolCreationSize;
50   QScopedPointer<library::Symbol> mNewSymbol;
51   QFileInfo mNewSymbolFile;
52 
ProjectLibraryTest()53   ProjectLibraryTest() {
54     mTempDir = FilePath::getRandomTempPath();
55     mLibDir = mTempDir.getPathTo("project library test");
56     mTempFs = TransactionalFileSystem::openRW(mTempDir);
57     mLibFs = TransactionalFileSystem::openRW(mLibDir);
58 
59     // create symbol inside project library
60     library::Symbol sym(Uuid::createRandom(), Version::fromString("1"), "",
61                         ElementName("Existing Symbol"), "", "");
62     TransactionalDirectory libSymDir(mLibFs, "sym");
63     sym.saveIntoParentDirectory(libSymDir);
64     mLibFs->save();
65     mExistingSymbolFile = QFileInfo(
66         mLibDir
67             .getPathTo(QString("sym/%1/symbol.lp").arg(sym.getUuid().toStr()))
68             .toStr());
69     modifyExistingSymbol();  // modify file to detect when it gets overwritten
70 
71     // create symbol outside the project library (emulating workspace library)
72     mNewSymbol.reset(new library::Symbol(Uuid::createRandom(),
73                                          Version::fromString("1"), "",
74                                          ElementName("New Symbol"), "", ""));
75     TransactionalDirectory tempSymDir(mTempFs);
76     mNewSymbol->saveIntoParentDirectory(tempSymDir);
77     mTempFs->save();
78     mNewSymbolFile =
79         mLibDir
80             .getPathTo(
81                 QString("sym/%1/symbol.lp").arg(mNewSymbol->getUuid().toStr()))
82             .toStr();
83 
84     // disable caching to get correct results
85     mExistingSymbolFile.setCaching(false);
86     mNewSymbolFile.setCaching(false);
87   }
88 
~ProjectLibraryTest()89   virtual ~ProjectLibraryTest() { QDir(mTempDir.toStr()).removeRecursively(); }
90 
getFirstSymbol(ProjectLibrary & lib)91   library::Symbol* getFirstSymbol(ProjectLibrary& lib) {
92     if (lib.getSymbols().isEmpty()) throw LogicError(__FILE__, __LINE__);
93     return lib.getSymbols().values().first();
94   }
95 
save(ProjectLibrary & lib)96   void save(ProjectLibrary& lib) {
97     lib.save();
98     mLibFs->save();
99     mTempFs->save();
100   }
101 
modifyExistingSymbol()102   void modifyExistingSymbol() {
103     FilePath fp(mExistingSymbolFile.absoluteFilePath());
104     FileUtils::writeFile(fp, FileUtils::readFile(fp).append(" "));
105     mExistingSymbolCreationSize = mExistingSymbolFile.size();
106   }
107 };
108 
109 /*******************************************************************************
110  *  Test Methods
111  ******************************************************************************/
112 
TEST_F(ProjectLibraryTest,testLoadSymbol)113 TEST_F(ProjectLibraryTest, testLoadSymbol) {
114   {
115     ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
116         new TransactionalDirectory(mLibFs)));
117     EXPECT_EQ(1, lib.getSymbols().count());
118     EXPECT_TRUE(mExistingSymbolFile.exists());
119   }
120   EXPECT_TRUE(mExistingSymbolFile.exists());
121   EXPECT_EQ(mExistingSymbolCreationSize,
122             mExistingSymbolFile.size());  // not upgraded
123 }
124 
TEST_F(ProjectLibraryTest,testAddSymbol)125 TEST_F(ProjectLibraryTest, testAddSymbol) {
126   {
127     ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
128         new TransactionalDirectory(mLibFs)));
129     lib.addSymbol(*mNewSymbol.take());
130     EXPECT_EQ(2, lib.getSymbols().count());
131     EXPECT_TRUE(mExistingSymbolFile.exists());
132     EXPECT_FALSE(mNewSymbolFile.exists());
133     EXPECT_FALSE(mNewSymbolFile.dir().exists());
134   }
135   EXPECT_TRUE(mExistingSymbolFile.exists());
136   EXPECT_FALSE(mNewSymbolFile.exists());
137   EXPECT_FALSE(mNewSymbolFile.dir().exists());
138   EXPECT_EQ(mExistingSymbolCreationSize,
139             mExistingSymbolFile.size());  // not upgraded
140 }
141 
TEST_F(ProjectLibraryTest,testAddSymbol_Save)142 TEST_F(ProjectLibraryTest, testAddSymbol_Save) {
143   {
144     ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
145         new TransactionalDirectory(mLibFs)));
146     lib.addSymbol(*mNewSymbol.take());
147     save(lib);
148     EXPECT_EQ(2, lib.getSymbols().count());
149     EXPECT_TRUE(mExistingSymbolFile.exists());
150     EXPECT_TRUE(mNewSymbolFile.exists());
151   }
152   EXPECT_TRUE(mExistingSymbolFile.exists());
153   EXPECT_TRUE(mNewSymbolFile.exists());
154   EXPECT_NE(mExistingSymbolCreationSize,
155             mExistingSymbolFile.size());  // upgraded!
156 }
157 
TEST_F(ProjectLibraryTest,testAddRemoveSymbol)158 TEST_F(ProjectLibraryTest, testAddRemoveSymbol) {
159   {
160     ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
161         new TransactionalDirectory(mLibFs)));
162     lib.addSymbol(*mNewSymbol);
163     lib.removeSymbol(*mNewSymbol.take());
164     EXPECT_EQ(1, lib.getSymbols().count());
165     EXPECT_TRUE(mExistingSymbolFile.exists());
166     EXPECT_FALSE(mNewSymbolFile.exists());
167     EXPECT_FALSE(mNewSymbolFile.dir().exists());
168   }
169   EXPECT_TRUE(mExistingSymbolFile.exists());
170   EXPECT_FALSE(mNewSymbolFile.exists());
171   EXPECT_FALSE(mNewSymbolFile.dir().exists());
172   EXPECT_EQ(mExistingSymbolCreationSize,
173             mExistingSymbolFile.size());  // not upgraded
174 }
175 
TEST_F(ProjectLibraryTest,testAddRemoveSymbol_Save)176 TEST_F(ProjectLibraryTest, testAddRemoveSymbol_Save) {
177   {
178     ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
179         new TransactionalDirectory(mLibFs)));
180     lib.addSymbol(*mNewSymbol);
181     lib.removeSymbol(*mNewSymbol.take());
182     save(lib);
183     EXPECT_EQ(1, lib.getSymbols().count());
184     EXPECT_TRUE(mExistingSymbolFile.exists());
185     EXPECT_FALSE(mNewSymbolFile.exists());
186     EXPECT_FALSE(mNewSymbolFile.dir().exists());
187   }
188   EXPECT_TRUE(mExistingSymbolFile.exists());
189   EXPECT_FALSE(mNewSymbolFile.exists());
190   EXPECT_FALSE(mNewSymbolFile.dir().exists());
191   EXPECT_NE(mExistingSymbolCreationSize,
192             mExistingSymbolFile.size());  // upgraded!
193 }
194 
TEST_F(ProjectLibraryTest,testRemoveSymbol)195 TEST_F(ProjectLibraryTest, testRemoveSymbol) {
196   {
197     ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
198         new TransactionalDirectory(mLibFs)));
199     lib.removeSymbol(*getFirstSymbol(lib));
200     EXPECT_EQ(0, lib.getSymbols().count());
201     EXPECT_TRUE(mExistingSymbolFile.exists());
202   }
203   EXPECT_TRUE(mExistingSymbolFile.exists());
204   EXPECT_EQ(mExistingSymbolCreationSize,
205             mExistingSymbolFile.size());  // not upgraded
206 }
207 
TEST_F(ProjectLibraryTest,testRemoveSymbol_Save)208 TEST_F(ProjectLibraryTest, testRemoveSymbol_Save) {
209   {
210     ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
211         new TransactionalDirectory(mLibFs)));
212     lib.removeSymbol(*getFirstSymbol(lib));
213     save(lib);
214     EXPECT_EQ(0, lib.getSymbols().count());
215     EXPECT_FALSE(mExistingSymbolFile.exists());
216     EXPECT_FALSE(mExistingSymbolFile.dir().exists());
217   }
218   EXPECT_FALSE(mExistingSymbolFile.exists());
219   EXPECT_FALSE(mExistingSymbolFile.dir().exists());
220 }
221 
TEST_F(ProjectLibraryTest,testRemoveAddSymbol)222 TEST_F(ProjectLibraryTest, testRemoveAddSymbol) {
223   {
224     ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
225         new TransactionalDirectory(mLibFs)));
226     library::Symbol* sym = getFirstSymbol(lib);
227     lib.removeSymbol(*sym);
228     lib.addSymbol(*sym);
229     EXPECT_EQ(1, lib.getSymbols().count());
230     EXPECT_TRUE(mExistingSymbolFile.exists());
231   }
232   EXPECT_TRUE(mExistingSymbolFile.exists());
233   EXPECT_EQ(mExistingSymbolCreationSize,
234             mExistingSymbolFile.size());  // not upgraded
235 }
236 
TEST_F(ProjectLibraryTest,testRemoveAddSymbol_Save)237 TEST_F(ProjectLibraryTest, testRemoveAddSymbol_Save) {
238   {
239     ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
240         new TransactionalDirectory(mLibFs)));
241     library::Symbol* sym = getFirstSymbol(lib);
242     lib.removeSymbol(*sym);
243     lib.addSymbol(*sym);
244     save(lib);
245     EXPECT_EQ(1, lib.getSymbols().count());
246     EXPECT_TRUE(mExistingSymbolFile.exists());
247   }
248   EXPECT_TRUE(mExistingSymbolFile.exists());
249   EXPECT_NE(mExistingSymbolCreationSize,
250             mExistingSymbolFile.size());  // upgraded!
251 }
252 
TEST_F(ProjectLibraryTest,testSavingToExistingEmptyDirectory)253 TEST_F(ProjectLibraryTest, testSavingToExistingEmptyDirectory) {
254   ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
255       new TransactionalDirectory(mLibFs)));
256 
257   // already create the destination directory to see if saving still works
258   EXPECT_FALSE(mNewSymbolFile.dir().exists());
259   FileUtils::makePath(FilePath(mNewSymbolFile.dir().absolutePath()));
260   EXPECT_TRUE(mNewSymbolFile.dir().exists());
261 
262   lib.addSymbol(*mNewSymbol.take());
263   save(lib);
264   EXPECT_TRUE(mNewSymbolFile.exists());
265 }
266 
TEST_F(ProjectLibraryTest,testIfExistingSymbolIsUpgradedOnlyOnce)267 TEST_F(ProjectLibraryTest, testIfExistingSymbolIsUpgradedOnlyOnce) {
268   ProjectLibrary lib(std::unique_ptr<TransactionalDirectory>(
269       new TransactionalDirectory(mLibFs)));
270   EXPECT_EQ(mExistingSymbolCreationSize,
271             mExistingSymbolFile.size());  // not upgraded
272   save(lib);
273   EXPECT_NE(mExistingSymbolCreationSize,
274             mExistingSymbolFile.size());  // upgraded!
275   modifyExistingSymbol();
276   EXPECT_EQ(mExistingSymbolCreationSize,
277             mExistingSymbolFile.size());  // not upgraded
278   save(lib);
279   EXPECT_EQ(mExistingSymbolCreationSize,
280             mExistingSymbolFile.size());  // not upgraded
281 }
282 
283 /*******************************************************************************
284  *  End of File
285  ******************************************************************************/
286 
287 }  // namespace tests
288 }  // namespace project
289 }  // namespace librepcb
290