cleanup test debugging

This commit is contained in:
John Kleinschmidt
2024-11-13 11:04:23 -05:00
parent 8d6df4b241
commit 0d3c0f7799
6 changed files with 6 additions and 410 deletions

View File

@@ -244,7 +244,7 @@ jobs:
echo ELECTRON_TEST_RESULTS_DIR=$PWD\junit >> $env:GITHUB_ENV
# Get which tests are on this shard
$tests_files=node script\split-tests ${{ matrix.shard }} 2
echo tests_files="$tests_files spec\woa-screencap-spec.ts" >> $env:GITHUB_ENV
echo tests_files="$tests_files" >> $env:GITHUB_ENV
if ('${{ inputs.target-arch }}' -eq 'x86') {
echo npm_config_arch=ia32 >> $env:GITHUB_ENV
}

View File

@@ -1,275 +0,0 @@
// 2>nul||@goto :batch
/*
:batch
@echo off
setlocal
:: find csc.exe
set "csc="
for /r "%SystemRoot%\Microsoft.NET\Framework\" %%# in ("*csc.exe") do set "csc=%%#"
if not exist "%csc%" (
echo no .net framework installed
exit /b 10
)
if not exist "%~n0.exe" (
call %csc% /nologo /r:"Microsoft.VisualBasic.dll" /out:"%~n0.exe" "%~dpsfnx0" || (
exit /b %errorlevel%
)
)
%~n0.exe %*
endlocal & exit /b %errorlevel%
*/
// reference
// https://gallery.technet.microsoft.com/scriptcenter/eeff544a-f690-4f6b-a586-11eea6fc5eb8
using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections.Generic;
using Microsoft.VisualBasic;
/// Provides functions to capture the entire screen, or a particular window, and save it to a file.
public class ScreenCapture
{
/// Creates an Image object containing a screen shot the active window
public Image CaptureActiveWindow()
{
return CaptureWindow(User32.GetForegroundWindow());
}
/// Creates an Image object containing a screen shot of the entire desktop
public Image CaptureScreen()
{
return CaptureWindow(User32.GetDesktopWindow());
}
/// Creates an Image object containing a screen shot of a specific window
private Image CaptureWindow(IntPtr handle)
{
// get te hDC of the target window
IntPtr hdcSrc = User32.GetWindowDC(handle);
// get the size
User32.RECT windowRect = new User32.RECT();
User32.GetWindowRect(handle, ref windowRect);
int width = windowRect.right - windowRect.left;
int height = windowRect.bottom - windowRect.top;
// create a device context we can copy to
IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
// create a bitmap we can copy it to,
// using GetDeviceCaps to get the width/height
IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
// select the bitmap object
IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
// bitblt over
GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY);
// restore selection
GDI32.SelectObject(hdcDest, hOld);
// clean up
GDI32.DeleteDC(hdcDest);
User32.ReleaseDC(handle, hdcSrc);
// get a .NET image object for it
Image img = Image.FromHbitmap(hBitmap);
// free up the Bitmap object
GDI32.DeleteObject(hBitmap);
return img;
}
public void CaptureActiveWindowToFile(string filename, ImageFormat format)
{
Image img = CaptureActiveWindow();
img.Save(filename, format);
}
public void CaptureScreenToFile(string filename, ImageFormat format)
{
Image img = CaptureScreen();
img.Save(filename, format);
}
static bool fullscreen = true;
static String file = "screenshot.bmp";
static System.Drawing.Imaging.ImageFormat format = System.Drawing.Imaging.ImageFormat.Bmp;
static String windowTitle = "";
static void parseArguments()
{
String[] arguments = Environment.GetCommandLineArgs();
if (arguments.Length == 1)
{
printHelp();
Environment.Exit(0);
}
if (arguments[1].ToLower().Equals("/h") || arguments[1].ToLower().Equals("/help"))
{
printHelp();
Environment.Exit(0);
}
file = arguments[1];
Dictionary<String, System.Drawing.Imaging.ImageFormat> formats =
new Dictionary<String, System.Drawing.Imaging.ImageFormat>();
formats.Add("bmp", System.Drawing.Imaging.ImageFormat.Bmp);
formats.Add("emf", System.Drawing.Imaging.ImageFormat.Emf);
formats.Add("exif", System.Drawing.Imaging.ImageFormat.Exif);
formats.Add("jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
formats.Add("jpeg", System.Drawing.Imaging.ImageFormat.Jpeg);
formats.Add("gif", System.Drawing.Imaging.ImageFormat.Gif);
formats.Add("png", System.Drawing.Imaging.ImageFormat.Png);
formats.Add("tiff", System.Drawing.Imaging.ImageFormat.Tiff);
formats.Add("wmf", System.Drawing.Imaging.ImageFormat.Wmf);
String ext = "";
if (file.LastIndexOf('.') > -1)
{
ext = file.ToLower().Substring(file.LastIndexOf('.') + 1, file.Length - file.LastIndexOf('.') - 1);
}
else
{
Console.WriteLine("Invalid file name - no extension");
Environment.Exit(7);
}
try
{
format = formats[ext];
}
catch (Exception e)
{
Console.WriteLine("Probably wrong file format:" + ext);
Console.WriteLine(e.ToString());
Environment.Exit(8);
}
if (arguments.Length > 2)
{
windowTitle = arguments[2];
fullscreen = false;
}
}
static void printHelp()
{
//clears the extension from the script name
String scriptName = Environment.GetCommandLineArgs()[0];
scriptName = scriptName.Substring(0, scriptName.Length);
Console.WriteLine(scriptName + " captures the screen or the active window and saves it to a file.");
Console.WriteLine("");
Console.WriteLine("Usage:");
Console.WriteLine(" " + scriptName + " filename [WindowTitle]");
Console.WriteLine("");
Console.WriteLine("filename - the file where the screen capture will be saved");
Console.WriteLine(" allowed file extensions are - Bmp,Emf,Exif,Gif,Icon,Jpeg,Png,Tiff,Wmf.");
Console.WriteLine("WindowTitle - instead of capture whole screen you can point to a window ");
Console.WriteLine(" with a title which will put on focus and captuted.");
Console.WriteLine(" For WindowTitle you can pass only the first few characters.");
Console.WriteLine(" If don't want to change the current active window pass only \"\"");
}
public static void Main()
{
User32.SetProcessDPIAware();
parseArguments();
ScreenCapture sc = new ScreenCapture();
if (!fullscreen && !windowTitle.Equals(""))
{
try
{
Interaction.AppActivate(windowTitle);
Console.WriteLine("setting " + windowTitle + " on focus");
}
catch (Exception e)
{
Console.WriteLine("Probably there's no window like " + windowTitle);
Console.WriteLine(e.ToString());
Environment.Exit(9);
}
}
try
{
if (fullscreen)
{
Console.WriteLine("Taking a capture of the whole screen to " + file);
sc.CaptureScreenToFile(file, format);
}
else
{
Console.WriteLine("Taking a capture of the active window to " + file);
sc.CaptureActiveWindowToFile(file, format);
}
}
catch (Exception e)
{
Console.WriteLine("Check if file path is valid " + file);
Console.WriteLine(e.ToString());
}
}
/// Helper class containing Gdi32 API functions
private class GDI32
{
public const int SRCCOPY = 0x00CC0020; // BitBlt dwRop parameter
[DllImport("gdi32.dll")]
public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest,
int nWidth, int nHeight, IntPtr hObjectSource,
int nXSrc, int nYSrc, int dwRop);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth,
int nHeight);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
}
/// Helper class containing User32 API functions
private class User32
{
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern int SetProcessDPIAware();
}
}

