diff --git a/cmake/external/firestore.cmake b/cmake/external/firestore.cmake index d94c6681e3..28774952c1 100644 --- a/cmake/external/firestore.cmake +++ b/cmake/external/firestore.cmake @@ -13,59 +13,26 @@ # limitations under the License. include(ExternalProject) +include(FindPythonInterp) if(TARGET firestore) return() endif() -function(GetReleasedDep version) - message("Getting released firebase-ios-sdk @ ${version}") - ExternalProject_Add( - firestore +ExternalProject_Add( + firestore - DOWNLOAD_DIR ${FIREBASE_DOWNLOAD_DIR} - URL https://github.com/firebase/firebase-ios-sdk/archive/${version}.tar.gz + DOWNLOAD_DIR ${FIREBASE_DOWNLOAD_DIR} + GIT_REPOSITORY https://github.com/firebase/firebase-ios-sdk + GIT_TAG 70aa8b82a2ed36dd14448174bea0fd7e575d4d49 + GIT_SHALLOW ON - PREFIX ${PROJECT_BINARY_DIR} - - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - PATCH_COMMAND patch -Np1 -i ${CMAKE_CURRENT_LIST_DIR}/firestore_snappy.patch.txt - HTTP_HEADER "${EXTERNAL_PROJECT_HTTP_HEADER}" - ) -endfunction() - -function(GetTag t) - message("Getting firebase-ios-sdk from ${t}") - ExternalProject_Add( - firestore - - DOWNLOAD_DIR ${FIREBASE_DOWNLOAD_DIR} - GIT_REPOSITORY "https://github.com/firebase/firebase-ios-sdk.git" - GIT_TAG ${t} - GIT_SHALLOW "ON" - - PREFIX ${PROJECT_BINARY_DIR} - - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - PATCH_COMMAND patch -Np1 -i ${CMAKE_CURRENT_LIST_DIR}/firestore_snappy.patch.txt - HTTP_HEADER "${EXTERNAL_PROJECT_HTTP_HEADER}" - ) -endfunction() - -if((NOT FIRESTORE_DEP_SOURCE) OR (FIRESTORE_DEP_SOURCE STREQUAL "RELEASED")) - # Get from released dependency by default - GetReleasedDep("CocoaPods-8.12.1") -else() - if(FIRESTORE_DEP_SOURCE STREQUAL "TIP") - GetTag("origin/master") - else() - GetTag(${FIRESTORE_DEP_SOURCE}) - endif() -endif() + PREFIX ${PROJECT_BINARY_DIR} + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + PATCH_COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/firestore_patch.py --leveldb-version-from ${CMAKE_CURRENT_LIST_DIR}/leveldb.cmake + HTTP_HEADER "${EXTERNAL_PROJECT_HTTP_HEADER}" +) diff --git a/cmake/external/firestore_patch.py b/cmake/external/firestore_patch.py new file mode 100644 index 0000000000..b5b50a258e --- /dev/null +++ b/cmake/external/firestore_patch.py @@ -0,0 +1,122 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Modify the version in leveldb.cmake from the Firebase iOS SDK to match the +version from this C++ SDK. +""" + +import argparse +import os +import pathlib +import re +from typing import List, Tuple + + +VERSION_PATTERN = r"\s*set\s*\(\s*version\s+([^)\s]+)\s*\)\s*" +VERSION_EXPR = re.compile(VERSION_PATTERN, re.IGNORECASE) +URL_HASH_PATTERN = r"\s*URL_HASH\s+([0-9a-zA-Z=]+)\s*" +URL_HASH_EXPR = re.compile(URL_HASH_PATTERN, re.IGNORECASE) + + +def main() -> None: + (src_file, dest_file) = parse_args() + + leveldb_version = load_value(src_file, VERSION_EXPR) + url_hash = load_value(src_file, URL_HASH_EXPR) + + set_value(dest_file, VERSION_EXPR, leveldb_version) + set_value(dest_file, URL_HASH_EXPR, url_hash) + + +def parse_args() -> Tuple[pathlib.Path, pathlib.Path]: + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument("--leveldb-version-from", required=True) + arg_parser.add_argument("--leveldb-version-to") + + parsed_args = arg_parser.parse_args() + + leveldb_cmake_src_file = pathlib.Path(parsed_args.leveldb_version_from) + + if parsed_args.leveldb_version_to: + leveldb_cmake_dest_file = pathlib.Path(parsed_args.leveldb_version_to) + else: + leveldb_cmake_dest_file = pathlib.Path.cwd() \ + / "cmake" / "external" / "leveldb.cmake" + + return (leveldb_cmake_src_file, leveldb_cmake_dest_file) + + +def load_value(file_: pathlib.Path, expr: re.Pattern) -> str: + value = None + value_line = None + value_line_number = None + + with file_.open("rt", encoding="utf8") as f: + for (line_number, line) in enumerate(f, start=1): + match = expr.fullmatch(line) + if not match: + continue + elif value is not None: + raise RegexMatchError( + f"Multiple lines matching regex {expr.pattern} found in " + f"{file_}: line {value_line_number}, {value_line.strip()} " + f"and line {line_number}, {line.strip()}") + + value = match.group(1).strip() + value_line = line + value_line_number = line_number + + if value is None: + raise RegexMatchError( + f"No line matching regex {expr.pattern} found in {file_}") + + return value + + +def set_value(file_: pathlib.Path, expr: re.Pattern, version: str) -> None: + with file_.open("rt", encoding="utf8") as f: + lines = list(f) + + matching_line = None + matching_line_number = None + + for (line_number, line) in enumerate(lines, start=1): + match = expr.fullmatch(line) + if not match: + continue + elif matching_line is not None: + raise RegexMatchError( + f"Multiple lines matching regex {expr.pattern} found in " + f"{file_}: line {matching_line_number}, {matching_line.strip()} " + f"and line {line_number}, {line.strip()}") + + lines[line_number - 1] = line[:match.start(1)] + version + line[match.end(1):] + matching_line = line + matching_line_number = line_number + + if matching_line is None: + raise RegexMatchError( + f"No line matching regex {expr.pattern} found in {file_}") + + with file_.open("wt", encoding="utf8") as f: + f.writelines(lines) + + +class RegexMatchError(Exception): + pass + + +if __name__ == "__main__": + main() diff --git a/cmake/external/firestore_patch_test.py b/cmake/external/firestore_patch_test.py new file mode 100644 index 0000000000..bf739be555 --- /dev/null +++ b/cmake/external/firestore_patch_test.py @@ -0,0 +1,265 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import pathlib +import re +import shutil +import tempfile +from typing import Iterable +import unittest +import unittest.mock + +import firestore_patch + + +class TestUtilsMixin: + + def create_temp_file_with_lines(self, lines: Iterable[str]) -> pathlib.Path: + (handle, path_str) = tempfile.mkstemp() + os.close(handle) + path = pathlib.Path(path_str) + self.addCleanup(path.unlink, missing_ok=True) # pytype: disable=attribute-error + + with path.open("wt", encoding="utf8") as f: + for line in lines: + print(line, file=f) + + return path + + def create_temp_file_with_line(self, line: str) -> pathlib.Path: + return self.create_temp_file_with_lines([line]) + + +class MainTest(TestUtilsMixin, unittest.TestCase): + + def test(self): + src_file = self.create_temp_file_with_lines([ + "aaa", + "bbb", + "set(version 1.2.3)", + "URL_HASH SHA256=abcdef", + "ccc", + "ddd", + ]) + dest_file = self.create_temp_file_with_lines([ + "aaa", + "bbb", + "set(version 4.5.6)", + "URL_HASH SHA256=ghijkl", + "ccc", + "ddd", + ]) + dest_file_contents_before = dest_file.read_text(encoding="utf8") + patcher = unittest.mock.patch.object( + firestore_patch, + "parse_args", + spec_set=True, + autospec=True, + return_value=(src_file, dest_file) + ) + + with patcher: + firestore_patch.main() + + dest_file_contents_after = dest_file.read_text(encoding="utf8") + self.assertEqual( + dest_file_contents_after, + dest_file_contents_before + .replace("4.5.6", "1.2.3") + .replace("ghijkl", "abcdef") + ) + + +class VersionExprTest(TestUtilsMixin, unittest.TestCase): + + def test_matches_semantic_version(self): + path = self.create_temp_file_with_line("set(version 1.2.3)") + + value = firestore_patch.load_value(path, firestore_patch.VERSION_EXPR) + + self.assertEqual(value, "1.2.3") + + def test_matches_sha1(self): + path = self.create_temp_file_with_line("set(version fd054fa01)") + + value = firestore_patch.load_value(path, firestore_patch.VERSION_EXPR) + + self.assertEqual(value, "fd054fa01") + + def test_ignores_whitespace(self): + path = self.create_temp_file_with_line(" set ( version 1.2.3 ) ") + + value = firestore_patch.load_value(path, firestore_patch.VERSION_EXPR) + + self.assertEqual(value, "1.2.3") + + def test_case_insensitive(self): + path = self.create_temp_file_with_line("sEt(vErSiOn 1.2.3)") + + value = firestore_patch.load_value(path, firestore_patch.VERSION_EXPR) + + self.assertEqual(value, "1.2.3") + + +class UrlHashExprTest(TestUtilsMixin, unittest.TestCase): + + def test_matches_sha256(self): + path = self.create_temp_file_with_line("URL_HASH SHA256=abc123def456") + + value = firestore_patch.load_value(path, firestore_patch.URL_HASH_EXPR) + + self.assertEqual(value, "SHA256=abc123def456") + + def test_ignores_whitespace(self): + path = self.create_temp_file_with_line(" URL_HASH abc123def456 ") + + value = firestore_patch.load_value(path, firestore_patch.URL_HASH_EXPR) + + self.assertEqual(value, "abc123def456") + + def test_case_insensitive(self): + path = self.create_temp_file_with_line("UrL_hAsH Sha256=abc123def456") + + value = firestore_patch.load_value(path, firestore_patch.URL_HASH_EXPR) + + self.assertEqual(value, "Sha256=abc123def456") + + +class LoadValueTest(TestUtilsMixin, unittest.TestCase): + + def test_loads_the_value(self): + path = self.create_temp_file_with_line("aaa1234ccc") + expr = re.compile(r"\D+(\d+)\D+") + + value = firestore_patch.load_value(path, expr) + + self.assertEqual(value, "1234") + + def test_loads_the_value_ignoring_non_matching_lines(self): + path = self.create_temp_file_with_lines([ + "blah", + "blah", + "aaa1234cccc", + "blah", + "blah", + ]) + expr = re.compile(r"\D+(\d+)\D+") + + value = firestore_patch.load_value(path, expr) + + self.assertEqual(value, "1234") + + def test_raises_error_if_no_matching_line_found(self): + path = self.create_temp_file_with_lines([ + "blah", + "blah", + "blah", + "blah", + ]) + expr = re.compile(r"\D+(\d+)\D+") + + with self.assertRaises(firestore_patch.RegexMatchError) as cm: + firestore_patch.load_value(path, expr) + + self.assertIn("no line matching", str(cm.exception).lower()) + self.assertIn(expr.pattern, str(cm.exception)) + self.assertIn(str(path), str(cm.exception)) + + def test_raises_error_if_multiple_matching_lines_found(self): + lines = ["blah"] * 100 + lines.append("aaa123bbb") + lines.append("ccc456ddd") + path = self.create_temp_file_with_lines(lines) + expr = re.compile(r"\D+(\d+)\D+") + + with self.assertRaises(firestore_patch.RegexMatchError) as cm: + firestore_patch.load_value(path, expr) + + self.assertIn("multiple lines matching", str(cm.exception).lower()) + self.assertIn(str(path), str(cm.exception)) + self.assertIn(expr.pattern, str(cm.exception)) + self.assertIn("line 101", str(cm.exception)) + self.assertIn("line 102", str(cm.exception)) + self.assertIn("123", str(cm.exception)) + self.assertIn("456", str(cm.exception)) + + +class SaveValueTest(TestUtilsMixin, unittest.TestCase): + + def test_saves_the_value(self): + path = self.create_temp_file_with_line("aaa1234ccc") + expr = re.compile(r"\D+(\d+)\D+") + + firestore_patch.set_value(path, expr, "9876") + + new_value = firestore_patch.load_value(path, expr) + self.assertEqual(new_value, "9876") + + def test_saves_the_value_ignoring_non_matching_lines(self): + path = self.create_temp_file_with_lines([ + "blah", + "blah", + "aaa1234cccc", + "blah", + "blah", + ]) + expr = re.compile(r"\D+(\d+)\D+") + file_contents_before = path.read_text(encoding="utf8") + + firestore_patch.set_value(path, expr, "9876") + + file_contents_after = path.read_text(encoding="utf8") + self.assertEqual( + file_contents_after, + file_contents_before.replace("1234", "9876"), + ) + + def test_raises_error_if_no_matching_line_found(self): + path = self.create_temp_file_with_lines([ + "blah", + "blah", + "blah", + "blah", + ]) + expr = re.compile(r"\D+(\d+)\D+") + + with self.assertRaises(firestore_patch.RegexMatchError) as cm: + firestore_patch.set_value(path, expr, "") + + self.assertIn("no line matching", str(cm.exception).lower()) + self.assertIn(expr.pattern, str(cm.exception)) + self.assertIn(str(path), str(cm.exception)) + + def test_raises_error_if_multiple_matching_lines_found(self): + lines = ["blah"] * 100 + lines.append("aaa123bbb") + lines.append("ccc456ddd") + path = self.create_temp_file_with_lines(lines) + expr = re.compile(r"\D+(\d+)\D+") + + with self.assertRaises(firestore_patch.RegexMatchError) as cm: + firestore_patch.set_value(path, expr, "") + + self.assertIn("multiple lines matching", str(cm.exception).lower()) + self.assertIn(str(path), str(cm.exception)) + self.assertIn(expr.pattern, str(cm.exception)) + self.assertIn("line 101", str(cm.exception)) + self.assertIn("line 102", str(cm.exception)) + self.assertIn("123", str(cm.exception)) + self.assertIn("456", str(cm.exception)) + + +if __name__ == "__main__": + unittest.main() diff --git a/cmake/external/firestore_snappy.patch.txt b/cmake/external/firestore_snappy.patch.txt deleted file mode 100644 index 5a50404302..0000000000 --- a/cmake/external/firestore_snappy.patch.txt +++ /dev/null @@ -1,619 +0,0 @@ -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 29458bf13..7be37691d 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -227,6 +227,12 @@ if(NOT ZLIB_FOUND) - endif() - - -+# Snappy -+set(SNAPPY_BUILD_TESTS OFF CACHE BOOL "Firestore disabled") -+set(SNAPPY_BUILD_BENCHMARKS OFF CACHE BOOL "Firestore disabled") -+add_external_subdirectory(snappy) -+firebase_ios_add_alias(Snappy::Snappy snappy) -+ - # LevelDB - set(LEVELDB_BUILD_TESTS OFF CACHE BOOL "Firestore disabled") - set(LEVELDB_BUILD_BENCHMARKS OFF CACHE BOOL "Firestore disabled") -diff --git a/cmake/external/CMakeLists.txt b/cmake/external/CMakeLists.txt -index 2179633a8..c1de37b6d 100644 ---- a/cmake/external/CMakeLists.txt -+++ b/cmake/external/CMakeLists.txt -@@ -30,6 +30,7 @@ include(c-ares) - include(googletest) - include(GoogleUtilities) - include(grpc) -+include(snappy) - include(leveldb) - include(libfuzzer) - include(nanopb) -diff --git a/cmake/external/leveldb.cmake b/cmake/external/leveldb.cmake -index b71a77535..2556d7041 100644 ---- a/cmake/external/leveldb.cmake -+++ b/cmake/external/leveldb.cmake -@@ -13,20 +13,27 @@ - # limitations under the License. - - include(ExternalProject) -+include(FindPythonInterp) - - if(TARGET leveldb) - return() - endif() - --set(version 1.22) -+set(version 1.23) -+ -+ExternalProject_Get_property(snappy SOURCE_DIR) -+set(snappy_source_dir "${SOURCE_DIR}") -+ExternalProject_Get_property(snappy BINARY_DIR) -+set(snappy_binary_dir "${BINARY_DIR}") - - ExternalProject_Add( - leveldb - -+ DEPENDS snappy -+ - DOWNLOAD_DIR ${FIREBASE_DOWNLOAD_DIR} - DOWNLOAD_NAME leveldb-${version}.tar.gz - URL https://github.com/google/leveldb/archive/${version}.tar.gz -- URL_HASH SHA256=55423cac9e3306f4a9502c738a001e4a339d1a38ffbee7572d4a07d5d63949b2 - - PREFIX ${PROJECT_BINARY_DIR} - -@@ -34,6 +41,7 @@ ExternalProject_Add( - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" -+ PATCH_COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/leveldb_patch.py --snappy-source-dir ${snappy_source_dir} --snappy-binary-dir ${snappy_binary_dir} - - HTTP_HEADER "${EXTERNAL_PROJECT_HTTP_HEADER}" - ) -diff --git a/cmake/external/leveldb_patch.py b/cmake/external/leveldb_patch.py -new file mode 100644 -index 000000000..51a62d54a ---- /dev/null -+++ b/cmake/external/leveldb_patch.py -@@ -0,0 +1,144 @@ -+# Copyright 2022 Google LLC -+# -+# Licensed under the Apache License, Version 2.0 (the "License"); -+# you may not use this file except in compliance with the License. -+# You may obtain a copy of the License at -+# -+# http://www.apache.org/licenses/LICENSE-2.0 -+# -+# Unless required by applicable law or agreed to in writing, software -+# distributed under the License is distributed on an "AS IS" BASIS, -+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+# See the License for the specific language governing permissions and -+# limitations under the License. -+ -+""" -+Modify the CMakeLists.txt from LevelDb to staticly link Snappy compression -+support. -+""" -+ -+import argparse -+import dataclasses -+import os -+import pathlib -+from typing import Iterable, Sequence -+ -+ -+def main() -> None: -+ arg_parser = argparse.ArgumentParser() -+ arg_parser.add_argument("--snappy-source-dir", required=True) -+ arg_parser.add_argument("--snappy-binary-dir", required=True) -+ parsed_args = arg_parser.parse_args() -+ del arg_parser -+ snappy_source_dir = pathlib.Path(parsed_args.snappy_source_dir) -+ snappy_binary_dir = pathlib.Path(parsed_args.snappy_binary_dir) -+ del parsed_args -+ -+ cmakelists_txt_file = pathlib.Path("CMakeLists.txt") -+ with cmakelists_txt_file.open("rt", encoding="utf8") as f: -+ lines = tuple(f) -+ -+ patcher = CMakeListsPatcher( -+ lines, -+ os.path.abspath(__file__), -+ snappy_source_dir, -+ snappy_binary_dir, -+ ) -+ -+ patched_lines = tuple(patcher.patch()) -+ -+ with cmakelists_txt_file.open("wt", encoding="utf8") as f: -+ f.writelines(patched_lines) -+ -+ -+@dataclasses.dataclass(frozen=True) -+class LineComponents: -+ full: str -+ indent: str -+ line: str -+ eol: str -+ -+ -+class CMakeListsPatcher: -+ -+ SNAPPY_DETECT_LINE = \ -+ """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""" -+ SNAPPY_INCLUDE_LINE = \ -+ "target_include_directories(leveldb" -+ SNAPPY_LINK_LINE = \ -+ "target_link_libraries(leveldb snappy)" -+ -+ def __init__( -+ self, -+ lines: Sequence[str], -+ script_name: str, -+ snappy_source_dir: pathlib.Path, -+ snappy_binary_dir: pathlib.Path) -> None: -+ self.i = 0 -+ self.lines = lines -+ self.script_name = script_name -+ self.snappy_source_dir_str = snappy_source_dir.as_posix() -+ self.snappy_binary_dir_str = snappy_binary_dir.as_posix() -+ -+ def patch(self) -> Iterable[str]: -+ while self.i < len(self.lines): -+ full_line = self.lines[self.i] -+ line = self._split_line(full_line) -+ self.i += 1 -+ -+ if line.line == self.SNAPPY_DETECT_LINE: -+ yield from self._on_snappy_detect_line(line) -+ elif line.line == self.SNAPPY_INCLUDE_LINE: -+ yield full_line -+ yield from self._on_leveldb_include_start() -+ elif line.line == self.SNAPPY_LINK_LINE: -+ yield from self._on_leveldb_snappy_link_line(line) -+ else: -+ yield full_line -+ -+ def _begin_mod_line(self, mod_name: str) -> str: -+ return f"# BEGIN: {mod_name} modification by {self.script_name}" -+ -+ def _end_mod_line(self, mod_name: str) -> str: -+ return f"# END: {mod_name} modification by {self.script_name}" -+ -+ def _on_snappy_detect_line(self, line: LineComponents) -> Iterable[str]: -+ yield self._begin_mod_line("snappy_detect_line") + line.eol -+ yield line.indent + "# " + line.line + line.eol -+ yield line.indent + """set(HAVE_SNAPPY ON CACHE BOOL "")""" + line.eol -+ yield self._end_mod_line("snappy_detect_line") + line.eol -+ -+ def _on_leveldb_include_start(self) -> Iterable[str]: -+ line1 = self._split_line(self.lines[self.i]) -+ line2 = self._split_line(self.lines[self.i + 1]) -+ begin_mod_line = self._begin_mod_line("leveldb_include_start") -+ -+ if line1.line == begin_mod_line: -+ return -+ -+ yield begin_mod_line + line1.eol -+ yield line1.indent + "PRIVATE" + line1.eol -+ yield line2.indent + self.snappy_source_dir_str + line2.eol -+ yield line2.indent + self.snappy_binary_dir_str + line2.eol -+ yield self._end_mod_line("leveldb_include_start") + line1.eol -+ -+ def _on_leveldb_snappy_link_line(self, line: LineComponents) -> Iterable[str]: -+ yield self._begin_mod_line("leveldb_snappy_link_line") + line.eol -+ yield line.indent + "# " + line.line + line.eol -+ yield line.indent + f"target_link_libraries(leveldb Snappy::Snappy)" + line.eol -+ yield self._end_mod_line("leveldb_snappy_link_line") + line.eol -+ -+ def _split_line(self, line: str) -> LineComponents: -+ line_rstripped = line.rstrip() -+ eol = line[len(line_rstripped):] -+ line_stripped = line_rstripped.strip() -+ indent = line_rstripped[:len(line_rstripped) - len(line_stripped)] -+ return LineComponents(full=line, indent=indent, line=line_stripped, eol=eol) -+ -+ -+class LeveDbPatchException(Exception): -+ pass -+ -+ -+if __name__ == "__main__": -+ main() -diff --git a/cmake/external/leveldb_patch_test.py b/cmake/external/leveldb_patch_test.py -new file mode 100644 -index 000000000..b1d62526b ---- /dev/null -+++ b/cmake/external/leveldb_patch_test.py -@@ -0,0 +1,328 @@ -+# Copyright 2022 Google LLC -+# -+# Licensed under the Apache License, Version 2.0 (the "License"); -+# you may not use this file except in compliance with the License. -+# You may obtain a copy of the License at -+# -+# http://www.apache.org/licenses/LICENSE-2.0 -+# -+# Unless required by applicable law or agreed to in writing, software -+# distributed under the License is distributed on an "AS IS" BASIS, -+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+# See the License for the specific language governing permissions and -+# limitations under the License. -+ -+import leveldb_patch -+import pathlib -+import unittest -+ -+ -+class CMakeListsPatcherTest(unittest.TestCase): -+ -+ def setUp(self): -+ super().setUp() -+ self.sample_snappy_source_dir = pathlib.Path("a/b/snappy_source_dir") -+ self.sample_snappy_binary_dir = pathlib.Path("a/b/snappy_binary_dir") -+ -+ def test_snappy_detect_line_is_commented_and_replaced(self): -+ lines = ( -+ """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", -+ ) -+ patcher = leveldb_patch.CMakeListsPatcher( -+ lines, -+ "MyCoolScript", -+ self.sample_snappy_source_dir, -+ self.sample_snappy_binary_dir, -+ ) -+ -+ patched_lines = tuple(patcher.patch()) -+ -+ self.assertSequenceEqual(patched_lines, [ -+ "# BEGIN: snappy_detect_line modification by MyCoolScript", -+ """# check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", -+ """set(HAVE_SNAPPY ON CACHE BOOL "")""", -+ "# END: snappy_detect_line modification by MyCoolScript", -+ ]) -+ -+ def test_snappy_detect_line_has_indent_and_eol_preserved(self): -+ lines = ( -+ """ check_library_exists(snappy snappy_compress "" HAVE_SNAPPY) \n""", -+ ) -+ patcher = leveldb_patch.CMakeListsPatcher( -+ lines, -+ "MyCoolScript", -+ self.sample_snappy_source_dir, -+ self.sample_snappy_binary_dir, -+ ) -+ -+ patched_lines = tuple(patcher.patch()) -+ -+ self.assertSequenceEqual(patched_lines, [ -+ "# BEGIN: snappy_detect_line modification by MyCoolScript \n", -+ """ # check_library_exists(snappy snappy_compress "" HAVE_SNAPPY) \n""", -+ """ set(HAVE_SNAPPY ON CACHE BOOL "") \n""", -+ "# END: snappy_detect_line modification by MyCoolScript \n", -+ ]) -+ -+ def test_snappy_detect_line_does_not_affect_surrounding_lines(self): -+ lines = ( -+ "aaa", -+ "bbb", -+ """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", -+ "ccc", -+ "ddd", -+ ) -+ patcher = leveldb_patch.CMakeListsPatcher( -+ lines, -+ "MyCoolScript", -+ self.sample_snappy_source_dir, -+ self.sample_snappy_binary_dir, -+ ) -+ -+ patched_lines = tuple(patcher.patch()) -+ -+ self.assertSequenceEqual(patched_lines, [ -+ "aaa", -+ "bbb", -+ "# BEGIN: snappy_detect_line modification by MyCoolScript", -+ """# check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", -+ """set(HAVE_SNAPPY ON CACHE BOOL "")""", -+ "# END: snappy_detect_line modification by MyCoolScript", -+ "ccc", -+ "ddd", -+ ]) -+ -+ def test_snappy_include_is_amended(self): -+ lines = ( -+ "target_include_directories(leveldb", -+ "PUBLIC", -+ "path1", -+ "path2", -+ ")", -+ ) -+ patcher = leveldb_patch.CMakeListsPatcher( -+ lines, -+ script_name="MyCoolSript", -+ snappy_source_dir=pathlib.Path("a/b"), -+ snappy_binary_dir=pathlib.Path("c/d"), -+ ) -+ -+ patched_lines = tuple(patcher.patch()) -+ -+ self.assertSequenceEqual(patched_lines, [ -+ "target_include_directories(leveldb", -+ "# BEGIN: leveldb_include_start modification by MyCoolSript", -+ "PRIVATE", -+ "a/b", -+ "c/d", -+ "# END: leveldb_include_start modification by MyCoolSript", -+ "PUBLIC", -+ "path1", -+ "path2", -+ ")", -+ ]) -+ -+ def test_snappy_include_lines_adopt_indenting_and_eol_convention(self): -+ lines = ( -+ "target_include_directories(leveldb\n", -+ " PUBLIC \n", -+ " path1 \n", -+ " path2 \n", -+ ")\n", -+ ) -+ patcher = leveldb_patch.CMakeListsPatcher( -+ lines, -+ script_name="MyCoolSript", -+ snappy_source_dir=pathlib.Path("a/b"), -+ snappy_binary_dir=pathlib.Path("c/d"), -+ ) -+ -+ patched_lines = tuple(patcher.patch()) -+ -+ self.assertSequenceEqual(patched_lines, [ -+ "target_include_directories(leveldb\n", -+ "# BEGIN: leveldb_include_start modification by MyCoolSript \n", -+ " PRIVATE \n", -+ " a/b \n", -+ " c/d \n", -+ "# END: leveldb_include_start modification by MyCoolSript \n", -+ " PUBLIC \n", -+ " path1 \n", -+ " path2 \n", -+ ")\n", -+ ]) -+ -+ def test_snappy_include_line_does_not_affect_surrounding_lines(self): -+ lines = ( -+ "aaa", -+ "bbb", -+ "target_include_directories(leveldb", -+ "PUBLIC", -+ "path1", -+ "path2", -+ ")", -+ "ccc", -+ "ddd", -+ ) -+ patcher = leveldb_patch.CMakeListsPatcher( -+ lines, -+ script_name="MyCoolSript", -+ snappy_source_dir=pathlib.Path("a/b"), -+ snappy_binary_dir=pathlib.Path("c/d"), -+ ) -+ -+ patched_lines = tuple(patcher.patch()) -+ -+ self.assertSequenceEqual(patched_lines, [ -+ "aaa", -+ "bbb", -+ "target_include_directories(leveldb", -+ "# BEGIN: leveldb_include_start modification by MyCoolSript", -+ "PRIVATE", -+ "a/b", -+ "c/d", -+ "# END: leveldb_include_start modification by MyCoolSript", -+ "PUBLIC", -+ "path1", -+ "path2", -+ ")", -+ "ccc", -+ "ddd", -+ ]) -+ -+ def test_leveldb_snappy_link_line_is_commented_and_replaced(self): -+ lines = ( -+ "target_link_libraries(leveldb snappy)", -+ ) -+ patcher = leveldb_patch.CMakeListsPatcher( -+ lines, -+ script_name="MyCoolSript", -+ snappy_source_dir=pathlib.Path("a/b"), -+ snappy_binary_dir=pathlib.Path("c/d"), -+ ) -+ -+ patched_lines = tuple(patcher.patch()) -+ -+ self.assertSequenceEqual(patched_lines, [ -+ "# BEGIN: leveldb_snappy_link_line modification by MyCoolSript", -+ "# target_link_libraries(leveldb snappy)", -+ "target_link_libraries(leveldb Snappy::Snappy)", -+ "# END: leveldb_snappy_link_line modification by MyCoolSript", -+ ]) -+ -+ def test_leveldb_snappy_link_line_has_indent_and_eol_preserved(self): -+ lines = ( -+ " target_link_libraries(leveldb snappy) \n", -+ ) -+ patcher = leveldb_patch.CMakeListsPatcher( -+ lines, -+ script_name="MyCoolSript", -+ snappy_source_dir=pathlib.Path("a/b"), -+ snappy_binary_dir=pathlib.Path("c/d"), -+ ) -+ -+ patched_lines = tuple(patcher.patch()) -+ -+ self.assertSequenceEqual(patched_lines, [ -+ "# BEGIN: leveldb_snappy_link_line modification by MyCoolSript \n", -+ " # target_link_libraries(leveldb snappy) \n", -+ " target_link_libraries(leveldb Snappy::Snappy) \n", -+ "# END: leveldb_snappy_link_line modification by MyCoolSript \n", -+ ]) -+ -+ def test_leveldb_snappy_link_line_does_not_affect_surrounding_lines(self): -+ lines = ( -+ "aaa", -+ "bbb", -+ "target_link_libraries(leveldb snappy)", -+ "ccc", -+ "ddd", -+ ) -+ patcher = leveldb_patch.CMakeListsPatcher( -+ lines, -+ script_name="MyCoolSript", -+ snappy_source_dir=pathlib.Path("a/b"), -+ snappy_binary_dir=pathlib.Path("c/d"), -+ ) -+ -+ patched_lines = tuple(patcher.patch()) -+ -+ self.assertSequenceEqual(patched_lines, [ -+ "aaa", -+ "bbb", -+ "# BEGIN: leveldb_snappy_link_line modification by MyCoolSript", -+ "# target_link_libraries(leveldb snappy)", -+ "target_link_libraries(leveldb Snappy::Snappy)", -+ "# END: leveldb_snappy_link_line modification by MyCoolSript", -+ "ccc", -+ "ddd", -+ ]) -+ -+ def test_all_patches_combined(self): -+ lines = ( -+ """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", -+ "target_include_directories(leveldb", -+ "PUBLIC", -+ "path1", -+ ")", -+ "target_link_libraries(leveldb snappy)", -+ ) -+ -+ patcher = leveldb_patch.CMakeListsPatcher( -+ lines, -+ script_name="MyCoolSript", -+ snappy_source_dir=pathlib.Path("a/b"), -+ snappy_binary_dir=pathlib.Path("c/d"), -+ ) -+ patched_lines = tuple(patcher.patch()) -+ -+ self.assertSequenceEqual(patched_lines, [ -+ "# BEGIN: snappy_detect_line modification by MyCoolSript", -+ """# check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)""", -+ """set(HAVE_SNAPPY ON CACHE BOOL "")""", -+ "# END: snappy_detect_line modification by MyCoolSript", -+ "target_include_directories(leveldb", -+ "# BEGIN: leveldb_include_start modification by MyCoolSript", -+ "PRIVATE", -+ "a/b", -+ "c/d", -+ "# END: leveldb_include_start modification by MyCoolSript", -+ "PUBLIC", -+ "path1", -+ ")", -+ "# BEGIN: leveldb_snappy_link_line modification by MyCoolSript", -+ "# target_link_libraries(leveldb snappy)", -+ "target_link_libraries(leveldb Snappy::Snappy)", -+ "# END: leveldb_snappy_link_line modification by MyCoolSript", -+ ]) -+ -+ def test_idempotence(self): -+ lines = ( -+ """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)\n""", -+ "target_include_directories(leveldb", -+ "PUBLIC", -+ "path1", -+ ")", -+ "target_link_libraries(leveldb snappy)", -+ ) -+ -+ patcher1 = leveldb_patch.CMakeListsPatcher( -+ lines, -+ script_name="MyCoolSript", -+ snappy_source_dir=pathlib.Path("a/b"), -+ snappy_binary_dir=pathlib.Path("c/d"), -+ ) -+ patched_lines1 = tuple(patcher1.patch()) -+ patcher2 = leveldb_patch.CMakeListsPatcher( -+ patched_lines1, -+ script_name="MyCoolSript", -+ snappy_source_dir=pathlib.Path("a/b"), -+ snappy_binary_dir=pathlib.Path("c/d"), -+ ) -+ patched_lines2 = tuple(patcher2.patch()) -+ -+ self.assertSequenceEqual(patched_lines1, patched_lines2) -+ -+ -+if __name__ == "__main__": -+ unittest.main() -diff --git a/cmake/external/snappy.cmake b/cmake/external/snappy.cmake -new file mode 100644 -index 000000000..9f25c03d0 ---- /dev/null -+++ b/cmake/external/snappy.cmake -@@ -0,0 +1,40 @@ -+# Copyright 2022 Google LLC -+# -+# Licensed under the Apache License, Version 2.0 (the "License"); -+# you may not use this file except in compliance with the License. -+# You may obtain a copy of the License at -+# -+# http://www.apache.org/licenses/LICENSE-2.0 -+# -+# Unless required by applicable law or agreed to in writing, software -+# distributed under the License is distributed on an "AS IS" BASIS, -+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+# See the License for the specific language governing permissions and -+# limitations under the License. -+ -+include(ExternalProject) -+ -+if(TARGET snappy) -+ return() -+endif() -+ -+set(version 1.1.9) -+ -+ExternalProject_Add( -+ snappy -+ -+ DOWNLOAD_DIR ${FIREBASE_DOWNLOAD_DIR} -+ DOWNLOAD_NAME snappy-${version}.tar.gz -+ URL https://github.com/google/snappy/archive/refs/tags/${version}.tar.gz -+ URL_HASH SHA256=75c1fbb3d618dd3a0483bff0e26d0a92b495bbe5059c8b4f1c962b478b6e06e7 -+ -+ PREFIX ${PROJECT_BINARY_DIR} -+ -+ CONFIGURE_COMMAND "" -+ BUILD_COMMAND "" -+ INSTALL_COMMAND "" -+ TEST_COMMAND "" -+ PATCH_COMMAND patch -Np1 -i ${CMAKE_CURRENT_LIST_DIR}/snappy.patch -+ -+ HTTP_HEADER "${EXTERNAL_PROJECT_HTTP_HEADER}" -+) -diff --git a/cmake/external/snappy.patch b/cmake/external/snappy.patch -new file mode 100644 -index 000000000..28bfb0837 ---- /dev/null -+++ b/cmake/external/snappy.patch -@@ -0,0 +1,12 @@ -+diff -Naur snappy/snappy.cc snappy_patched/snappy.cc -+--- snappy/snappy.cc 2022-04-12 20:44:55.000000000 -0400 -++++ snappy_patched/snappy.cc 2022-04-12 20:47:05.000000000 -0400 -+@@ -1014,7 +1014,7 @@ -+ } -+ -+ SNAPPY_ATTRIBUTE_ALWAYS_INLINE -+-size_t AdvanceToNextTag(const uint8_t** ip_p, size_t* tag) { -++inline size_t AdvanceToNextTag(const uint8_t** ip_p, size_t* tag) { -+ const uint8_t*& ip = *ip_p; -+ // This section is crucial for the throughput of the decompression loop. -+ // The latency of an iteration is fundamentally constrained by the diff --git a/cmake/external/leveldb.cmake b/cmake/external/leveldb.cmake index 18970659a4..a8a154bb4e 100644 --- a/cmake/external/leveldb.cmake +++ b/cmake/external/leveldb.cmake @@ -26,6 +26,7 @@ ExternalProject_Add( DOWNLOAD_DIR ${FIREBASE_DOWNLOAD_DIR} DOWNLOAD_NAME leveldb-${version}.tar.gz URL https://github.com/google/leveldb/archive/${version}.tar.gz + URL_HASH SHA256=9a37f8a6174f09bd622bc723b55881dc541cd50747cbd08831c2a82d620f6d76 PREFIX ${PROJECT_BINARY_DIR} diff --git a/firestore/integration_test_internal/CMakeLists.txt b/firestore/integration_test_internal/CMakeLists.txt index d9d7d20849..96c76bd824 100644 --- a/firestore/integration_test_internal/CMakeLists.txt +++ b/firestore/integration_test_internal/CMakeLists.txt @@ -333,6 +333,13 @@ else() ${FIREBASE_INTEGRATION_TEST_SRCS} ) + # Set a preprocessor define so that tests can distinguish between having been + # built by Xcode vs. cmake. + target_compile_definitions(${integration_test_target_name} + PRIVATE + FIREBASE_TESTS_BUILT_BY_CMAKE + ) + if(APPLE) set(ADDITIONAL_LIBS gssapi_krb5 diff --git a/firestore/integration_test_internal/src/leveldb_snappy_test.cc b/firestore/integration_test_internal/src/leveldb_snappy_test.cc index 33f2247cd8..781481b763 100644 --- a/firestore/integration_test_internal/src/leveldb_snappy_test.cc +++ b/firestore/integration_test_internal/src/leveldb_snappy_test.cc @@ -20,231 +20,8 @@ #error "This test should not be included in Android." #endif -#include -#include -#include -#include -#include - -#include "Firestore/core/src/local/leveldb_util.h" -#include "Firestore/core/src/util/filesystem.h" -#include "Firestore/core/src/util/path.h" - -#include "firebase_test_framework.h" -#include "gtest/gtest.h" -#include "leveldb/db.h" - -// TODO(dconeybe) Reference this test in the iOS SDK instead of making a -// copy of it here in the C++ SDK. - -namespace { - -using ::firebase::firestore::local::ConvertStatus; -using ::firebase::firestore::util::Filesystem; -using ::firebase::firestore::util::Path; - -// Creates a LevelDb database that uses Snappy compression for at least one of -// its blocks. Attempting to iterate over this database using a LevelDb library -// that does not have Snappy compression compiled in will return a failed status -// with reason "corruption". -Path CreateLevelDbDatabaseThatUsesSnappyCompression(); - -// This test ensures that we don't accidentally regress having added in Snappy -// compression support (https://github.com/firebase/firebase-ios-sdk/pull/9596). -TEST(LevelDbSnappy, LevelDbHasSnappySupportCompiledIn) { - // Do not run this test on iOS because LevelDb in iOS does not support Snappy. - SKIP_TEST_ON_IOS; - - Path leveldb_path = CreateLevelDbDatabaseThatUsesSnappyCompression(); - if (HasFatalFailure()) return; - - leveldb::Options options; - options.create_if_missing = false; - - std::unique_ptr db; - { - leveldb::DB* db_ptr; - leveldb::Status status = - leveldb::DB::Open(options, leveldb_path.ToUtf8String(), &db_ptr); - ASSERT_TRUE(status.ok()); - db.reset(db_ptr); - } - - // One of the assertions below will fail when LevelDb attempts to read a block - // that is compressed with Snappy and Snappy compression support is not - // compiled in. - std::unique_ptr it( - db->NewIterator(leveldb::ReadOptions())); - for (it->SeekToFirst(); it->Valid(); it->Next()) { - ASSERT_TRUE(it->status().ok()) << ConvertStatus(it->status()); - } - ASSERT_TRUE(it->status().ok()) << ConvertStatus(it->status()); -} - -const std::array LevelDbSnappyFile_000005_ldb{ - 0x84, 0x03, 0x80, 0x00, 0x42, 0x00, 0x85, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x5F, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x01, 0x8B, 0x43, 0x6F, - 0x6C, 0x41, 0x2F, 0x44, 0x6F, 0x63, 0x41, 0x2F, 0x43, 0x6F, 0x6C, 0x42, - 0x01, 0x0A, 0x68, 0x42, 0x7C, 0x66, 0x3A, 0x7C, 0x6F, 0x62, 0x3A, 0x5F, - 0x5F, 0x6E, 0x61, 0x6D, 0x65, 0x5F, 0x5F, 0x61, 0x73, 0x63, 0x00, 0x01, - 0x8C, 0x82, 0x80, 0x01, 0x07, 0x00, 0x05, 0x01, 0x08, 0x01, 0x13, 0x50, - 0x11, 0x3E, 0x01, 0x16, 0x00, 0x0A, 0x05, 0x15, 0xF0, 0x3C, 0x00, 0x08, - 0x02, 0x20, 0x05, 0x32, 0x4A, 0x12, 0x48, 0x70, 0x72, 0x6F, 0x6A, 0x65, - 0x63, 0x74, 0x73, 0x2F, 0x54, 0x65, 0x73, 0x74, 0x54, 0x65, 0x72, 0x6D, - 0x69, 0x6E, 0x61, 0x74, 0x65, 0x2F, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, - 0x73, 0x65, 0x73, 0x2F, 0x28, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, - 0x29, 0x2F, 0x64, 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x01, - 0x7B, 0x3E, 0x85, 0x00, 0x0C, 0x0D, 0x07, 0x50, 0x08, 0x15, 0x5A, 0x00, - 0x03, 0xFE, 0x5A, 0x00, 0x2E, 0x5A, 0x00, 0x38, 0x07, 0x12, 0x06, 0x5F, - 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x00, 0x01, 0x80, 0x01, 0x0B, 0x11, - 0x65, 0x1C, 0x10, 0x05, 0x20, 0x01, 0x12, 0x07, 0x06, 0x09, 0x15, 0x10, - 0x00, 0x03, 0x01, 0x10, 0x04, 0x00, 0x01, 0x09, 0x10, 0x24, 0x01, 0x12, - 0x01, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x01, 0x35, 0x00, 0x06, - 0x09, 0x15, 0x10, 0x37, 0x0C, 0x07, 0x01, 0x05, 0x09, 0x0B, 0x10, 0x36, - 0x0C, 0x07, 0x01, 0x04, 0x09, 0x0B, 0x10, 0x35, 0x0C, 0x07, 0x01, 0x03, - 0x09, 0x0B, 0x4C, 0x34, 0x0C, 0x07, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x2C, 0x6E, 0xE0, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0xC0, 0xF2, 0xA1, 0xB0, 0x00, 0x09, 0x03, 0x86, 0x01, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x58, 0xC2, 0x94, 0x06, 0x8C, 0x02, 0x08, - 0x99, 0x02, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x57, 0xFB, 0x80, 0x8B, 0x24, 0x75, 0x47, 0xDB, -}; -const std::array LevelDbSnappyFile_000017_ldb{ - 0x00, 0x14, 0x50, 0x85, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x01, - 0x8C, 0x82, 0x80, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x02, 0x20, 0x0A, 0x32, 0x4A, 0x12, 0x48, 0x70, 0x72, 0x6F, 0x6A, 0x65, - 0x63, 0x74, 0x73, 0x2F, 0x54, 0x65, 0x73, 0x74, 0x54, 0x65, 0x72, 0x6D, - 0x69, 0x6E, 0x61, 0x74, 0x65, 0x2F, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, - 0x73, 0x65, 0x73, 0x2F, 0x28, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, - 0x29, 0x2F, 0x64, 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x2F, - 0x43, 0x6F, 0x6C, 0x41, 0x2F, 0x44, 0x6F, 0x63, 0x41, 0x2F, 0x43, 0x6F, - 0x6C, 0x42, 0x2F, 0x44, 0x6F, 0x63, 0x42, 0x07, 0x12, 0x06, 0x5F, 0x67, - 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x00, 0x01, 0x80, 0x01, 0x0D, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x10, 0x0A, 0x20, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCD, 0xE0, 0x39, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF2, 0xA1, 0xB0, - 0x00, 0x09, 0x03, 0x86, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x00, 0x8A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0xE4, 0xA7, 0x7E, 0x74, 0x8F, 0x01, 0x08, 0x9C, 0x01, 0x17, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0xFB, 0x80, 0x8B, - 0x24, 0x75, 0x47, 0xDB, -}; -const std::array LevelDbSnappyFile_000085_ldb{}; -const std::array LevelDbSnappyFile_CURRENT{ - 0x4D, 0x41, 0x4E, 0x49, 0x46, 0x45, 0x53, 0x54, - 0x2D, 0x30, 0x30, 0x30, 0x30, 0x38, 0x34, 0x0A, -}; -const std::array LevelDbSnappyFile_LOG_old{ - 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, 0x31, - 0x31, 0x3A, 0x33, 0x39, 0x3A, 0x34, 0x36, 0x2E, 0x32, 0x35, 0x37, 0x32, - 0x35, 0x31, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x35, 0x33, - 0x31, 0x34, 0x30, 0x30, 0x30, 0x20, 0x52, 0x65, 0x63, 0x6F, 0x76, 0x65, - 0x72, 0x69, 0x6E, 0x67, 0x20, 0x6C, 0x6F, 0x67, 0x20, 0x23, 0x38, 0x31, - 0x0A, 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, - 0x31, 0x31, 0x3A, 0x33, 0x39, 0x3A, 0x34, 0x36, 0x2E, 0x33, 0x30, 0x34, - 0x35, 0x35, 0x32, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x35, - 0x33, 0x31, 0x34, 0x30, 0x30, 0x30, 0x20, 0x44, 0x65, 0x6C, 0x65, 0x74, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x33, 0x20, 0x23, 0x38, 0x30, - 0x0A, 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, - 0x31, 0x31, 0x3A, 0x33, 0x39, 0x3A, 0x34, 0x36, 0x2E, 0x33, 0x30, 0x35, - 0x30, 0x36, 0x34, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x35, - 0x33, 0x31, 0x34, 0x30, 0x30, 0x30, 0x20, 0x44, 0x65, 0x6C, 0x65, 0x74, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x30, 0x20, 0x23, 0x38, 0x31, - 0x0A, -}; -const std::array LevelDbSnappyFile_LOG{ - 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, 0x31, - 0x31, 0x3A, 0x35, 0x36, 0x3A, 0x35, 0x36, 0x2E, 0x34, 0x39, 0x33, 0x31, - 0x34, 0x32, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x61, 0x32, - 0x35, 0x34, 0x30, 0x30, 0x30, 0x20, 0x52, 0x65, 0x63, 0x6F, 0x76, 0x65, - 0x72, 0x69, 0x6E, 0x67, 0x20, 0x6C, 0x6F, 0x67, 0x20, 0x23, 0x38, 0x33, - 0x0A, 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, - 0x31, 0x31, 0x3A, 0x35, 0x36, 0x3A, 0x35, 0x36, 0x2E, 0x35, 0x33, 0x34, - 0x37, 0x34, 0x35, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x61, - 0x32, 0x35, 0x34, 0x30, 0x30, 0x30, 0x20, 0x44, 0x65, 0x6C, 0x65, 0x74, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x33, 0x20, 0x23, 0x38, 0x32, - 0x0A, 0x32, 0x30, 0x32, 0x32, 0x2F, 0x30, 0x34, 0x2F, 0x30, 0x34, 0x2D, - 0x31, 0x31, 0x3A, 0x35, 0x36, 0x3A, 0x35, 0x36, 0x2E, 0x35, 0x33, 0x35, - 0x32, 0x34, 0x32, 0x20, 0x30, 0x78, 0x37, 0x30, 0x30, 0x30, 0x30, 0x61, - 0x32, 0x35, 0x34, 0x30, 0x30, 0x30, 0x20, 0x44, 0x65, 0x6C, 0x65, 0x74, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x30, 0x20, 0x23, 0x38, 0x33, - 0x0A, -}; -const std::array LevelDbSnappyFile_MANIFEST_000084{ - 0x45, 0x63, 0x9F, 0xDD, 0xAC, 0x00, 0x01, 0x01, 0x1A, 0x6C, 0x65, 0x76, - 0x65, 0x6C, 0x64, 0x62, 0x2E, 0x42, 0x79, 0x74, 0x65, 0x77, 0x69, 0x73, - 0x65, 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6F, 0x72, 0x07, - 0x00, 0x05, 0xE5, 0x02, 0x42, 0x85, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5F, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x01, 0x8B, 0x43, 0x6F, 0x6C, - 0x41, 0x2F, 0x44, 0x6F, 0x63, 0x41, 0x2F, 0x43, 0x6F, 0x6C, 0x42, 0x2F, - 0x44, 0x6F, 0x63, 0x42, 0x7C, 0x66, 0x3A, 0x7C, 0x6F, 0x62, 0x3A, 0x5F, - 0x5F, 0x6E, 0x61, 0x6D, 0x65, 0x5F, 0x5F, 0x61, 0x73, 0x63, 0x00, 0x01, - 0x8C, 0x82, 0x80, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, - 0x85, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x00, 0x01, 0x80, 0x01, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x11, 0xE8, 0x01, - 0x14, 0x85, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x01, 0x8C, 0x82, - 0x80, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x85, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x5F, 0x67, 0x6C, 0x6F, 0x62, 0x61, 0x6C, - 0x00, 0x01, 0x80, 0x01, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB1, - 0x03, 0xAC, 0xBA, 0x08, 0x00, 0x01, 0x02, 0x55, 0x09, 0x00, 0x03, 0x56, - 0x04, 0x0D, -}; - -template -void WriteFile(const Path& dir, - const std::string& file_name, - const T& data_array) { - Filesystem* fs = Filesystem::Default(); - { - auto status = fs->RecursivelyCreateDir(dir); - if (!status.ok()) { - FAIL() << "Creating directory failed: " << dir.ToUtf8String() << " (" - << status.error_message() << ")"; - } - } - - Path file = dir.AppendUtf8(file_name); - std::ofstream out_file(file.native_value(), std::ios::binary); - if (!out_file) { - FAIL() << "Unable to open file for writing: " << file.ToUtf8String(); - } - - out_file.write(reinterpret_cast(data_array.data()), - data_array.size()); - out_file.close(); - if (!out_file) { - FAIL() << "Writing to file failed: " << file.ToUtf8String(); - } -} - -Path LevelDbDir() { - Filesystem* fs = Filesystem::Default(); - Path dir = fs->TempDir().AppendUtf8("PersistenceTesting"); - - // Delete the directory first to ensure isolation between runs. - auto status = fs->RecursivelyRemove(dir); - EXPECT_TRUE(status.ok()) << "Failed to clean up leveldb in dir " - << dir.ToUtf8String() << ": " << status.ToString(); - - return dir; -} - -Path CreateLevelDbDatabaseThatUsesSnappyCompression() { - Path leveldb_dir = LevelDbDir(); - WriteFile(leveldb_dir, "000005.ldb", LevelDbSnappyFile_000005_ldb); - WriteFile(leveldb_dir, "000017.ldb", LevelDbSnappyFile_000017_ldb); - WriteFile(leveldb_dir, "000085.ldb", LevelDbSnappyFile_000085_ldb); - WriteFile(leveldb_dir, "CURRENT", LevelDbSnappyFile_CURRENT); - WriteFile(leveldb_dir, "LOG.old", LevelDbSnappyFile_LOG_old); - WriteFile(leveldb_dir, "LOG", LevelDbSnappyFile_LOG); - WriteFile(leveldb_dir, "MANIFEST-000084", LevelDbSnappyFile_MANIFEST_000084); - return leveldb_dir; -} - -} // namespace +// Just re-use the unit test from the iOS SDK. +// TODO(dconeybe) Import ALL the unit tests from the iOS SDK by adding them +// to the CMakeLists.txt in the parent directory of this file. That way we can +// run all of the tests from the iOS SDK on each platform targeted by this repo. +#include "Firestore/core/test/unit/local/leveldb_snappy_test.cc" diff --git a/firestore/src/main/query_main.cc b/firestore/src/main/query_main.cc index 9ce4060fcc..d11c14002f 100644 --- a/firestore/src/main/query_main.cc +++ b/firestore/src/main/query_main.cc @@ -227,7 +227,7 @@ core::Bound QueryInternal::ToBound( components->values[i] = *DeepClone(*value).release(); } - return core::Bound::FromValue(std::move(components), IsBefore(bound_pos)); + return core::Bound::FromValue(std::move(components), IsInclusive(bound_pos)); } core::Bound QueryInternal::ToBound( @@ -261,7 +261,7 @@ core::Bound QueryInternal::ToBound( } } - return core::Bound::FromValue(std::move(components), IsBefore(bound_pos)); + return core::Bound::FromValue(std::move(components), IsInclusive(bound_pos)); } Message QueryInternal::ConvertDocumentId( @@ -327,14 +327,14 @@ api::Query QueryInternal::CreateQueryWithBound(BoundPosition bound_pos, FIRESTORE_UNREACHABLE(); } -bool QueryInternal::IsBefore(BoundPosition bound_pos) { +bool QueryInternal::IsInclusive(BoundPosition bound_pos) { switch (bound_pos) { case BoundPosition::kStartAt: - case BoundPosition::kEndBefore: + case BoundPosition::kEndAt: return true; case BoundPosition::kStartAfter: - case BoundPosition::kEndAt: + case BoundPosition::kEndBefore: return false; } diff --git a/firestore/src/main/query_main.h b/firestore/src/main/query_main.h index bc430aae9b..4f3e89016d 100644 --- a/firestore/src/main/query_main.h +++ b/firestore/src/main/query_main.h @@ -23,6 +23,7 @@ #include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h" #include "Firestore/core/src/api/query_core.h" #include "Firestore/core/src/core/bound.h" +#include "Firestore/core/src/core/field_filter.h" #include "Firestore/core/src/core/order_by.h" #include "Firestore/core/src/core/query.h" #include "Firestore/core/src/nanopb/message.h" @@ -182,7 +183,7 @@ class QueryInternal { kEndAt, }; - using Operator = core::Filter::Operator; + using Operator = core::FieldFilter::Operator; Query Where(const FieldPath& field, Operator op, @@ -200,7 +201,7 @@ class QueryInternal { const nanopb::Message& from, const core::Query& internal_query) const; - static bool IsBefore(BoundPosition bound_pos); + static bool IsInclusive(BoundPosition bound_pos); core::Bound ToBound(BoundPosition bound_pos, const DocumentSnapshot& public_snapshot) const; diff --git a/firestore/src/main/user_data_converter_main.cc b/firestore/src/main/user_data_converter_main.cc index a87ae70c42..0de008dcce 100644 --- a/firestore/src/main/user_data_converter_main.cc +++ b/firestore/src/main/user_data_converter_main.cc @@ -315,7 +315,7 @@ nanopb::Message UserDataConverter::ParseArray( for (size_t i = 0; i != input.size(); ++i) { auto parsed = ParseData(input[i], context.ChildContext(i)); if (!parsed) { - parsed = NullValue(); + parsed = Message(NullValue()); } result->array_value.values[i] = *parsed->release(); }