diff --git a/.circleci/config.yml b/.circleci/config.yml index 9edc8038ea055..0ff74d0fb2326 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -331,6 +331,14 @@ step-electron-build: &step-electron-build cd src ninja -C out/Default electron -j $NUMBER_OF_NINJA_PROCESSES +step-native-unittests-build: &step-native-unittests-build + run: + name: Build native test targets + no_output_timeout: 30m + command: | + cd src + ninja -C out/Default shell_browser_ui_unittests -j $NUMBER_OF_NINJA_PROCESSES + step-maybe-electron-dist-strip: &step-maybe-electron-dist-strip run: name: Strip electron binaries @@ -402,6 +410,11 @@ step-nodejs-headers-store: &step-nodejs-headers-store path: src/out/Default/gen/node_headers.tar.gz destination: node_headers.tar.gz +step-native-unittests-store: &step-native-unittests-store + store_artifacts: + path: src/out/Default/shell_browser_ui_unittests + destination: shell_browser_ui_unittests + step-electron-publish: &step-electron-publish run: name: Publish Electron Dist @@ -422,6 +435,7 @@ step-persist-data-for-tests: &step-persist-data-for-tests # Build artifacts - src/out/Default/dist.zip - src/out/Default/mksnapshot.zip + - src/out/Default/shell_browser_ui_unittests - src/out/Default/gen/node_headers - src/out/ffmpeg/ffmpeg.zip - src/electron @@ -822,6 +836,10 @@ steps-electron-build: &steps-electron-build - *step-electron-dist-store - *step-ninja-summary + # Native test targets + - *step-native-unittests-build + - *step-native-unittests-store + # Node.js headers - *step-nodejs-headers-build - *step-nodejs-headers-store @@ -892,6 +910,10 @@ steps-electron-build-with-inline-checkout-for-tests: &steps-electron-build-with- - *step-electron-dist-store - *step-ninja-summary + # Native test targets + - *step-native-unittests-build + - *step-native-unittests-store + # Node.js headers - *step-nodejs-headers-build - *step-nodejs-headers-store diff --git a/BUILD.gn b/BUILD.gn index 94aad7e5334ef..ec505f2f3b4bc 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -4,6 +4,7 @@ import("//build/config/win/manifest.gni") import("//content/public/app/mac_helpers.gni") import("//pdf/features.gni") import("//printing/buildflags/buildflags.gni") +import("//testing/test.gni") import("//third_party/ffmpeg/ffmpeg_options.gni") import("//tools/generate_library_loader/generate_library_loader.gni") import("//tools/grit/grit_rule.gni") @@ -60,6 +61,10 @@ config("branding") { ] } +config("electron_lib_config") { + include_dirs = [ "." ] +} + # We geneate the definitions twice here, once in //electron/electron.d.ts # and once in $target_gen_dir # The one in $target_gen_dir is used for the actual TSC build later one @@ -333,7 +338,10 @@ source_set("electron_lib") { configs += [ "//v8:external_startup_data" ] configs += [ "//third_party/electron_node:node_internals" ] - public_configs = [ ":branding" ] + public_configs = [ + ":branding", + ":electron_lib_config", + ] deps = [ ":atom_js2c", @@ -475,6 +483,19 @@ source_set("electron_lib") { "//content/common:mac_helpers", "//ui/accelerated_widget_mac", ] + + libs = [ + "AVFoundation.framework", + "Carbon.framework", + "LocalAuthentication.framework", + "QuartzCore.framework", + "Quartz.framework", + "Security.framework", + "SecurityInterface.framework", + "ServiceManagement.framework", + "StoreKit.framework", + ] + sources += [ "shell/browser/ui/views/autofill_popup_view.cc", "shell/browser/ui/views/autofill_popup_view.h", @@ -774,6 +795,7 @@ if (is_mac) { "Libraries", ] public_deps = [ + ":electron_framework_libraries", ":electron_lib", ] deps = [ @@ -795,18 +817,6 @@ if (is_mac) { include_dirs = [ "." ] sources = filenames.framework_sources - libs = [ - "AVFoundation.framework", - "Carbon.framework", - "LocalAuthentication.framework", - "QuartzCore.framework", - "Quartz.framework", - "Security.framework", - "SecurityInterface.framework", - "ServiceManagement.framework", - "StoreKit.framework", - ] - if (enable_osr) { libs += [ "IOSurface.framework" ] } @@ -1189,6 +1199,37 @@ if (is_mac) { } } +test("shell_browser_ui_unittests") { + sources = [ + "//electron/shell/browser/ui/accelerator_util_unittests.cc", + "//electron/shell/browser/ui/run_all_unittests.cc", + ] + + configs += [ ":electron_lib_config" ] + + deps = [ + ":electron_lib", + "//base", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + "//ui/base", + "//ui/strings", + ] + + if (is_mac) { + # Resolve paths owing to different test executable locations + ldflags = [ + "-F", + rebase_path("external_binaries", root_build_dir), + "-rpath", + "@loader_path", + "-rpath", + "@executable_path/" + rebase_path("external_binaries", root_build_dir), + ] + } +} + template("dist_zip") { _runtime_deps_target = "${target_name}__deps" _runtime_deps_file = diff --git a/appveyor.yml b/appveyor.yml index 8d35d3ed22537..6d521aef252a0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -83,10 +83,12 @@ build_script: - gn gen out/ffmpeg "--args=import(\"//electron/build/args/ffmpeg.gn\") %GN_EXTRA_ARGS%" - ninja -C out/ffmpeg electron:electron_ffmpeg_zip - ninja -C out/Default electron:electron_dist_zip + - ninja -C out/Default shell_browser_ui_unittests - ninja -C out/Default electron:electron_mksnapshot_zip - ninja -C out/Default electron:electron_chromedriver_zip - ninja -C out/Default third_party/electron_node:headers - appveyor PushArtifact out/Default/dist.zip + - appveyor PushArtifact out/Default/shell_browser_ui_unittests.exe - appveyor PushArtifact out/Default/chromedriver.zip - appveyor PushArtifact out/ffmpeg/ffmpeg.zip - 7z a node_headers.zip out\Default\gen\node_headers diff --git a/azure-pipelines-woa.yml b/azure-pipelines-woa.yml index 352123cd04130..4dde4afb0304c 100644 --- a/azure-pipelines-woa.yml +++ b/azure-pipelines-woa.yml @@ -18,6 +18,14 @@ steps: env: APPVEYOR_TOKEN: $(APPVEYOR_TOKEN) +- powershell: | + $localArtifactPath = "$pwd\src\out\Default\shell_browser_ui_unittests.exe" + $serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/shell_browser_ui_unittests.exe" + Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" } + displayName: 'Download and extract native test executables for test' + env: + APPVEYOR_TOKEN: $(APPVEYOR_TOKEN) + - powershell: | $localArtifactPath = "$pwd\ffmpeg.zip" $serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/ffmpeg.zip" diff --git a/script/lib/utils.js b/script/lib/utils.js index 0c1053a1d0898..56a4c8b268864 100644 --- a/script/lib/utils.js +++ b/script/lib/utils.js @@ -27,7 +27,7 @@ function getOutDir (shouldLog) { if (process.env.ELECTRON_OUT_DIR) { return process.env.ELECTRON_OUT_DIR } else { - for (const buildType of ['Debug', 'Testing', 'Release']) { + for (const buildType of ['Debug', 'Testing', 'Release', 'Default']) { const outPath = path.resolve(SRC_DIR, 'out', buildType) if (fs.existsSync(outPath)) { if (shouldLog) console.log(`OUT_DIR is: ${buildType}`) diff --git a/script/native-test-targets.json b/script/native-test-targets.json new file mode 100644 index 0000000000000..7819797145775 --- /dev/null +++ b/script/native-test-targets.json @@ -0,0 +1,3 @@ +[ + "shell_browser_ui_unittests" +] \ No newline at end of file diff --git a/script/spec-runner.js b/script/spec-runner.js index 0a588dd6ce52f..e4bcde4a9e588 100755 --- a/script/spec-runner.js +++ b/script/spec-runner.js @@ -12,7 +12,8 @@ const pass = '✓'.green const fail = '✗'.red const args = require('minimist')(process.argv, { - string: ['runners'], + string: ['runners', 'target'], + boolean: ['buildNativeTests'], unknown: arg => unknownFlags.push(arg) }) @@ -34,7 +35,8 @@ const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx' const runners = new Map([ ['main', { description: 'Main process specs', run: runMainProcessElectronTests }], - ['remote', { description: 'Remote based specs', run: runRemoteBasedElectronTests }] + ['remote', { description: 'Remote based specs', run: runRemoteBasedElectronTests }], + ['native', { description: 'Native specs', run: runNativeElectronTests }] ]) const specHashPath = path.resolve(__dirname, '../spec/.hash') @@ -48,7 +50,7 @@ if (args.runners) { } console.log('Only running:', runnersToRun) } else { - console.log(`Triggering both ${[...runners.keys()].join(' and ')} runners`) + console.log(`Triggering runners: ${[...runners.keys()].join(', ')}`) } async function main () { @@ -141,6 +143,57 @@ async function runRemoteBasedElectronTests () { console.log(`${pass} Electron remote process tests passed.`) } +async function runNativeElectronTests () { + let testTargets = require('./native-test-targets.json') + const outDir = `out/${utils.getOutDir(false)}` + + // If native tests are being run, only one arg would be relevant + if (args.target && !testTargets.includes(args.target)) { + console.log(`${fail} ${args.target} must be a subset of [${[testTargets].join(', ')}]`) + process.exit(1) + } + + // Optionally build all native test targets + if (args.buildNativeTests) { + for (const target of testTargets) { + const build = childProcess.spawnSync('ninja', ['-C', outDir, target], { + cwd: path.resolve(__dirname, '../..'), + stdio: 'inherit' + }) + + // Exit if test target failed to build + if (build.status !== 0) { + console.log(`${fail} ${target} failed to build.`) + process.exit(1) + } + } + } + + // If a specific target was passed, only build and run that target + if (args.target) testTargets = [args.target] + + // Run test targets + const failures = [] + for (const target of testTargets) { + console.info('\nRunning native test for target:', target) + const testRun = childProcess.spawnSync(`./${outDir}/${target}`, { + cwd: path.resolve(__dirname, '../..'), + stdio: 'inherit' + }) + + // Collect failures and log at end + if (testRun.status !== 0) failures.push({ target }) + } + + // Exit if any failures + if (failures.length > 0) { + console.log(`${fail} Electron native tests failed for the following targets: `, failures) + process.exit(1) + } + + console.log(`${pass} Electron native tests passed.`) +} + async function runMainProcessElectronTests () { let exe = path.resolve(BASE, utils.getElectronExec()) const runnerArgs = ['electron/spec-main', ...unknownArgs.slice(2)] diff --git a/shell/browser/ui/accelerator_util_unittests.cc b/shell/browser/ui/accelerator_util_unittests.cc new file mode 100644 index 0000000000000..99f179847874c --- /dev/null +++ b/shell/browser/ui/accelerator_util_unittests.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2019 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/ui/accelerator_util.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace accelerator_util { + +TEST(AcceleratorUtilTest, StringToAccelerator) { + struct { + const std::string& description; + bool expected_success; + } keys[] = { + {"♫♫♫♫♫♫♫", false}, {"Cmd+Plus", true}, {"Ctrl+Space", true}, + {"CmdOrCtrl", false}, {"Alt+Tab", true}, {"AltGr+Backspace", true}, + {"Super+Esc", true}, {"Super+X", true}, {"Shift+1", true}, + }; + + for (const auto& key : keys) { + // Initialize empty-but-not-null accelerator + ui::Accelerator out = ui::Accelerator(ui::VKEY_UNKNOWN, ui::EF_NONE); + bool success = StringToAccelerator(key.description, &out); + EXPECT_EQ(success, key.expected_success); + } +} + +} // namespace accelerator_util diff --git a/shell/browser/ui/run_all_unittests.cc b/shell/browser/ui/run_all_unittests.cc new file mode 100644 index 0000000000000..eff83fde07408 --- /dev/null +++ b/shell/browser/ui/run_all_unittests.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2019 GitHub, Inc. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/bind.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" +#include "build/build_config.h" + +int main(int argc, char** argv) { + base::TestSuite test_suite(argc, argv); + return base::LaunchUnitTests( + argc, argv, + base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite))); +} diff --git a/vsts-arm-test-steps.yml b/vsts-arm-test-steps.yml index d083f3179f25a..29c4a54500145 100644 --- a/vsts-arm-test-steps.yml +++ b/vsts-arm-test-steps.yml @@ -53,6 +53,15 @@ steps: env: CIRCLE_TOKEN: $(CIRCLECI_TOKEN) +- bash: | + export NATIVE_UNITTESTS_DEST=$PWD/src/out/Default + cd src/electron + node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=shell_browser_ui_unittests --dest=$NATIVE_UNITTESTS_DEST + chmod +x $NATIVE_UNITTESTS_DEST/shell_browser_ui_unittests + displayName: 'Download native unittest executables' + env: + CIRCLE_TOKEN: $(CIRCLECI_TOKEN) + - bash: | sh -e /etc/init.d/xvfb start displayName: Setup for headless testing