2222
2323#include < array>
2424#include < fstream>
25+ #include < functional>
2526#include < iostream>
2627#include < memory>
2728#include < string>
3031#include " Firestore/core/src/util/filesystem.h"
3132#include " Firestore/core/src/util/path.h"
3233
33- #include " firebase_test_framework.h"
3434#include " gtest/gtest.h"
3535#include " leveldb/db.h"
3636
@@ -49,36 +49,109 @@ using ::firebase::firestore::util::Path;
4949// with reason "corruption".
5050Path CreateLevelDbDatabaseThatUsesSnappyCompression ();
5151
52- // This test ensures that we don't accidentally regress having added in Snappy
53- // compression support (https://github.com/firebase/firebase-ios-sdk/pull/9596).
54- TEST (LevelDbSnappy, LevelDbHasSnappySupportCompiledIn) {
55- // Do not run this test on iOS because LevelDb in iOS does not support Snappy.
56- SKIP_TEST_ON_IOS;
52+ // Creates and opens a LevelDb database that contains at least one block that
53+ // is compressed with Snappy compression, then iterates over it, invoking the
54+ // given callback with the status at each point in the iteration. Once the
55+ // callback is invoked with a `status` where `status.ok()` is not true, then
56+ // iteration will stop and the callback will not be invoked again.
57+ void IterateOverLevelDbDatabaseThatUsesSnappyCompression (
58+ std::function<void (const leveldb::Status&)>);
5759
58- Path leveldb_path = CreateLevelDbDatabaseThatUsesSnappyCompression ();
59- if (HasFatalFailure ()) return ;
60+ #if FIREBASE_TESTS_BUILT_BY_CMAKE
6061
61- leveldb::Options options;
62- options.create_if_missing = false ;
62+ // Ensure that LevelDb is compiled with Snappy compression support.
63+ // See https://github.com/firebase/firebase-ios-sdk/pull/9596 for details.
64+ TEST (LevelDbSnappy, LevelDbSupportsSnappy) {
65+ IterateOverLevelDbDatabaseThatUsesSnappyCompression (
66+ [](const leveldb::Status& status) {
67+ ASSERT_TRUE (status.ok ()) << ConvertStatus (status);
68+ });
69+ }
70+
71+ #else // FIREBASE_TESTS_BUILT_BY_CMAKE
72+
73+ // Ensure that LevelDb is NOT compiled with Snappy compression support.
74+ TEST (LevelDbSnappy, LevelDbDoesNotSupportSnappy) {
75+ bool got_failed_status = false ;
76+ IterateOverLevelDbDatabaseThatUsesSnappyCompression (
77+ [&](const leveldb::Status& status) {
78+ if (!status.ok ()) {
79+ got_failed_status = true ;
80+ ASSERT_TRUE (status.IsCorruption ()) << ConvertStatus (status);
81+ }
82+ });
6383
84+ if (!HasFailure ()) {
85+ ASSERT_TRUE (got_failed_status)
86+ << " Reading a Snappy-compressed LevelDb database was successful; "
87+ " however, it should NOT have been successful "
88+ " since Snappy support is expected to NOT be available." ;
89+ }
90+ }
91+
92+ #endif // FIREBASE_TESTS_BUILT_BY_CMAKE
93+
94+ void IterateOverLevelDbDatabaseThatUsesSnappyCompression (
95+ std::function<void (const leveldb::Status&)> callback) {
6496 std::unique_ptr<leveldb::DB> db;
6597 {
98+ Path leveldb_path = CreateLevelDbDatabaseThatUsesSnappyCompression ();
99+ if (leveldb_path.empty ()) {
100+ return ;
101+ }
102+
103+ leveldb::Options options;
104+ options.create_if_missing = false ;
105+
66106 leveldb::DB* db_ptr;
67107 leveldb::Status status =
68108 leveldb::DB::Open (options, leveldb_path.ToUtf8String (), &db_ptr);
69- ASSERT_TRUE (status.ok ());
109+
110+ ASSERT_TRUE (status.ok ())
111+ << " Opening LevelDb database " << leveldb_path.ToUtf8String ()
112+ << " failed: " << ConvertStatus (status);
113+
70114 db.reset (db_ptr);
71115 }
72116
73- // One of the assertions below will fail when LevelDb attempts to read a block
74- // that is compressed with Snappy and Snappy compression support is not
75- // compiled in.
76117 std::unique_ptr<leveldb::Iterator> it (
77118 db->NewIterator (leveldb::ReadOptions ()));
78119 for (it->SeekToFirst (); it->Valid (); it->Next ()) {
79- ASSERT_TRUE (it->status ().ok ()) << ConvertStatus (it->status ());
120+ callback (it->status ());
121+ if (!it->status ().ok ()) {
122+ return ;
123+ }
124+ }
125+
126+ // Invoke the callback on the final status.
127+ callback (it->status ());
128+ }
129+
130+ template <typename T>
131+ void WriteFile (const Path& dir,
132+ const std::string& file_name,
133+ const T& data_array) {
134+ Filesystem* fs = Filesystem::Default ();
135+ {
136+ auto status = fs->RecursivelyCreateDir (dir);
137+ if (!status.ok ()) {
138+ FAIL () << " Creating directory failed: " << dir.ToUtf8String () << " ("
139+ << status.error_message () << " )" ;
140+ }
141+ }
142+
143+ Path file = dir.AppendUtf8 (file_name);
144+ std::ofstream out_file (file.native_value (), std::ios::binary);
145+ if (!out_file) {
146+ FAIL () << " Unable to open file for writing: " << file.ToUtf8String ();
147+ }
148+
149+ out_file.write (reinterpret_cast <const char *>(data_array.data ()),
150+ data_array.size ());
151+ out_file.close ();
152+ if (!out_file) {
153+ FAIL () << " Writing to file failed: " << file.ToUtf8String ();
80154 }
81- ASSERT_TRUE (it->status ().ok ()) << ConvertStatus (it->status ());
82155}
83156
84157const std::array<unsigned char , 0x00000165 > LevelDbSnappyFile_000005_ldb{
@@ -196,54 +269,35 @@ const std::array<unsigned char, 0x000000C2> LevelDbSnappyFile_MANIFEST_000084{
196269 0x04 , 0x0D ,
197270};
198271
199- template <typename T>
200- void WriteFile (const Path& dir,
201- const std::string& file_name,
202- const T& data_array) {
203- Filesystem* fs = Filesystem::Default ();
204- {
205- auto status = fs->RecursivelyCreateDir (dir);
206- if (!status.ok ()) {
207- FAIL () << " Creating directory failed: " << dir.ToUtf8String () << " ("
208- << status.error_message () << " )" ;
209- }
210- }
211-
212- Path file = dir.AppendUtf8 (file_name);
213- std::ofstream out_file (file.native_value (), std::ios::binary);
214- if (!out_file) {
215- FAIL () << " Unable to open file for writing: " << file.ToUtf8String ();
216- }
217-
218- out_file.write (reinterpret_cast <const char *>(data_array.data ()),
219- data_array.size ());
220- out_file.close ();
221- if (!out_file) {
222- FAIL () << " Writing to file failed: " << file.ToUtf8String ();
223- }
224- }
225-
226272Path LevelDbDir () {
227273 Filesystem* fs = Filesystem::Default ();
228- Path dir = fs->TempDir ().AppendUtf8 (" PersistenceTesting " );
274+ Path dir = fs->TempDir ().AppendUtf8 (" LevelDbSnappyTest " );
229275
230276 // Delete the directory first to ensure isolation between runs.
231277 auto status = fs->RecursivelyRemove (dir);
232- EXPECT_TRUE (status.ok ()) << " Failed to clean up leveldb in dir "
278+ EXPECT_TRUE (status.ok ()) << " Failed to clean up leveldb in directory "
233279 << dir.ToUtf8String () << " : " << status.ToString ();
280+ if (!status.ok ()) {
281+ return {};
282+ }
234283
235284 return dir;
236285}
237286
238287Path CreateLevelDbDatabaseThatUsesSnappyCompression () {
239288 Path leveldb_dir = LevelDbDir ();
289+ if (leveldb_dir.empty ()) {
290+ return {};
291+ }
292+
240293 WriteFile (leveldb_dir, " 000005.ldb" , LevelDbSnappyFile_000005_ldb);
241294 WriteFile (leveldb_dir, " 000017.ldb" , LevelDbSnappyFile_000017_ldb);
242295 WriteFile (leveldb_dir, " 000085.ldb" , LevelDbSnappyFile_000085_ldb);
243296 WriteFile (leveldb_dir, " CURRENT" , LevelDbSnappyFile_CURRENT);
244297 WriteFile (leveldb_dir, " LOG.old" , LevelDbSnappyFile_LOG_old);
245298 WriteFile (leveldb_dir, " LOG" , LevelDbSnappyFile_LOG);
246299 WriteFile (leveldb_dir, " MANIFEST-000084" , LevelDbSnappyFile_MANIFEST_000084);
300+
247301 return leveldb_dir;
248302}
249303
0 commit comments