Browse Source

ci: enable arm64 macOS tests (#24832)

* ci: enable arm64 macOS tests
John Kleinschmidt 4 years ago
parent
commit
034a792df1

+ 64 - 7
.circleci/config.yml

@@ -97,6 +97,10 @@ machine-mac-large-arm: &machine-mac-large-arm
   macos:
     xcode: "12.2.0"
 
+machine-mac-arm64: &machine-mac-arm64
+  resource_class: electronjs/macos-arm64
+  machine: true
+
 # Build configurations options.
 env-testing-build: &env-testing-build
   GN_CONFIG: //electron/build/args/testing.gn
@@ -141,6 +145,7 @@ env-apple-silicon: &env-apple-silicon
   GN_EXTRA_ARGS: 'target_cpu = "arm64" use_prebuilt_v8_context_snapshot = true'
   TARGET_ARCH: arm64
   USE_PREBUILT_V8_CONTEXT_SNAPSHOT: 1
+  npm_config_arch: arm64
 
 env-arm64: &env-arm64
   GN_EXTRA_ARGS: 'target_cpu = "arm64" fatal_linker_warnings = false enable_linux_installer = false'
@@ -457,7 +462,7 @@ step-install-signing-cert-on-mac: &step-install-signing-cert-on-mac
   run:
     name: Import and trust self-signed codesigning cert on MacOS
     command: |
-      if [ "`uname`" == "Darwin" ]; then
+      if  [ "$TARGET_ARCH" != "arm64" ] && [ "`uname`" == "Darwin" ]; then
         cd src/electron
         ./script/codesign/generate-identity.sh
       fi
@@ -670,6 +675,7 @@ step-persist-data-for-tests: &step-persist-data-for-tests
       - src/electron
       - src/third_party/electron_node
       - src/third_party/nan
+      - src/cross-arch-snapshots
 
 step-electron-dist-unzip: &step-electron-dist-unzip
   run:
@@ -732,7 +738,11 @@ step-verify-mksnapshot: &step-verify-mksnapshot
     name: Verify mksnapshot
     command: |
       cd src
-      python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default
+      if  [ "$TARGET_ARCH" == "arm64" ] &&[ "`uname`" == "Darwin" ]; then
+        python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default --snapshot-files-dir $PWD/cross-arch-snapshots
+      else
+        python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default
+      fi
 
 step-verify-chromedriver: &step-verify-chromedriver
   run:
@@ -835,7 +845,7 @@ step-maybe-cross-arch-snapshot: &step-maybe-cross-arch-snapshot
   run:
     name: Generate cross arch snapshot (arm/arm64)
     command: |
-      if [ "$TRIGGER_ARM_TEST" == "true" ] && [ -z "$CIRCLE_PR_NUMBER" ]; then
+      if [ "$GENERATE_CROSS_ARCH_SNAPSHOT" == "true" ] && [ -z "$CIRCLE_PR_NUMBER" ]; then
         cd src
         if [ "$TARGET_ARCH" == "arm" ]; then
           export MKSNAPSHOT_PATH="clang_x86_v8_arm"
@@ -868,8 +878,13 @@ step-maybe-trigger-arm-test: &step-maybe-trigger-arm-test
       if [ "$TRIGGER_ARM_TEST" == "true" ] && [ -z "$CIRCLE_PR_NUMBER" ]; then
         #Trigger VSTS job, passing along CircleCI job number and branch to build
         if [ "`uname`" == "Darwin" ]; then
-          echo "Triggering electron-arm2-testing build on Azure DevOps"  
-          node electron/script/release/ci-release-build.js --job=electron-arm2-testing --ci=DevOps --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
+          if [ x"$MAS_BUILD" == x"true" ]; then
+            export DEVOPS_BUILD="electron-mas-arm64-testing"
+          else
+            export DEVOPS_BUILD="electron-osx-arm64-testing"
+          fi
+          echo "Triggering $DEVOPS_BUILD build on Azure DevOps"
+          node electron/script/release/ci-release-build.js --job=$DEVOPS_BUILD --ci=DevOps --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
         else
           echo "Triggering electron-$TARGET_ARCH-testing build on VSTS"  
           node electron/script/release/ci-release-build.js --job=electron-$TARGET_ARCH-testing --ci=VSTS --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
@@ -1290,8 +1305,14 @@ steps-tests: &steps-tests
           ELECTRON_DISABLE_SECURITY_WARNINGS: 1
         command: |
           cd src
-          (cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging --files $(circleci tests glob spec-main/*-spec.ts | circleci tests split))
-          (cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging --files $(circleci tests glob spec/*-spec.js | circleci tests split))
+          if  [ "$TARGET_ARCH" == "arm64" ] &&[ "`uname`" == "Darwin" ]; then
+            export ELECTRON_SKIP_NATIVE_MODULE_TESTS=true
+            (cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging)
+            (cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging)
+          else
+            (cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging --files $(circleci tests glob spec-main/*-spec.ts | circleci tests split))
+            (cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging --files $(circleci tests glob spec/*-spec.js | circleci tests split))
+          fi
     - run:
         name: Check test results existence
         command: |
@@ -1889,6 +1910,7 @@ jobs:
       <<: *env-testing-build
       <<: *env-ninja-status
       TRIGGER_ARM_TEST: true
+      GENERATE_CROSS_ARCH_SNAPSHOT: true
       GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
     steps:
       - electron-build:
@@ -1947,6 +1969,7 @@ jobs:
       <<: *env-testing-build
       <<: *env-ninja-status
       TRIGGER_ARM_TEST: true
+      GENERATE_CROSS_ARCH_SNAPSHOT: true
       GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
     steps:
       - electron-build:
@@ -2098,6 +2121,7 @@ jobs:
       <<: *env-macos-build
       <<: *env-apple-silicon
       GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
+      GENERATE_CROSS_ARCH_SNAPSHOT: true
     steps:
       - electron-build:
           persist: true
@@ -2204,6 +2228,7 @@ jobs:
       <<: *env-macos-build
       <<: *env-mas-apple-silicon
       GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
+      GENERATE_CROSS_ARCH_SNAPSHOT: true
     steps:
       - electron-build:
           persist: true
@@ -2357,6 +2382,14 @@ jobs:
       <<: *env-send-slack-notifications
     <<: *steps-verify-ffmpeg
 
+  osx-testing-arm64-tests:  
+    <<: *machine-mac-arm64
+    environment:
+      <<: *env-mac-large
+      <<: *env-stack-dumping
+      <<: *env-apple-silicon
+    <<: *steps-tests
+
   mas-testing-x64-tests:
     <<: *machine-mac-large
     environment:
@@ -2380,6 +2413,14 @@ jobs:
       <<: *env-send-slack-notifications
     <<: *steps-verify-ffmpeg
 
+  mas-testing-arm64-tests:
+    <<: *machine-mac-arm64
+    environment:
+      <<: *env-mac-large
+      <<: *env-stack-dumping
+      <<: *env-apple-silicon
+    <<: *steps-tests
+
   # Layer 4: Summary.
   linux-x64-release-summary:
     <<: *machine-linux-medium
@@ -2591,6 +2632,14 @@ workflows:
           requires:
             - mac-checkout-and-save-cache
 
+      - osx-testing-arm64-tests:
+          filters:
+            branches:
+              # Do not run this on forked pull requests
+              ignore: /pull\/[0-9]+/
+          requires:
+            - osx-testing-arm64
+
       - mas-testing-x64:
           requires:
             - mac-checkout-and-save-cache
@@ -2607,6 +2656,14 @@ workflows:
           requires:
             - mac-checkout-and-save-cache
 
+      - mas-testing-arm64-tests:
+          filters:
+            branches:
+              # Do not run this on forked pull requests
+              ignore: /pull\/[0-9]+/
+          requires:
+            - mas-testing-arm64
+
   nightly-linux-release-test:
     triggers:
       - schedule:

+ 27 - 10
azure-pipelines-arm.yml

@@ -17,6 +17,7 @@ steps:
     node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=dist.zip --dest=$ZIP_DEST
     cd $ZIP_DEST
     unzip -o dist.zip
+    xattr -cr Electron.app    
   displayName: 'Download and unzip dist files for test'
   env:
     CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
@@ -48,27 +49,31 @@ steps:
    mkdir -p $CROSS_ARCH_SNAPSHOTS
    cd src/electron
    node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/snapshot_blob.bin --dest=$CROSS_ARCH_SNAPSHOTS
-   node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/v8_context_snapshot.bin --dest=$CROSS_ARCH_SNAPSHOTS
+   node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/v8_context_snapshot.arm64.bin --dest=$CROSS_ARCH_SNAPSHOTS
   displayName: 'Download cross arch snapshot files'
   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'
+    cd src
+    export ELECTRON_OUT_DIR=Default
+    export npm_config_arch=arm64
+    (cd electron && node script/yarn test --enable-logging --runners main)
+  displayName: 'Run Electron main tests'
+  timeoutInMinutes: 20
   env:
-    CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
+    ELECTRON_DISABLE_SECURITY_WARNINGS: 1
+    IGNORE_YARN_INSTALL_ERROR: 1
+    ELECTRON_TEST_RESULTS_DIR: junit
 
 - bash: |
     cd src
     export ELECTRON_OUT_DIR=Default
-    set npm_config_arch=arm64
-    (cd electron && node script/yarn test -- --enable-logging)
-  displayName: 'Run Electron tests'
+    export npm_config_arch=arm64
+    (cd electron && node script/yarn test --enable-logging --runners remote)
+  displayName: 'Run Electron remote tests'
   timeoutInMinutes: 20
+  condition: succeededOrFailed()
   env:
     ELECTRON_DISABLE_SECURITY_WARNINGS: 1
     IGNORE_YARN_INSTALL_ERROR: 1
@@ -80,6 +85,8 @@ steps:
   displayName: Verify non proprietary ffmpeg
   timeoutInMinutes: 5
   condition: succeededOrFailed()
+  env:
+    TARGET_ARCH: arm64
 
 - bash: |
     cd src
@@ -98,6 +105,16 @@ steps:
 
   condition: succeededOrFailed()
 
+- bash: killall Electron || echo "No Electron processes left running"
+  displayName: 'Kill processes left running from last test run'
+  condition: always()
+
+- bash: |
+    rm -rf ~/Library/Application\ Support/Electron*
+    rm -rf ~/Library/Application\ Support/electron*
+  displayName: 'Delete user app data directories'
+  condition: always()
+
 - task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3
   displayName: 'Clean Agent Directories'
 

+ 0 - 1
patches/v8/.patches

@@ -7,5 +7,4 @@ workaround_an_undefined_symbol_error.patch
 do_not_export_private_v8_symbols_on_windows.patch
 revert_cleanup_switch_offset_of_to_offsetof_where_possible.patch
 fix_build_deprecated_attirbute_for_older_msvc_versions.patch
-fix_use_proper_page_size_for_mac_arm64.patch
 chore_disallow_copying_cppheapcreateparams.patch

+ 0 - 43
patches/v8/fix_use_proper_page_size_for_mac_arm64.patch

@@ -1,43 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: John Kleinschmidt <[email protected]>
-Date: Wed, 8 Jul 2020 10:46:13 -0400
-Subject: fix: use proper page size for mac arm64
-
-This fix temporarily sets page size to 16k for mac arm64 so that mksnapshot can run on x64
-and generate a mac arm64 snapshot
-
-diff --git a/src/base/platform/platform-posix.cc b/src/base/platform/platform-posix.cc
-index ab0d7839a453d87a8404ca791c1022871e8fd606..821a3925e91c229873f75ac024c1b388b4d0c06e 100644
---- a/src/base/platform/platform-posix.cc
-+++ b/src/base/platform/platform-posix.cc
-@@ -165,9 +165,19 @@ void* Allocate(void* hint, size_t size, OS::MemoryPermission access,
-                PageType page_type) {
-   int prot = GetProtectionFromMemoryPermission(access);
-   int flags = GetFlagsForMemoryPermission(access, page_type);
-+#if defined(__APPLE__) && V8_TARGET_ARCH_ARM64 && defined(__x86_64__)
-+  // XXX: This logic is simple and leaky as it is only used for mksnapshot.
-+  size_t alignment = 16384;
-+  void* result = mmap(hint, size + alignment, prot, flags, kMmapFd,
-+                      kMmapFdOffset);
-+  if (result == MAP_FAILED) return nullptr;
-+  return reinterpret_cast<void*>(
-+      RoundUp(reinterpret_cast<uintptr_t>(result), alignment));
-+#else
-   void* result = mmap(hint, size, prot, flags, kMmapFd, kMmapFdOffset);
-   if (result == MAP_FAILED) return nullptr;
-   return result;
-+#endif
- }
- 
- #endif  // !V8_OS_FUCHSIA
-@@ -224,7 +234,9 @@ void OS::Initialize(bool hard_abort, const char* const gc_fake_mmap) {
- }
- 
- int OS::ActivationFrameAlignment() {
--#if V8_TARGET_ARCH_ARM
-+#if defined(__APPLE__) && V8_TARGET_ARCH_ARM
-+  return 4;
-+#elif V8_TARGET_ARCH_ARM
-   // On EABI ARM targets this is required for fp correctness in the
-   // runtime system.
-   return 8;

+ 2 - 1
script/release/ci-release-build.js

@@ -33,7 +33,8 @@ const circleCIJobs = circleCIPublishWorkflows.concat([
 
 const vstsArmJobs = [
   'electron-arm-testing',
-  'electron-arm2-testing',
+  'electron-osx-arm64-testing',
+  'electron-mas-arm64-testing',
   'electron-arm64-testing',
   'electron-woa-testing'
 ];

+ 1 - 1
script/verify-ffmpeg.py

@@ -48,7 +48,7 @@ def main():
     # FIXME: Enable after ELECTRON_ENABLE_LOGGING works again
     # env['ELECTRON_ENABLE_LOGGING'] = 'true'
     testargs = [electron, test_path]
-    if sys.platform == 'win32' and platform.machine() == 'ARM64':
+    if sys.platform != 'linux' and (platform.machine() == 'ARM64' or os.environ.get('TARGET_ARCH') == 'arm64'):
       testargs.append('--disable-accelerated-video-decode')
     subprocess.check_call(testargs, env=env)
   except subprocess.CalledProcessError as e:

+ 4 - 1
script/verify-mksnapshot.py

@@ -32,7 +32,10 @@ def main():
         print('ok mksnapshot successfully created snapshot_blob.bin.')
         context_snapshot = 'v8_context_snapshot.bin'
         if platform.system() == 'Darwin':
-          context_snapshot = 'v8_context_snapshot.x86_64.bin'
+          if os.environ.get('TARGET_ARCH') == 'arm64':
+            context_snapshot = 'v8_context_snapshot.arm64.bin'
+          else:
+            context_snapshot = 'v8_context_snapshot.x86_64.bin'
         context_snapshot_path = os.path.join(app_path, context_snapshot)
         gen_binary = get_binary_path('v8_context_snapshot_generator', \
                                     app_path)

+ 1 - 5
spec-main/api-app-spec.ts

@@ -594,7 +594,7 @@ describe('app module', () => {
     });
   });
 
-  describe('app.get/setLoginItemSettings API', function () {
+  ifdescribe(process.platform !== 'linux' && !process.mas)('app.get/setLoginItemSettings API', function () {
     const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
     const processStartArgs = [
       '--processStart', `"${path.basename(process.execPath)}"`,
@@ -611,10 +611,6 @@ describe('app module', () => {
       '/d'
     ];
 
-    before(function () {
-      if (process.platform === 'linux' || process.mas) this.skip();
-    });
-
     beforeEach(() => {
       app.setLoginItemSettings({ openAtLogin: false });
       app.setLoginItemSettings({ openAtLogin: false, path: updateExe, args: processStartArgs });

+ 1 - 1
spec-main/api-auto-updater-spec.ts

@@ -54,7 +54,7 @@ ifdescribe(!process.mas)('autoUpdater module', function () {
       });
     });
 
-    ifdescribe(process.platform === 'darwin')('on Mac', function () {
+    ifdescribe(process.platform === 'darwin' && process.arch !== 'arm64')('on Mac', function () {
       it('emits an error when the application is unsigned', async () => {
         const errorEvent = emittedOnce(autoUpdater, 'error');
         autoUpdater.setFeedURL({ url: '' });

+ 2 - 3
spec-main/api-autoupdater-darwin-spec.ts

@@ -6,15 +6,14 @@ import * as fs from 'fs-extra';
 import * as os from 'os';
 import * as path from 'path';
 import { AddressInfo } from 'net';
+import { ifdescribe } from './spec-helpers';
 
 const features = process._linkedBinding('electron_common_features');
 
 const fixturesPath = path.resolve(__dirname, 'fixtures');
 
 // We can only test the auto updater on darwin non-component builds
-const describeFn = (process.platform === 'darwin' && !process.mas && !features.isComponentBuild() ? describe : describe.skip);
-
-describeFn('autoUpdater behavior', function () {
+ifdescribe(process.platform === 'darwin' && process.arch !== 'arm64' && !process.mas && !features.isComponentBuild())('autoUpdater behavior', function () {
   this.timeout(120000);
 
   let identity = '';

+ 1 - 1
spec-main/api-content-tracing-spec.ts

@@ -5,7 +5,7 @@ import * as path from 'path';
 import { ifdescribe, delay } from './spec-helpers';
 
 // FIXME: The tests are skipped on arm/arm64.
-ifdescribe(!(process.platform !== 'win32' && ['arm', 'arm64'].includes(process.arch)))('contentTracing', () => {
+ifdescribe(!(['arm', 'arm64'].includes(process.arch)))('contentTracing', () => {
   const record = async (options: TraceConfig | TraceCategoriesAndOptions, outputFilePath: string | undefined, recordTimeInMilliseconds = 1e1) => {
     await app.whenReady();
 

+ 1 - 1
spec-main/api-net-spec.ts

@@ -1483,7 +1483,7 @@ describe('net module', () => {
       const urlRequest = net.request(serverUrl);
       urlRequest.end(randomBuffer(kOneMegaByte));
       const [error] = await emittedOnce(urlRequest, 'error');
-      expect(error.message).to.be.oneOf(['net::ERR_CONNECTION_RESET', 'net::ERR_CONNECTION_ABORTED']);
+      expect(error.message).to.be.oneOf(['net::ERR_FAILED', 'net::ERR_CONNECTION_RESET', 'net::ERR_CONNECTION_ABORTED']);
     });
 
     it('should not emit any event after close', async () => {

+ 29 - 22
spec-main/api-web-contents-spec.ts

@@ -1738,28 +1738,6 @@ describe('webContents module', () => {
       expect(data).to.be.an.instanceof(Buffer).that.is.not.empty();
     });
 
-    it('respects custom settings', async () => {
-      await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'print-to-pdf.html'));
-
-      const data = await w.webContents.printToPDF({
-        pageRanges: {
-          from: 0,
-          to: 2
-        },
-        landscape: true
-      });
-
-      const doc = await pdfjs.getDocument(data).promise;
-
-      // Check that correct # of pages are rendered.
-      expect(doc.numPages).to.equal(3);
-
-      // Check that PDF is generated in landscape mode.
-      const firstPage = await doc.getPage(1);
-      const { width, height } = firstPage.getViewport({ scale: 100 });
-      expect(width).to.be.greaterThan(height);
-    });
-
     it('does not crash when called multiple times in parallel', async () => {
       const promises = [];
       for (let i = 0; i < 3; i++) {
@@ -1783,6 +1761,35 @@ describe('webContents module', () => {
         expect(data).to.be.an.instanceof(Buffer).that.is.not.empty();
       }
     });
+
+    describe('using a large document', () => {
+      beforeEach(async () => {
+        w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } });
+        await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'print-to-pdf.html'));
+      });
+
+      afterEach(closeAllWindows);
+
+      it('respects custom settings', async () => {
+        const data = await w.webContents.printToPDF({
+          pageRanges: {
+            from: 0,
+            to: 2
+          },
+          landscape: true
+        });
+
+        const doc = await pdfjs.getDocument(data).promise;
+
+        // Check that correct # of pages are rendered.
+        expect(doc.numPages).to.equal(3);
+
+        // Check that PDF is generated in landscape mode.
+        const firstPage = await doc.getPage(1);
+        const { width, height } = firstPage.getViewport({ scale: 100 });
+        expect(width).to.be.greaterThan(height);
+      });
+    });
   });
 
   describe('PictureInPicture video', () => {

+ 2 - 8
spec/asar-spec.js

@@ -5,7 +5,7 @@ const path = require('path');
 const temp = require('temp').track();
 const util = require('util');
 const { emittedOnce } = require('./events-helpers');
-const { ifit } = require('./spec-helpers');
+const { ifit, ifdescribe } = require('./spec-helpers');
 const nativeImage = require('electron').nativeImage;
 
 const features = process._linkedBinding('electron_common_features');
@@ -1385,17 +1385,11 @@ describe('asar package', function () {
       });
     });
 
-    describe('child_process.execFile', function () {
+    ifdescribe(process.platform === 'darwin' && process.arch !== 'arm64')('child_process.execFile', function () {
       const execFile = ChildProcess.execFile;
       const execFileSync = ChildProcess.execFileSync;
       const echo = path.join(asarDir, 'echo.asar', 'echo');
 
-      before(function () {
-        if (process.platform !== 'darwin') {
-          this.skip();
-        }
-      });
-
       it('executes binaries', function (done) {
         execFile(echo, ['test'], function (error, stdout) {
           try {