diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index 3f295a2cb..5f1c050c8 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -135,6 +135,8 @@ jobs: run: make ios-uitest # size test + - name: Copy example config + run: cp bazel/example_config.bzl bazel/config.bzl - name: Build app for size test & output size working-directory: ./ diff --git a/WORKSPACE b/WORKSPACE index aee010ba2..b57779006 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -48,3 +48,12 @@ load( ) xcodeproj_rules_dependencies() + +load( + "@build_bazel_rules_apple//apple:apple.bzl", + "provisioning_profile_repository", +) + +provisioning_profile_repository( + name = "local_provisioning_profiles", +) diff --git a/bazel/flags.bzl b/bazel/flags.bzl index 1a8d6f535..301e03c04 100644 --- a/bazel/flags.bzl +++ b/bazel/flags.bzl @@ -11,6 +11,7 @@ WARNING_FLAGS = [ "-Wno-unused-parameter", "-Wno-unused-variable", "-Wno-variadic-macros", + "-Wno-unknown-pragmas", ] + select({ "//:ios": [ "-Wno-newline-eof", diff --git a/metrics/BUILD.bazel b/metrics/BUILD.bazel index 42fbba6f1..d46427c1d 100644 --- a/metrics/BUILD.bazel +++ b/metrics/BUILD.bazel @@ -1,18 +1,26 @@ filegroup( name = "render-test-files", - srcs = glob([ - "cache-style.db", + srcs = glob( + [ + "integration/**", + "tests/**", + "linux-gcc8-release/**", + "expectations/platform-all/**", + "ignores/**", + "ios-render-test-runner/**", # iOS only + "expectations/platform-ios/**", # iOS only + ], + allow_empty = False, + ) + [ "cache-metrics.db", - "integration/**", - "tests/**", - "linux-gcc8-release/**", - "expectations/platform-all/**", - "ignores/**", - ]) + [ + "cache-style.db", + "ios-render-test-runner-metrics.json", # iOS only + "ios-render-test-runner-style.json", # iOS only "linux-gcc8-release-metrics.json", "linux-gcc8-release-style.json", ], visibility = [ - "//render-test:__pkg__", + "//platform/ios/test/common:__pkg__", + "//render-test:__subpackages__", ], ) diff --git a/platform/ios/BUILD.bazel b/platform/ios/BUILD.bazel index 1eebed165..be9412980 100644 --- a/platform/ios/BUILD.bazel +++ b/platform/ios/BUILD.bazel @@ -1,13 +1,15 @@ -load("@build_bazel_rules_apple//apple:apple.bzl", "apple_static_xcframework", "apple_xcframework") +load("@build_bazel_rules_apple//apple:apple.bzl", "apple_static_xcframework", "apple_xcframework", "local_provisioning_profile") load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application", "ios_framework") load("@build_bazel_rules_apple//apple:resources.bzl", "apple_resource_bundle") load("//bazel:flags.bzl", "CPP_FLAGS", "MAPLIBRE_FLAGS", "WARNING_FLAGS") load( "@rules_xcodeproj//xcodeproj:defs.bzl", "top_level_target", + "xcode_provisioning_profile", "xcodeproj", ) load("bazel/macros.bzl", "info_plist") +load("bazel/config.bzl", "APPLE_MOBILE_PROVISIONING_PROFILE_TEAM_ID") # This list of public headers is from what Mapbox vendors with their dynamic framework # If we forget one the build will fail, and if we add too many there will be a warning @@ -460,6 +462,7 @@ ios_application( ], infoplists = ["platform/ios/app/Info.plist"], minimum_os_version = "12.0", + provisioning_profile = "xcode_profile", resources = _IOS_APPLICATION_RESOURCES, visibility = ["//visibility:public"], deps = [ @@ -502,15 +505,39 @@ genrule( """, ) +local_provisioning_profile( + name = "provisioning_profile", + profile_name = "iOS Team Provisioning Profile: *", + team_id = APPLE_MOBILE_PROVISIONING_PROFILE_TEAM_ID, +) + +xcode_provisioning_profile( + name = "xcode_profile", + managed_by_xcode = True, + provisioning_profile = ":provisioning_profile", + visibility = ["//visibility:public"], +) + xcodeproj( name = "xcodeproj", + build_mode = "xcode", minimum_xcode_version = "14", project_name = "MapLibre", tags = ["manual"], top_level_targets = [ top_level_target( "App", - target_environments = ["simulator"], + target_environments = [ + "simulator", + "device", + ], + ), + top_level_target( + "//render-test/ios:RenderTest", + target_environments = [ + "simulator", + "device", + ], ), ], ) diff --git a/platform/ios/bazel/.gitignore b/platform/ios/bazel/.gitignore new file mode 100644 index 000000000..b1053f241 --- /dev/null +++ b/platform/ios/bazel/.gitignore @@ -0,0 +1 @@ +/config.bzl \ No newline at end of file diff --git a/platform/ios/bazel/example_config.bzl b/platform/ios/bazel/example_config.bzl new file mode 100644 index 000000000..54da9a270 --- /dev/null +++ b/platform/ios/bazel/example_config.bzl @@ -0,0 +1 @@ +APPLE_MOBILE_PROVISIONING_PROFILE_TEAM_ID = "TC45MCF93C" diff --git a/platform/ios/test/common/BUILD.bazel b/platform/ios/test/common/BUILD.bazel new file mode 100644 index 000000000..ef7df159f --- /dev/null +++ b/platform/ios/test/common/BUILD.bazel @@ -0,0 +1,48 @@ +load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application") + +cc_library( + name = "ios_test_runner_lib", + srcs = [ + "ios_test_runner.hpp", + ], + includes = [ + ".", + ], + visibility = [ + "//render-test/ios:__pkg__", + ], +) + +objc_library( + name = "render_test_app_srcs", + srcs = [ + "AppDelegate.h", + "AppDelegate.m", + "main.m", + ], + data = [ + "LaunchScreen.storyboard", + "Main.storyboard", + ], + deps = [ + "ios_test_runner_lib", + ], +) + +ios_application( + name = "RenderTestApp", + bundle_id = "org.maplibre.RenderTestApp", + families = ["iphone"], + infoplists = [ + "//render-test/ios:Info.plist", + ], + minimum_os_version = "12.0", + provisioning_profile = "//platform/ios:xcode_profile", + visibility = [ + "//visibility:public", + ], + deps = [ + "render_test_app_srcs", + "//render-test/ios:render_test_ios_src", + ], +) diff --git a/platform/ios/test/common/iosTestRunner.mm b/platform/ios/test/common/iosTestRunner.mm index b1efa74e6..67eca9d1d 100644 --- a/platform/ios/test/common/iosTestRunner.mm +++ b/platform/ios/test/common/iosTestRunner.mm @@ -25,6 +25,7 @@ NSError *error; NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *bundleRoot = [[NSBundle mainBundle] bundlePath]; + NSArray *bundleContents = [fileManager contentsOfDirectoryAtPath: bundleRoot error: &error]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDir = [paths objectAtIndex: 0]; diff --git a/render-test/BUILD.bazel b/render-test/BUILD.bazel index db6a967a6..2c2be3bdb 100644 --- a/render-test/BUILD.bazel +++ b/render-test/BUILD.bazel @@ -10,11 +10,12 @@ cc_library( ], visibility = [ "//platform/default:__pkg__", + "//render-test/ios:__pkg__", ], ) -cc_test( - name = "render-test", +cc_library( + name = "render-test-srcs", srcs = glob( [ "*.cpp", @@ -23,21 +24,33 @@ cc_test( ) + [ "include/mbgl/render_test.hpp", ], + copts = CPP_FLAGS + MAPLIBRE_FLAGS, + includes = [ + "include", + ], + visibility = [ + "//render-test/ios:__pkg__", + ], + deps = [ + "//expression-test:test_runner_common", + ] + select({ + "//:ios": ["//platform/ios:sdk"], + "//:linux": ["//platform/linux:impl"], + }), +) + +cc_test( + name = "render-test", args = [ "--manifestPath", "metrics/linux-gcc8-release-style.json", ], - copts = CPP_FLAGS + MAPLIBRE_FLAGS, data = [ "//metrics:render-test-files", ], - includes = [ - "include", - ], deps = [ - "//expression-test:test_runner_common", + "render-test-srcs", "//platform/default:render-test-bin", - "//platform/linux:impl", "//vendor:mapbox-base", ], ) diff --git a/render-test/ios/BUILD.bazel b/render-test/ios/BUILD.bazel new file mode 100644 index 000000000..18afc682f --- /dev/null +++ b/render-test/ios/BUILD.bazel @@ -0,0 +1,66 @@ +load("//bazel:flags.bzl", "CPP_FLAGS", "MAPLIBRE_FLAGS") +load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test") +load("@build_bazel_rules_apple//apple:resources.bzl", "apple_resource_bundle") + +apple_resource_bundle( + name = "TestData", + infoplists = [ + "Empty.plist", + ], + structured_resources = [ + "//metrics:render-test-files", + ], +) + +objc_library( + name = "tests", + testonly = True, + srcs = [ + "tests/Tests.m", + ], + deps = [ + "render_test_ios_src", + ], +) + +objc_library( + name = "render_test_ios_src", + srcs = [ + "iosTestRunner.h", + "iosTestRunner.mm", + "ios_test_runner.cpp", + ], + copts = CPP_FLAGS + MAPLIBRE_FLAGS, + data = [ + "TestData", + ], + includes = [ + ".", + ], + visibility = [ + "//platform/ios/test/common:__pkg__", + ], + deps = [ + "//:mbgl-core", + "//platform/ios/test/common:ios_test_runner_lib", + "//render-test:render-test-lib", + "//render-test:render-test-srcs", + "//vendor:zip-archive", + ], +) + +ios_unit_test( + name = "RenderTest", + minimum_os_version = "12.0", + provisioning_profile = "//platform/ios:xcode_profile", + runner = "@build_bazel_rules_apple//apple/testing/default_runner:ios_xctestrun_ordered_runner", + test_host = "//platform/ios/test/common:RenderTestApp", + visibility = [ + "//visibility:public", + ], + deps = [":tests"], +) + +exports_files([ + "Info.plist", +]) diff --git a/render-test/ios/Empty.plist b/render-test/ios/Empty.plist new file mode 100644 index 000000000..4c8222816 --- /dev/null +++ b/render-test/ios/Empty.plist @@ -0,0 +1,18 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleIdentifier + org.maplibre.$(PRODUCT_NAME) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0 + + \ No newline at end of file diff --git a/render-test/ios/Info.plist b/render-test/ios/Info.plist index de3af9d72..91046744a 100644 --- a/render-test/ios/Info.plist +++ b/render-test/ios/Info.plist @@ -1,5 +1,5 @@ - + CFBundleDevelopmentRegion @@ -9,15 +9,15 @@ CFBundleExecutable RenderTestApp CFBundleIdentifier - com.mapbox.RenderTestApp + org.maplibre.RenderTestApp CFBundleGetInfoString - com.mapbox.RenderTestApp + org.maplibre.RenderTestApp CFBundleInfoDictionaryVersion 1.0 CFBundleLongVersionString 1.0 CFBundleName - com.mapbox.RenderTestApp + org.maplibre.RenderTestApp CFBundlePackageType APPL CFBundleShortVersionString @@ -28,10 +28,6 @@ 7877 CSResourcesFileMapped - NSHumanReadableCopyright - © 2014–2020 Mapbox - LSMinimumSystemVersion - ${MACOSX_DEPLOYMENT_TARGET} LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/render-test/ios/iosTestRunner.mm b/render-test/ios/iosTestRunner.mm index 4c3f7b87f..3c5dff4b0 100644 --- a/render-test/ios/iosTestRunner.mm +++ b/render-test/ios/iosTestRunner.mm @@ -25,117 +25,102 @@ -(instancetype)init { self = [super init]; - if (self) { - self.testStatus = NO; - self.runner = new TestRunner(); - NSString *path = nil; - NSError *error; - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSString *bundleRoot = [[NSBundle mainBundle] bundlePath]; - NSArray *bundleContents = [fileManager contentsOfDirectoryAtPath: bundleRoot error: &error]; - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDir = [paths objectAtIndex: 0]; - - for (uint32_t i = 0; i < bundleContents.count; i++) { - NSString *dirName = [bundleContents objectAtIndex: i]; - if ([dirName isEqualToString:@"test-data"]) { - NSString *destinationPath = [documentsDir stringByAppendingPathComponent: dirName]; - //Using NSFileManager we can perform many file system operations. - BOOL success = [fileManager fileExistsAtPath: destinationPath]; - if (success) { - success = [fileManager removeItemAtPath:destinationPath error:NULL]; - } - break; - } + if (!self) { + return nil; + } + + self.testStatus = NO; + self.runner = new TestRunner(); + NSError *error; + BOOL success; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *xcTestBundleRoot = [[NSBundle bundleForClass:[self class]] resourcePath]; + NSString *testDataDir = [xcTestBundleRoot stringByAppendingString:@"/TestData.bundle"]; + + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDir = [paths objectAtIndex: 0]; + + NSString *destinationPath = [documentsDir stringByAppendingPathComponent: @"test-data"]; + NSString *path = destinationPath; + + // delete destinationPath if it exists + if ([fileManager fileExistsAtPath:destinationPath]) { + success = [fileManager removeItemAtPath: destinationPath error: &error]; + if (!success) { + NSAssert1(0, @"Failed to delete '%@'.", destinationPath); } - - for (uint32_t i = 0; i < bundleContents.count; i++) { - NSString *dirName = [bundleContents objectAtIndex: i]; - if ([dirName isEqualToString:@"test-data"]) { - NSString *destinationPath = [documentsDir stringByAppendingPathComponent: dirName]; - BOOL success = [fileManager fileExistsAtPath: destinationPath]; - if (!success) { - NSString *copyDirPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: dirName]; + } - success = [fileManager copyItemAtPath: copyDirPath toPath: destinationPath error: &error]; - - if (!success){ - NSAssert1(0, @"Failed to copy file '%@'.", [error localizedDescription]); - NSLog(@"Failed to copy %@ file, error %@", dirName, [error localizedDescription]); - } - else { - path = destinationPath; - NSLog(@"File copied %@ OK", dirName); - } - } - else { - NSLog(@"File exits %@, skip copy", dirName); - } - break; - } + success = [fileManager copyItemAtPath: testDataDir toPath: destinationPath error: &error]; + if (!success){ + NSAssert1(0, @"Failed to copy file '%@'.", [error localizedDescription]); + NSLog(@"Failed to copy %@ file, error %@", testDataDir, [error localizedDescription]); + } + else { + path = destinationPath; + NSLog(@"File copied %@ OK", testDataDir); + } + + if (path) { + std::string basePath = std::string([path UTF8String]); + self.testStatus = self.runner->startTest(basePath) ? YES : NO; + self.styleResultPath = [path stringByAppendingPathComponent:@"/ios-render-test-runner-style.html"]; + self.metricResultPath = [path stringByAppendingPathComponent:@"/ios-render-test-runner-metrics.html"]; + + BOOL fileFound = [fileManager fileExistsAtPath: self.styleResultPath]; + if (fileFound == NO) { + NSLog(@"Style test result file '%@' doese not exit ", self.styleResultPath); + self.testStatus = NO; } - if (path) { - std::string basePath = std::string([path UTF8String]); - self.testStatus = self.runner->startTest(basePath) ? YES : NO; - self.styleResultPath = [path stringByAppendingPathComponent:@"/ios-render-test-runner-style.html"]; - self.metricResultPath = [path stringByAppendingPathComponent:@"/ios-render-test-runner-metrics.html"]; + fileFound = [fileManager fileExistsAtPath: self.metricResultPath]; + if (fileFound == NO) { + NSLog(@"Metric test result file '%@' doese not exit ", self.metricResultPath); + self.testStatus = NO; + } - BOOL fileFound = [fileManager fileExistsAtPath: self.styleResultPath]; - if (fileFound == NO) { - NSLog(@"Style test result file '%@' doese not exit ", self.styleResultPath); - self.testStatus = NO; - } - - fileFound = [fileManager fileExistsAtPath: self.metricResultPath]; - if (fileFound == NO) { - NSLog(@"Metric test result file '%@' doese not exit ", self.metricResultPath); - self.testStatus = NO; - } - - NSString *rebaselinePath = [path stringByAppendingPathComponent:@"/baselines"]; - BOOL needArchiving = NO; - BOOL isDir = NO; - NSArray *subpaths = [[NSArray alloc] init]; - if ([fileManager fileExistsAtPath:rebaselinePath isDirectory: &isDir] && isDir){ - subpaths = [fileManager subpathsAtPath:rebaselinePath]; - for(NSString *path in subpaths) + NSString *rebaselinePath = [path stringByAppendingPathComponent:@"/baselines"]; + BOOL needArchiving = NO; + BOOL isDir = NO; + NSArray *subpaths = [[NSArray alloc] init]; + if ([fileManager fileExistsAtPath:rebaselinePath isDirectory: &isDir] && isDir){ + subpaths = [fileManager subpathsAtPath:rebaselinePath]; + for(NSString *path in subpaths) + { + NSString *longPath = [rebaselinePath stringByAppendingPathComponent:path]; + if([fileManager fileExistsAtPath:longPath isDirectory:&isDir] && !isDir) { - NSString *longPath = [rebaselinePath stringByAppendingPathComponent:path]; - if([fileManager fileExistsAtPath:longPath isDirectory:&isDir] && !isDir) - { - needArchiving = YES; - break; - } + needArchiving = YES; + break; } } + } + else { + NSLog(@"Metric path '%@' doese not exit ", rebaselinePath); + } + + if (needArchiving) { + NSString *archivePath = [path stringByAppendingString:@"/metrics.zip"]; + BOOL success = [SSZipArchive createZipFileAtPath:archivePath + withContentsOfDirectory:rebaselinePath + keepParentDirectory:NO + compressionLevel:-1 + password:nil + AES:YES + progressHandler:nil]; + + if (success) { + NSLog(@"Successfully archive all of the metrics into metrics.zip"); + self.metricPath = archivePath; + } else { - NSLog(@"Metric path '%@' doese not exit ", rebaselinePath); - } - - if (needArchiving) { - NSString *archivePath = [path stringByAppendingString:@"/metrics.zip"]; - BOOL success = [SSZipArchive createZipFileAtPath:archivePath - withContentsOfDirectory:rebaselinePath - keepParentDirectory:NO - compressionLevel:-1 - password:nil - AES:YES - progressHandler:nil]; - - if (success) { - NSLog(@"Successfully archive all of the metrics into metrics.zip"); - self.metricPath = archivePath; - } - else { - NSLog(@"Failed to archive rebaselined metrics into metrics.zip"); - } + NSLog(@"Failed to archive rebaselined metrics into metrics.zip"); } } - - delete self.runner; - self.runner = nullptr; } + + delete self.runner; + self.runner = nullptr; return self; } diff --git a/render-test/runner.cpp b/render-test/runner.cpp index 2ce806079..0e9e23304 100644 --- a/render-test/runner.cpp +++ b/render-test/runner.cpp @@ -814,7 +814,14 @@ void TestRunner::run(TestMetadata& metadata) { auto it = std::find_if(symbols.begin(), symbols.end(), [isIcon, box](const auto& a) { return a.isIcon == isIcon && a.location.min == box.min && a.location.max == box.max; }); + +// TODO: remove usage of std::codecvt_utf8 +// https://github.com/maplibre/maplibre-native/issues/1269 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" static std::wstring_convert, char16_t> cv; +#pragma clang diagnostic pop + if (it == symbols.end()) { symbols.push_back({tileId, box, placed, isIcon}); return; diff --git a/vendor/BUILD.bazel b/vendor/BUILD.bazel index 53e32afc4..29e84782b 100644 --- a/vendor/BUILD.bazel +++ b/vendor/BUILD.bazel @@ -104,6 +104,24 @@ cc_library( visibility = ["//visibility:public"], ) +objc_library( + name = "zip-archive", + srcs = [ + "zip-archive/SSZipArchive/SSZipArchive.h", + "zip-archive/SSZipArchive/SSZipArchive.m", + "zip-archive/SSZipArchive/SSZipCommon.h", + ] + glob( + [ + "zip-archive/SSZipArchive/minizip/*.c", + "zip-archive/SSZipArchive/minizip/*.h", + ], + allow_empty = False, + ), + defines = ["ZLIB_COMPAT"], + includes = ["zip-archive/SSZipArchive"], + visibility = ["//visibility:public"], +) + # vendor/boost-files.json cc_library( name = "boost", @@ -192,6 +210,7 @@ cc_library( hdrs = glob(["icu/include/unicode/*.h"]), copts = CPP_FLAGS + [ "-Wno-c11-extensions", + "-Wno-conversion", ], includes = ["icu/include"], visibility = ["//visibility:public"],