• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visgl / luma.gl / 23384903092

21 Mar 2026 05:30PM UTC coverage: 72.417% (+9.3%) from 63.136%
23384903092

push

github

web-flow
feat(devtool); Enable LLMs to automatically test website, package for devtools upstream (#2557)

4103 of 6350 branches covered (64.61%)

Branch coverage included in aggregate %.

215 of 271 new or added lines in 13 files covered. (79.34%)

45 existing lines in 6 files now uncovered.

9124 of 11915 relevant lines covered (76.58%)

277.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.95
/modules/test-utils/src/create-test-device.ts
1
// luma.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
import type {Device, CanvasContextProps} from '@luma.gl/core';
6
import {luma, log} from '@luma.gl/core';
7
import {webgl2Adapter, WebGLDevice} from '@luma.gl/webgl';
8
import {webgpuAdapter, WebGPUDevice} from '@luma.gl/webgpu';
9
import {nullAdapter} from './null-device/null-adapter';
10
import {NullDevice} from './null-device/null-device';
11

12
const DEFAULT_CANVAS_CONTEXT_PROPS: CanvasContextProps = {width: 1, height: 1};
57✔
13

14
/** A null device intended for testing - @note Only available after getTestDevices() has completed */
15
let nullDevicePromise: Promise<NullDevice> | null = null;
57✔
16
/** This WebGL Device can be used directly but will not have WebGL debugging initialized */
17
let webglDevicePromise: Promise<WebGLDevice> | null = null;
57✔
18
/** A shared offscreen WebGL device for presentation-context tests */
19
let presentationWebglDevicePromise: Promise<WebGLDevice | null> | null = null;
57✔
20
/** A WebGL 2 Device intended for testing - @note Only available after getTestDevices() has completed */
21
let webgpuDevicePromise: Promise<WebGPUDevice | null> | null = null;
57✔
22

23
/** Includes WebGPU device if available */
24
export async function getTestDevices(
25
  types: Readonly<('webgl' | 'webgpu' | 'null' | 'unknown')[]> = ['webgl', 'webgpu']
65✔
26
): Promise<Device[]> {
27
  const promises = types.map(type => getTestDevice(type));
142✔
28
  const devices = await Promise.all(promises);
65✔
29
  return devices.filter(device => device !== null);
142✔
30
}
31

32
export async function getTestDevice(
33
  type: 'webgl' | 'webgpu' | 'null' | 'unknown'
34
): Promise<Device | null> {
35
  switch (type) {
142!
36
    case 'webgl':
37
      return getOrCreateWebGLTestDevicePromise();
64✔
38
    case 'webgpu':
39
      return getOrCreateWebGPUTestDevicePromise();
64✔
40
    case 'null':
41
      return getOrCreateNullTestDevicePromise();
14✔
42
    case 'unknown':
43
      return null;
×
44
  }
45
}
46

47
/** returns WebGPU device promise, if available */
48
export function getWebGPUTestDevice(): Promise<WebGPUDevice | null> {
49
  return getOrCreateWebGPUTestDevicePromise();
39✔
50
}
51

52
/** returns WebGL device promise, if available */
53
export async function getWebGLTestDevice(): Promise<WebGLDevice> {
54
  return getOrCreateWebGLTestDevicePromise();
152✔
55
}
56

57
/** returns an offscreen WebGL device promise for presentation-context tests, if available */
58
export async function getPresentationWebGLTestDevice(): Promise<WebGLDevice | null> {
59
  return getOrCreatePresentationWebGLTestDevicePromise();
5✔
60
}
61

62
/** returns null device promise, if available */
63
export async function getNullTestDevice(): Promise<NullDevice> {
64
  return getOrCreateNullTestDevicePromise();
10✔
65
}
66

67
function getOrCreateWebGPUTestDevicePromise(): Promise<WebGPUDevice | null> {
68
  webgpuDevicePromise ||= makeWebGPUTestDevice();
103✔
69
  return webgpuDevicePromise;
103✔
70
}
71

72
function getOrCreateWebGLTestDevicePromise(): Promise<WebGLDevice> {
73
  webglDevicePromise ||= makeWebGLTestDevice();
216✔
74
  return webglDevicePromise;
216✔
75
}
76

77
function getOrCreatePresentationWebGLTestDevicePromise(): Promise<WebGLDevice | null> {
78
  presentationWebglDevicePromise ||= makePresentationWebGLTestDevice();
5✔
79
  return presentationWebglDevicePromise;
5✔
80
}
81

82
function getOrCreateNullTestDevicePromise(): Promise<NullDevice> {
83
  nullDevicePromise ||= makeNullTestDevice();
24✔
84
  return nullDevicePromise;
24✔
85
}
86

87
async function makeWebGPUTestDevice(): Promise<WebGPUDevice | null> {
88
  const webgpuDeviceResolvers = withResolvers<WebGPUDevice | null>();
26✔
89
  try {
26✔
90
    const webgpuDevice = (await luma.createDevice({
26✔
91
      id: 'webgpu-test-device',
92
      type: 'webgpu',
93
      adapters: [webgpuAdapter],
94
      createCanvasContext: DEFAULT_CANVAS_CONTEXT_PROPS,
95
      debug: true
96
    })) as unknown as WebGPUDevice;
97
    webgpuDeviceResolvers.resolve(webgpuDevice);
26✔
98
  } catch (error) {
UNCOV
99
    log.error(String(error))();
×
100
    // @ts-ignore TODO
UNCOV
101
    webgpuDeviceResolvers.resolve(null);
×
102
  }
103
  return webgpuDeviceResolvers.promise;
26✔
104
}
105

106
/** returns WebGL device promise, if available */
107
async function makeWebGLTestDevice(): Promise<WebGLDevice> {
108
  const webglDeviceResolvers = withResolvers<WebGLDevice>();
48✔
109
  try {
48✔
110
    const webglDevice = (await luma.createDevice({
48✔
111
      id: 'webgl-test-device',
112
      type: 'webgl',
113
      adapters: [webgl2Adapter],
114
      createCanvasContext: DEFAULT_CANVAS_CONTEXT_PROPS,
115
      debug: true
116
    })) as unknown as WebGLDevice;
117
    webglDevice.lost.finally(() => {
48✔
118
      if (webglDevicePromise === webglDeviceResolvers.promise) {
6!
NEW
119
        webglDevicePromise = null;
×
120
      }
121
    });
122
    webglDeviceResolvers.resolve(webglDevice);
48✔
123
  } catch (error) {
124
    log.error(String(error))();
×
125
    // @ts-ignore TODO
126
    webglDeviceResolvers.resolve(null);
×
127
  }
128
  return webglDeviceResolvers.promise;
48✔
129
}
130

131
async function makePresentationWebGLTestDevice(): Promise<WebGLDevice | null> {
132
  if (typeof OffscreenCanvas === 'undefined') {
1!
NEW
133
    return null;
×
134
  }
135

136
  const presentationWebGLDeviceResolvers = withResolvers<WebGLDevice | null>();
1✔
137
  try {
1✔
138
    const webglDevice = (await luma.createDevice({
1✔
139
      id: 'webgl-presentation-context-test-device',
140
      type: 'webgl',
141
      adapters: [webgl2Adapter],
142
      createCanvasContext: {canvas: new OffscreenCanvas(4, 4)},
143
      debug: true
144
    })) as unknown as WebGLDevice;
145
    webglDevice.lost.finally(() => {
1✔
146
      if (presentationWebglDevicePromise === presentationWebGLDeviceResolvers.promise) {
1!
NEW
147
        presentationWebglDevicePromise = null;
×
148
      }
149
    });
150
    presentationWebGLDeviceResolvers.resolve(webglDevice);
1✔
151
  } catch (error) {
NEW
152
    log.error(String(error))();
×
NEW
153
    presentationWebGLDeviceResolvers.resolve(null);
×
154
  }
155
  return presentationWebGLDeviceResolvers.promise;
1✔
156
}
157

158
/** returns null device promise, if available */
159
async function makeNullTestDevice(): Promise<NullDevice> {
160
  const nullDeviceResolvers = withResolvers<NullDevice>();
7✔
161
  try {
7✔
162
    const nullDevice = (await luma.createDevice({
7✔
163
      id: 'null-test-device',
164
      type: 'null',
165
      adapters: [nullAdapter],
166
      createCanvasContext: DEFAULT_CANVAS_CONTEXT_PROPS,
167
      debug: true
168
    })) as unknown as NullDevice;
169
    nullDeviceResolvers.resolve(nullDevice);
7✔
170
  } catch (error) {
171
    log.error(String(error))();
×
172
    // @ts-ignore TODO
173
    nullDevicePromise = Promise.resolve(null);
×
174
  }
175
  return nullDeviceResolvers.promise;
7✔
176
}
177

178
// HELPERS
179

180
// TODO - replace with Promise.withResolvers once we upgrade TS baseline
181
function withResolvers<T>(): {
182
  promise: Promise<T>;
183
  resolve: (t: T) => void;
184
  reject: (error: Error) => void;
185
} {
186
  let resolve;
187
  let reject;
188
  const promise = new Promise<T>((_resolve, _reject) => {
82✔
189
    resolve = _resolve;
82✔
190
    reject = _reject;
82✔
191
  });
192
  // @ts-ignore Assigned in callback.
193
  return {promise, resolve, reject};
82✔
194
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc