diff --git a/.github/workflows/macos-ci.yml b/.github/workflows/macos-ci.yml index f56a5b9f4..05244621e 100644 --- a/.github/workflows/macos-ci.yml +++ b/.github/workflows/macos-ci.yml @@ -118,10 +118,22 @@ jobs: ccache --max-size=2G ccache --show-stats - - name: Build macos test + - name: Run macOS Objective-C tests run: | make macos-test + - name: npm install # C++ tests are run with root as working directory + run: npm ci --ignore-scripts + working-directory: . + + - name: Run macOS C++ unit tests + run: | + cmake -S . -B $BUILD_DIR + cmake --build $BUILD_DIR --target mbgl-test-runner + ctest -VV --test-dir $BUILD_DIR -R mbgl-test-runner + env: + BUILD_DIR: ../../../maplibre-gl-native-macostestbuild + - name: Check public symbols run: make darwin-check-public-symbols diff --git a/expression-test/main.cpp b/expression-test/main.cpp index db468dffe..0bc3fd1a6 100644 --- a/expression-test/main.cpp +++ b/expression-test/main.cpp @@ -3,8 +3,10 @@ #include "expression_test_logger.hpp" #include +#include -int main(int argc, char** argv) { +int main(int argc, char** argv) +try { // Parse args std::vector testPaths; mbgl::filesystem::path rootPath; @@ -70,6 +72,10 @@ int main(int argc, char** argv) { printStats(stats); writeHTMLResults(stats, rootPath.string(), shuffle, seed); - + return stats.errored.size() + stats.failed.size() == 0 ? 0 : 1; } +catch (std::exception const& ex) { + std::cerr << "Caught an exception while running tests:\n" << ex.what() << '\n'; + return 1; +} diff --git a/package.json b/package.json index 9be00573f..3b1f77e92 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ }, "license": "BSD-2-Clause", "dependencies": { - "@mapbox/cmake-node-module": "^1.2.0", "@acalcutt/node-pre-gyp": "^1.0.11", "@acalcutt/node-pre-gyp-github": "1.4.8", + "@mapbox/cmake-node-module": "^1.2.0", "minimatch": "^6.1.6", "npm-run-all": "^4.0.2" }, diff --git a/platform/ios/CMakeLists.txt b/platform/ios/CMakeLists.txt index 443ff20ad..8b137a76e 100644 --- a/platform/ios/CMakeLists.txt +++ b/platform/ios/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.19) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../ ${CMAKE_CURRENT_BINARY_DIR}/mapbox-gl-native ) +project (mbgl) +enable_testing() -project (mbgl) \ No newline at end of file +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../ ${CMAKE_CURRENT_BINARY_DIR}/mapbox-gl-native ) diff --git a/platform/ios/platform/default/src/mbgl/render-test/main.cpp b/platform/ios/platform/default/src/mbgl/render-test/main.cpp index 9b22b20e0..b997c7d1b 100644 --- a/platform/ios/platform/default/src/mbgl/render-test/main.cpp +++ b/platform/ios/platform/default/src/mbgl/render-test/main.cpp @@ -1,5 +1,9 @@ #include -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +try { return mbgl::runRenderTests(argc, argv); +} catch (std::exception const& e) { + std::cerr << "Caught an exception while running tests\n" << e.what() << '\n'; + return 1; } diff --git a/platform/macos/macos.cmake b/platform/macos/macos.cmake index 4d2970d1d..dc6576e9b 100644 --- a/platform/macos/macos.cmake +++ b/platform/macos/macos.cmake @@ -158,4 +158,11 @@ set_target_properties(mbgl-benchmark-runner mbgl-test-runner mbgl-render-test-ru if(NOT DEFINED ENV{CI}) add_test(NAME mbgl-benchmark-runner COMMAND mbgl-benchmark-runner WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) endif() -add_test(NAME mbgl-test-runner COMMAND mbgl-test-runner WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) +add_test( + NAME mbgl-test-runner + COMMAND + node + ${PROJECT_SOURCE_DIR}/test/storage/with-server.js + ${PROJECT_SOURCE_DIR}/test/storage/server.js + $ + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) diff --git a/test/fixtures/.gitignore b/test/fixtures/.gitignore new file mode 100644 index 000000000..3a379d66b --- /dev/null +++ b/test/fixtures/.gitignore @@ -0,0 +1,2 @@ +diff.png +actual.png \ No newline at end of file diff --git a/test/fixtures/local_glyphs/ping_fang/expected.png b/test/fixtures/local_glyphs/ping_fang/expected.png index 6e7e5c80b..028483613 100644 Binary files a/test/fixtures/local_glyphs/ping_fang/expected.png and b/test/fixtures/local_glyphs/ping_fang/expected.png differ diff --git a/test/fixtures/local_glyphs/ping_fang_semibold/expected.png b/test/fixtures/local_glyphs/ping_fang_semibold/expected.png index 6bb0a2503..9723cc282 100644 Binary files a/test/fixtures/local_glyphs/ping_fang_semibold/expected.png and b/test/fixtures/local_glyphs/ping_fang_semibold/expected.png differ diff --git a/test/src/mbgl/test/http_server.cpp b/test/src/mbgl/test/http_server.cpp index 5679b1188..2c48cf7fa 100644 --- a/test/src/mbgl/test/http_server.cpp +++ b/test/src/mbgl/test/http_server.cpp @@ -205,5 +205,6 @@ HttpServer::~HttpServer() { serverThread.join(); } + } // namespace test } // namespace mbgl diff --git a/test/src/mbgl/test/test.cpp b/test/src/mbgl/test/test.cpp index d90be0747..4c47094db 100644 --- a/test/src/mbgl/test/test.cpp +++ b/test/src/mbgl/test/test.cpp @@ -7,10 +7,6 @@ namespace mbgl { int runTests(int argc, char *argv[]) { -#if TEST_HAS_SERVER - auto server = std::make_unique(); -#endif - testing::InitGoogleTest(&argc, argv); // In order to run specific tests diff --git a/test/storage/resource.test.cpp b/test/storage/resource.test.cpp index 41bf71471..e03f339a9 100644 --- a/test/storage/resource.test.cpp +++ b/test/storage/resource.test.cpp @@ -86,7 +86,7 @@ TEST(Resource, Tile) { Resource wmsTile = Resource::tile("http://example.com/?bbox={bbox-epsg-3857}", 2.0, 0, 0, 1, Tileset::Scheme::XYZ); EXPECT_EQ(Resource::Kind::Tile, wmsTile.kind); - EXPECT_EQ("http://example.com/?bbox=-20037508.342789245,0,0,20037508.342789245", wmsTile.url); + EXPECT_EQ("http://example.com/?bbox=-20037508.342789244,0,0,20037508.342789244", wmsTile.url); EXPECT_EQ("http://example.com/?bbox={bbox-epsg-3857}", wmsTile.tileData->urlTemplate); EXPECT_EQ(1, wmsTile.tileData->pixelRatio); EXPECT_EQ(0, wmsTile.tileData->x); diff --git a/test/storage/server.js b/test/storage/server.js new file mode 100755 index 000000000..cfacf7ce9 --- /dev/null +++ b/test/storage/server.js @@ -0,0 +1,182 @@ +#!/usr/bin/env node +/* jshint node: true */ +'use strict'; + +// This needs to be here to make sure the pipe stays open. +// We're waiting until the stdin pipe gets closed (e.g. because the parent +// process dies) +process.stdin.on('readable', function() {}); +process.stdin.on('end', function() { process.exit(0); }); + + +var fs = require('fs'); +var express = require('express'); +var app = express(); + +// We're manually setting Etag headers. +app.disable('etag'); + +app.get('/test', function (req, res) { + if (req.query.modified) { + res.setHeader('Last-Modified', (new Date(req.query.modified * 1000)).toUTCString()); + } + if (req.query.expires) { + res.setHeader('Expires', (new Date(req.query.expires * 1000)).toUTCString()); + } + if (req.query.etag) { + res.setHeader('ETag', req.query.etag); + } + if (req.query.cachecontrol) { + res.setHeader('Cache-Control', req.query.cachecontrol); + } + res.send('Hello World!'); +}); + +app.get('/stale/*', function() { + // Never respond. +}); + +var cacheCounter = 0; +app.get('/cache', function(req, res) { + res.setHeader('Cache-Control', 'max-age=30'); // Allow caching for 30 seconds + res.send('Response ' + (++cacheCounter)); +}); + +app.get('/revalidate-same', function(req, res) { + if (req.headers['if-none-match'] == 'snowfall') { + // Second request can be cached for 1 second. + res.setHeader('Cache-Control', 'max-age=1, must-revalidate'); + res.status(304).end(); + } else { + // First request must always be revalidated. + res.setHeader('ETag', 'snowfall'); + res.setHeader('Cache-Control', 'must-revalidate'); + res.status(200).send('Response'); + } +}); + +var expiresCounter = 0; +app.get('/clockskew', function (req, res) { + res.setHeader('Expires', (new Date(2010, 1, 1, 10, ++expiresCounter, 0)).toUTCString()); + res.status(200).send('Response'); +}); + +app.get('/revalidate-modified', function(req, res) { + var jan1 = new Date('jan 1 2015 utc'); + + if (req.headers['if-modified-since']) { + var modified_since = new Date(req.headers['if-modified-since']); + if (modified_since >= jan1) { + res.setHeader('Cache-Control', 'max-age=1, must-revalidate'); + res.status(304).end(); + return; + } + } + + // First request must always be revalidated. + res.setHeader('Last-Modified', jan1.toUTCString()); + res.setHeader('Cache-Control', 'must-revalidate'); + res.status(200).send('Response'); +}); + + +var revalidateEtagCounter = 1; +app.get('/revalidate-etag', function(req, res) { + res.setHeader('ETag', 'response-' + revalidateEtagCounter); + res.setHeader('Cache-Control', 'must-revalidate'); + + res.status(200).send('Response ' + revalidateEtagCounter); + revalidateEtagCounter++; +}); + +app.get('/empty-data', function(req, res) { + res.status(200).send(); +}); + +app.get('/no-content', function(req, res) { + res.status(204).send(); +}); + +app.get('/not-found', function(req, res) { + res.status(404).send('Not Found!'); +}); + +app.get('/permanent-error', function(req, res) { + res.status(500).send('Server Error!'); +}); + +var temporaryErrorCounter = 0; +app.get('/temporary-error', function(req, res) { + if (temporaryErrorCounter === 0) { + res.status(500).end(); + } else { + res.status(200).send('Hello World!'); + } + + temporaryErrorCounter++; +}); + +app.get('/rate-limit', function(req, res) { + + if (req.query.std) { + res.setHeader('Retry-After', 1); + } else if (req.query.mbx) { + res.setHeader('x-rate-limit-reset', Math.round(Date.now() / 1000) + 1); + } + + res.status(429).end(); +}); + +var styleFailOnce500 = true; +app.get('/style-fail-once-500', function (req, res) { + if (styleFailOnce500) { + res.status(500).send('Server Error!'); + styleFailOnce500 = false; + } else { + res.status(200).send('{ "version": 8, "name": "Teste Style" }'); + } +}); + +var styleFailOnce404 = true; +app.get('/style-fail-once-404', function (req, res) { + if (styleFailOnce404) { + res.status(404).send('Not found!'); + styleFailOnce404 = false; + } else { + res.status(200).send('{ "version": 8, "name": "Teste Style" }'); + } +}); + +var styleFailOnce404Cache = true; +app.get('/style-fail-once-404-cache', function (req, res) { + if (styleFailOnce404Cache) { + res.setHeader('Cache-Control', 'max-age=30'); + res.status(404).send('Not found!'); + styleFailOnce404Cache = false; + } else { + res.status(200).send('{ "version": 8, "name": "Teste Style" }'); + } +}); + +app.get('/delayed', function(req, res) { + setTimeout(function() { + res.status(200).send('Response'); + }, 200); +}); + + +app.get('/load/:number(\\d+)', function(req, res) { + res.send('Request ' + req.params.number); +}); + +app.get('/online/:style(*)', function(req, res) { + const file = (__dirname + "/../fixtures/map/online/" + req.params.style).replace("storage/../", ""); + res.sendFile(file); // Set disposition and send it. + // res.status(200).send(); + // res.send('Request ' + req.params.style); +}); + +var server = app.listen(3000, function () { + // Tell parent that we're now listening. + process.stdout.write("OK"); +}); diff --git a/test/storage/with-server.js b/test/storage/with-server.js new file mode 100644 index 000000000..45bc50489 --- /dev/null +++ b/test/storage/with-server.js @@ -0,0 +1,34 @@ +// This script runs a Node.js server passed to it while running aother program. + +const { spawn } = require('child_process') + +if (process.argv.length < 4) { + console.error("Usage: node with-server.js [arg]..."); + process.exit(1); +} + +const serverScript = process.argv[2]; + +const prog = process.argv[3]; +const args = process.argv.slice(4); + +const sameStdio = { stdio: [process.stdin, process.stdout, process.stderr] }; + +const server = spawn('node', [serverScript]); + +server.stdout.on('data', (data) => { + const dataAsStr = data.toString(); + + if (dataAsStr === "OK") { + const test = spawn(prog, args, sameStdio); + + test.on('close', (code) => { + console.error(`Sub process exited with code '${code}', killing server`); + server.kill(); + process.exit(code); + }); + return; + } + + console.error(`[server] ${dataAsStr}`); +}); diff --git a/test/tile/tile_coordinate.test.cpp b/test/tile/tile_coordinate.test.cpp index a5b5bd991..9ad2d1d96 100644 --- a/test/tile/tile_coordinate.test.cpp +++ b/test/tile/tile_coordinate.test.cpp @@ -94,8 +94,8 @@ TEST(TileCoordinate, ToGeometryCoordinate) { for (uint8_t zoom = 0; zoom <= 16; ++zoom) { auto maxTilesPerAxis = static_cast(std::pow(2, zoom)); for (const auto& edge : edges) { - uint32_t tileX = edge.x == 0 ? 0 : edge.x == 1 ? maxTilesPerAxis - 1 : (maxTilesPerAxis / 2) - 1; - uint32_t tileY = edge.y == 0 ? 0 : edge.y == 1 ? maxTilesPerAxis - 1 : (maxTilesPerAxis / 2) - 1; + uint32_t tileX = edge.x == 0 ? 0 : edge.x == 1 ? maxTilesPerAxis - 1 : (maxTilesPerAxis / 2.0) - 1; + uint32_t tileY = edge.y == 0 ? 0 : edge.y == 1 ? maxTilesPerAxis - 1 : (maxTilesPerAxis / 2.0) - 1; UnwrappedTileID unwrapped(0, CanonicalTileID { zoom, tileX, tileY }); auto tilePointX = ((edge.x * maxTilesPerAxis) - tileX) * util::EXTENT;