Browse Source

chore: enable native unittesting (#20293)

Shelley Vohr 5 years ago
parent
commit
80af35e0cd

+ 22 - 0
.circleci/config.yml

@@ -328,6 +328,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
@@ -399,6 +407,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
@@ -419,6 +432,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
@@ -819,6 +833,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
@@ -889,6 +907,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

+ 54 - 13
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 =

+ 2 - 0
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

+ 8 - 0
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"

+ 1 - 1
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}`)

+ 3 - 0
script/native-test-targets.json

@@ -0,0 +1,3 @@
+[
+  "shell_browser_ui_unittests"
+]

+ 56 - 3
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)]

+ 29 - 0
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

+ 15 - 0
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)));
+}

+ 9 - 0
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