1414#include " components/FileChangeMonitorImpl.h"
1515#include " testing/TestEnvironmentCore.h"
1616#include " utils/u8path.h"
17+ #include " utils/FileUtils.h"
1718
1819#include < fstream>
1920
2021using namespace raco ::core;
2122
22- class FileChangeMonitorTest : public TestEnvironmentCore {
23+ class BasicFileChangeMonitorTest : public TestEnvironmentCore {
24+ protected:
25+ bool waitForFileChangeCounterGEq (int count, int timeOutInMS = 2000 ) {
26+ auto start = std::chrono::steady_clock::now ();
27+ do {
28+ // Programmatic file/directory modifications need to be explicitly processed in a Qt event loop (QCoreApplication).
29+
30+ // Briefly yield, so the QFileSystemWatcher has time to react to our file changes and
31+ // add its event to the event loop.
32+ std::this_thread::sleep_for (std::chrono::milliseconds (1 ));
33+ // Process the event the QFileSystemWatcher queued. This will cause the FileChangeListener
34+ // to create a timer.
35+ QCoreApplication::processEvents ();
36+
37+ // Wait for the timer to queue its event, and then process the timer event, which eventually
38+ // leads to the callbacks registered with FileMonitor::registerFileChangedHandler to be called.
39+ std::this_thread::sleep_for (std::chrono::milliseconds (raco::components::FileChangeListenerImpl::DELAYED_FILE_LOAD_TIME_MSEC + 100 ));
40+ QCoreApplication::processEvents ();
41+ }
42+ while (fileChangeCounter_ < count && std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now () - start).count () <= timeOutInMS);
43+
44+ return fileChangeCounter_ == count;
45+ }
46+
47+ int argc = 0 ;
48+ // Apparently QCoreApplication needs to be initialized before the ProjectFileChangeMonitor is created, since that
49+ // will create signal connections which don't work on Linux otherwise (but they do work on Windows).
50+ QCoreApplication eventLoop_{argc, nullptr };
51+
52+ int fileChangeCounter_{0 };
53+ std::function<void (void )> testCallback_ = [this ]() { ++fileChangeCounter_; };
54+ std::unique_ptr<raco::components::ProjectFileChangeMonitor> testFileChangeMonitor_ = std::make_unique<raco::components::ProjectFileChangeMonitor>();
55+ std::vector<raco::components::ProjectFileChangeMonitor::UniqueListener> createdFileListeners_;
56+ };
57+
58+ class FileChangeMonitorTest : public BasicFileChangeMonitorTest {
2359protected:
2460 static constexpr const char * TEST_RESOURCES_FOLDER_NAME = " testresources" ;
2561 static constexpr const char * TEST_FILE_NAME = " test.txt" ;
@@ -28,8 +64,7 @@ class FileChangeMonitorTest : public TestEnvironmentCore {
2864 TestEnvironmentCore::SetUp ();
2965 std::filesystem::create_directory (testFolderPath_);
3066
31- testFileOutputStream_ = std::ofstream (testFilePath_.string (), std::ios_base::out);
32- testFileOutputStream_.close ();
67+ raco::utils::file::write (testFilePath_, {});
3368
3469 createdFileListeners_.emplace_back (testFileChangeMonitor_->registerFileChangedHandler (testFilePath_.string (), testCallback_));
3570 }
@@ -47,28 +82,6 @@ class FileChangeMonitorTest : public TestEnvironmentCore {
4782 TestEnvironmentCore::TearDown ();
4883 }
4984
50- int waitForFileChangeCounterGEq (int count, int timeOutInMS = 2000 ) {
51- auto start = std::chrono::steady_clock::now ();
52- do {
53- // Programmatic file/directory modifications need to be explicitly processed in a Qt event loop (QCoreApplication).
54-
55- // Briefly yield, so the QFileSystemWatcher has time to react to our file changes and
56- // add its event to the event loop.
57- std::this_thread::sleep_for (std::chrono::milliseconds (1 ));
58- // Process the event the QFileSystemWatcher queued. This will cause the FileChangeListener
59- // to create a timer.
60- QCoreApplication::processEvents ();
61-
62- // Wait for the timer to queue its event, and then process the timer event, which eventually
63- // leads to the callbacks registered with FileMonitor::registerFileChangedHandler to be called.
64- std::this_thread::sleep_for (std::chrono::milliseconds (raco::components::FileChangeListenerImpl::DELAYED_FILE_LOAD_TIME_MSEC + 100 ));
65- QCoreApplication::processEvents ();
66- }
67- while (fileChangeCounter_ < count && std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now () - start).count () <= timeOutInMS);
68-
69- return fileChangeCounter_;
70- }
71-
7285 void runRenamingRoutine (raco::utils::u8path& originPath, const char * firstRename, const char * secondRename) {
7386 auto newFilePath = originPath;
7487 newFilePath.replace_filename (firstRename);
@@ -77,62 +90,47 @@ class FileChangeMonitorTest : public TestEnvironmentCore {
7790 evenNewerFilePath.replace_filename (secondRename);
7891
7992 std::filesystem::rename (testFilePath_, newFilePath);
80- ASSERT_EQ (waitForFileChangeCounterGEq (1 ), 1 );
93+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
8194
8295 std::filesystem::rename (newFilePath, evenNewerFilePath);
83- ASSERT_EQ (waitForFileChangeCounterGEq (1 ), 1 );
96+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
8497
8598 std::filesystem::rename (evenNewerFilePath, testFilePath_);
86- ASSERT_EQ (waitForFileChangeCounterGEq (2 ), 2 );
99+ ASSERT_TRUE (waitForFileChangeCounterGEq (2 ));
87100
88101 std::filesystem::rename (testFilePath_, newFilePath);
89- ASSERT_EQ (waitForFileChangeCounterGEq (3 ), 3 );
102+ ASSERT_TRUE (waitForFileChangeCounterGEq (3 ));
90103 }
91104
92- int argc = 0 ;
93- // Apparently QCoreApplication needs to be initialized before the ProjectFileChangeMonitor is created, since that
94- // will create signal connections which don't work on Linux otherwise (but they do work on Windows).
95- QCoreApplication eventLoop_{argc, nullptr };
96- std::ofstream testFileOutputStream_;
97- int fileChangeCounter_{0 };
98105 raco::utils::u8path testFolderPath_{test_path ().append (TEST_RESOURCES_FOLDER_NAME)};
99106 raco::utils::u8path testFilePath_{raco::utils::u8path (testFolderPath_).append (TEST_FILE_NAME)};
100- std::function<void (void )> testCallback_ = [this ]() { ++fileChangeCounter_; };
101- std::unique_ptr<raco::components::ProjectFileChangeMonitor> testFileChangeMonitor_ = std::make_unique<raco::components::ProjectFileChangeMonitor>();
102- std::vector<raco::components::ProjectFileChangeMonitor::UniqueListener> createdFileListeners_;
103107};
104108
105-
106109TEST_F (FileChangeMonitorTest, InstantiationNoFileChange) {
107110 ASSERT_EQ (fileChangeCounter_, 0 );
108111}
109112
110113TEST_F (FileChangeMonitorTest, FileModificationCreation) {
111114 testFilePath_ = raco::utils::u8path (testFolderPath_).append (" differentFile.txt" );
112115 createdFileListeners_.emplace_back (testFileChangeMonitor_->registerFileChangedHandler (testFilePath_.string (), testCallback_));
113- ASSERT_EQ (waitForFileChangeCounterGEq (0 ), 0 );
116+ ASSERT_TRUE (waitForFileChangeCounterGEq (0 ));
114117
115- testFileOutputStream_ = std::ofstream (testFilePath_.string (), std::ios_base::out);
116- testFileOutputStream_.close ();
118+ raco::utils::file::write (testFilePath_, {});
117119
118- ASSERT_EQ (waitForFileChangeCounterGEq (1 ), 1 );
120+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
119121}
120122
121123
122124TEST_F (FileChangeMonitorTest, FileModificationEditing) {
123- testFileOutputStream_.open (testFilePath_.string (), std::ios_base::out);
124-
125- testFileOutputStream_ << " Test" ;
126- testFileOutputStream_.flush ();
127- testFileOutputStream_.close ();
125+ raco::utils::file::write (testFilePath_, " Test" );
128126
129- ASSERT_EQ (waitForFileChangeCounterGEq (1 ), 1 );
127+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
130128}
131129
132130
133131TEST_F (FileChangeMonitorTest, FileModificationDeletion) {
134132 std::filesystem::remove (testFilePath_);
135- ASSERT_EQ (waitForFileChangeCounterGEq (1 ), 1 );
133+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
136134}
137135
138136
@@ -142,16 +140,17 @@ TEST_F(FileChangeMonitorTest, FileModificationRenaming) {
142140
143141
144142TEST_F (FileChangeMonitorTest, FileModificationMultipleModificationsAtTheSameTime) {
145- testFileOutputStream_. open (testFilePath_.string (), std::ios_base::out);
146- testFileOutputStream_ << " Test" ;
143+ std::ofstream testFileOutputStream (testFilePath_.string (), std::ios_base::out);
144+ testFileOutputStream << " Test" ;
147145
148146 std::ofstream otherOutputStream = std::ofstream (testFilePath_.string (), std::ios_base::app);
149147 otherOutputStream << " Other" ;
150148 otherOutputStream.close ();
151149
152- testFileOutputStream_ .close ();
150+ testFileOutputStream .close ();
153151
154- ASSERT_GE (waitForFileChangeCounterGEq (1 ), 1 ); // Linux gives sometimes one, sometimes two events.
152+ // Linux gives sometimes one, sometimes two events.
153+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
155154}
156155
157156#if (!defined(__linux))
@@ -163,22 +162,128 @@ TEST_F(FileChangeMonitorTest, FileModificationSetAndTryToEditReadOnly) {
163162 ASSERT_TRUE (!ec) << " Failed to set permissons. Error code: " << ec.value () << " Error message: '" << ec.message () << " '" ;
164163
165164 // fileChangeCounter_ will be 0 in WSL, but the proper value in Linux container
166- ASSERT_EQ (waitForFileChangeCounterGEq (1 ), 1 );
165+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
167166
168- testFileOutputStream_. open (testFilePath_.string (), std::ios_base::out);
169- ASSERT_FALSE (testFileOutputStream_ .is_open ());
167+ std::ofstream testFileOutputStream (testFilePath_.string (), std::ios_base::out);
168+ ASSERT_FALSE (testFileOutputStream .is_open ());
170169
171- ASSERT_EQ (waitForFileChangeCounterGEq (1 ), 1 );
170+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
172171}
173172#endif
174173
175174
176175TEST_F (FileChangeMonitorTest, FolderModificationDeletion) {
177176 std::filesystem::remove_all (test_path ().append (TEST_RESOURCES_FOLDER_NAME));
178- ASSERT_EQ (waitForFileChangeCounterGEq (1 ), 1 );
177+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
179178}
180179
181180
182181TEST_F (FileChangeMonitorTest, FolderModificationRenaming) {
183182 runRenamingRoutine (testFolderPath_, " newFolder" , " evenNewerFolder" );
184183}
184+
185+ TEST_F (BasicFileChangeMonitorTest, create_file_existing_directory) {
186+ auto testFolderPath = test_path () / " test-folder" ;
187+ std::string test_file_name = " test.txt" ;
188+
189+ std::filesystem::create_directory (testFolderPath);
190+
191+ createdFileListeners_.emplace_back (testFileChangeMonitor_->registerFileChangedHandler ((testFolderPath / test_file_name).string (), testCallback_));
192+ EXPECT_EQ (fileChangeCounter_, 0 );
193+
194+ raco::utils::file::write (testFolderPath / test_file_name, {});
195+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
196+ }
197+
198+ TEST_F (BasicFileChangeMonitorTest, create_file_no_directory) {
199+ auto testFolderPath = test_path () / " test-folder" ;
200+ std::string test_file_name = " test.txt" ;
201+
202+ createdFileListeners_.emplace_back (testFileChangeMonitor_->registerFileChangedHandler ((testFolderPath / test_file_name).string (), testCallback_));
203+ EXPECT_EQ (fileChangeCounter_, 0 );
204+
205+ std::filesystem::create_directory (testFolderPath);
206+ ASSERT_TRUE (waitForFileChangeCounterGEq (0 ));
207+
208+ raco::utils::file::write (testFolderPath / test_file_name, {});
209+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
210+ }
211+
212+ TEST_F (FileChangeMonitorTest, directory_rename_disappearance) {
213+ auto movedFolderPath = test_path () / " moved-directory" ;
214+ std::filesystem::rename (testFolderPath_, movedFolderPath);
215+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
216+
217+ std::filesystem::rename (movedFolderPath, testFolderPath_);
218+ ASSERT_TRUE (waitForFileChangeCounterGEq (2 ));
219+ }
220+
221+ TEST_F (BasicFileChangeMonitorTest, directory_rename_appearance) {
222+ auto initialFolderPath = test_path () / " initial-folder" ;
223+ auto testFolderPath = test_path () / " test-folder" ;
224+ std::string test_file_name = " test.txt" ;
225+
226+ std::filesystem::create_directory (initialFolderPath);
227+ raco::utils::file::write (initialFolderPath / test_file_name, {});
228+
229+ createdFileListeners_.emplace_back (testFileChangeMonitor_->registerFileChangedHandler ((testFolderPath / test_file_name).string (), testCallback_));
230+ EXPECT_EQ (fileChangeCounter_, 0 );
231+
232+ std::filesystem::rename (initialFolderPath, testFolderPath);
233+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
234+ }
235+
236+ TEST_F (BasicFileChangeMonitorTest, directory_appear_file_change) {
237+ auto initialFolderPath = test_path () / " initial-folder" ;
238+ auto testFolderPath = test_path () / " test-folder" ;
239+ std::string test_file_name = " test.txt" ;
240+
241+ std::filesystem::create_directory (initialFolderPath);
242+ raco::utils::file::write (initialFolderPath / test_file_name, {});
243+
244+ createdFileListeners_.emplace_back (testFileChangeMonitor_->registerFileChangedHandler ((testFolderPath / test_file_name).string (), testCallback_));
245+ EXPECT_EQ (fileChangeCounter_, 0 );
246+
247+ std::filesystem::rename (initialFolderPath, testFolderPath);
248+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
249+
250+ raco::utils::file::write (testFolderPath / test_file_name, " Test" );
251+ ASSERT_TRUE (waitForFileChangeCounterGEq (2 ));
252+ }
253+
254+ TEST_F (BasicFileChangeMonitorTest, directory_appear_file_disappear) {
255+ auto initialFolderPath = test_path () / " initial-folder" ;
256+ auto testFolderPath = test_path () / " test-folder" ;
257+ std::string test_file_name = " test.txt" ;
258+
259+ std::filesystem::create_directory (initialFolderPath);
260+ raco::utils::file::write (initialFolderPath / test_file_name, {});
261+
262+ createdFileListeners_.emplace_back (testFileChangeMonitor_->registerFileChangedHandler ((testFolderPath / test_file_name).string (), testCallback_));
263+ EXPECT_EQ (fileChangeCounter_, 0 );
264+
265+ std::filesystem::rename (initialFolderPath, testFolderPath);
266+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
267+
268+ std::filesystem::rename (testFolderPath / test_file_name, testFolderPath / " new-file-name.txt" );
269+ ASSERT_TRUE (waitForFileChangeCounterGEq (2 ));
270+ }
271+
272+ TEST_F (BasicFileChangeMonitorTest, directory_appear_file_appear) {
273+ auto initialFolderPath = test_path () / " initial-folder" ;
274+ auto testFolderPath = test_path () / " test-folder" ;
275+ std::string initial_test_file_name = " initial.txt" ;
276+ std::string test_file_name = " test.txt" ;
277+
278+ std::filesystem::create_directory (initialFolderPath);
279+ raco::utils::file::write (initialFolderPath / initial_test_file_name, {});
280+
281+ createdFileListeners_.emplace_back (testFileChangeMonitor_->registerFileChangedHandler ((testFolderPath / test_file_name).string (), testCallback_));
282+ EXPECT_EQ (fileChangeCounter_, 0 );
283+
284+ std::filesystem::rename (initialFolderPath, testFolderPath);
285+ ASSERT_TRUE (waitForFileChangeCounterGEq (0 ));
286+
287+ std::filesystem::rename (testFolderPath / initial_test_file_name, testFolderPath / test_file_name);
288+ ASSERT_TRUE (waitForFileChangeCounterGEq (1 ));
289+ }
0 commit comments