View File

@@ -170,8 +170,10 @@ app.whenReady().then(async () => {
}
const cb = () => {
console.log(`In SPEC CB, process next tick with failures: ${runner.failures} for ${process.platform}`);
process.exit(runner.failures);
// Ensure the callback is called after runner is defined
process.nextTick(() => {
process.exit(runner.failures);
});
};
// Set up chai in the correct order

View File

@@ -122,7 +122,7 @@ export class ScreenCapture {
return this._expectImpl(findPoint(this.display.size), hexColor, true);
}
public async captureFrame (): Promise<NativeImage> {
private async captureFrame (): Promise<NativeImage> {
const sources = await desktopCapturer.getSources({
types: ['screen'],
thumbnailSize: this.display.size

View File

@@ -3,128 +3,16 @@ import { BrowserWindow } from 'electron/main';
import { AssertionError } from 'chai';
import { SuiteFunction, TestFunction } from 'mocha';
import { createHook } from 'node:async_hooks';
import * as childProcess from 'node:child_process';
import { readFileSync } from 'node:fs';
import * as http from 'node:http';
import * as http2 from 'node:http2';
import * as https from 'node:https';
import * as net from 'node:net';
import * as path from 'node:path';
import { relative } from 'node:path';
import { setTimeout } from 'node:timers/promises';
import * as url from 'node:url';
import { fileURLToPath } from 'node:url';
import * as v8 from 'node:v8';
const IGNORED_TYPES = [
'TIMERWRAP',
'PROMISE',
'PerformanceObserver',
'RANDOMBYTESREQUEST'
];
let asyncResources:Map<number, any>;
let hook:any;
export function initWhyIsNodeRunning () {
asyncResources = new Map();
hook = createHook({
init (asyncId, type, triggerAsyncId, resource) {
if (IGNORED_TYPES.includes(type)) {
return;
}
const stacks = captureStackTraces().slice(1);
asyncResources.set(asyncId, {
type,
resource,
stacks
});
},
destroy (asyncId) {
asyncResources.delete(asyncId);
}
});
hook.enable();
}
export function whyIsNodeRunning () {
hook.disable();
const activeAsyncResources = Array.from(asyncResources.values())
.filter(({ resource }) => resource.hasRef?.() ?? true);
console.error(`There are ${activeAsyncResources.length} handle(s) keeping the process running.`);
for (const asyncResource of activeAsyncResources) {
printStacks(asyncResource, console);
}
}
function printStacks (asyncResource:any, logger:any) {
const stacks = asyncResource.stacks.filter((stack:any) => {
const fileName = stack.getFileName();
return fileName !== null && !fileName.startsWith('node:internal/async_hooks');
});
logger.error('');
logger.error(`# ${asyncResource.type}`);
if (!stacks[0]) {
logger.error('(unknown stack trace)');
return;
}
const maxLength = stacks.reduce((length:any, stack:any) => Math.max(length, formatLocation(stack).length), 0);
for (const stack of stacks) {
const location = formatLocation(stack);
const padding = ' '.repeat(maxLength - location.length);
try {
const lines = readFileSync(normalizeFilePath(stack.getFileName()), 'utf-8').split(/\n|\r\n/);
const line = lines[stack.getLineNumber() - 1].trim();
logger.error(`${location}${padding} - ${line}`);
} catch {
logger.error(`${location}${padding}`);
}
}
}
function formatLocation (stack:any) {
const filePath = formatFilePath(stack.getFileName());
return `${filePath}:${stack.getLineNumber()}`;
}
function formatFilePath (filePath:any) {
const absolutePath = normalizeFilePath(filePath);
const relativePath = relative(process.cwd(), absolutePath);
return relativePath.startsWith('..') ? absolutePath : relativePath;
}
function normalizeFilePath (filePath:any) {
return filePath.startsWith('file://') ? fileURLToPath(filePath) : filePath;
}
// See: https://v8.dev/docs/stack-trace-api
function captureStackTraces () {
const target:any = {};
const original = Error.prepareStackTrace;
Error.prepareStackTrace = (error, stackTraces) => stackTraces; // eslint-disable-line
Error.captureStackTrace(target, captureStackTraces);
const capturedTraces = target.stack;
Error.prepareStackTrace = original;
return capturedTraces;
}
const addOnly = <T>(fn: Function): T => {
const wrapped = (...args: any[]) => {
return fn(...args);

View File

@@ -1,19 +0,0 @@
import { screen } from 'electron/main';
import { createArtifactWithRandomId } from './lib/artifacts';
import { ScreenCapture } from './lib/screen-helpers';
import { ifit } from './lib/spec-helpers';
describe('trying to get screen capture on WOA', () => {
ifit(process.platform === 'win32' && process.arch === 'arm64')('does it work', async () => {
const display = screen.getPrimaryDisplay();
const screenCapture = new ScreenCapture(display);
const frame = await screenCapture.captureFrame();
// Save the image as an artifact for better debugging
const artifactName = await createArtifactWithRandomId(
(id) => `screen-cap-${id}.png`,
frame.toPNG()
);
console.log(`Created artifact at: ${artifactName}`);
});
});