Skip to content

Commit

Permalink
Merge pull request #27 from vixalien/tests
Browse files Browse the repository at this point in the history
Add tests
  • Loading branch information
ahgilak authored Jun 16, 2024
2 parents f332ea0 + cf56ea0 commit 9289337
Show file tree
Hide file tree
Showing 21 changed files with 1,043 additions and 33 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Meson Tests

on:
workflow_dispatch:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v1
with:
deno-version: v1.x

- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install meson libglib2.0-dev
- name: Create Build Environment
run: meson setup ${{github.workspace}}/build

- name: Build
working-directory: ${{github.workspace}}/build
shell: bash
run: ninja

- name: Stylecheck
run: deno fmt --check

- name: Lint
run: deno lint

- name: Test
working-directory: ${{github.workspace}}/build
shell: bash
run: meson test --suite deno-gi --print-errorlogs
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.vscode/

deno.json
deno.jsonc
deno.lock
/subprojects
11 changes: 11 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"unstable": ["ffi"],
"lock": false,
"tasks": {
"test": "deno test --unstable-ffi --allow-ffi --allow-read test/unit/**/*"
},
"imports": {
"https://github.com/ahgilak/deno_gi/raw/main/mod.ts": "./mod.ts"
},
"exclude": ["subprojects", "build"]
}
12 changes: 12 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
project(
'deno-gi',
'c',
version: 'master',
)

gnome = import('gnome')
fs = import('fs')

deno = find_program('deno', required: true)

subdir('tests')
4 changes: 4 additions & 0 deletions src/base_utils/ffipp.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// deno-lint-ignore-file no-explicit-any
type ReplaceNestedObjectType<T> = {
[K in keyof T]: T[K] extends FFIFunc<infer R, infer A>
? (...args: { [a in keyof A]: ArgType<A[a]> }) => R
Expand Down Expand Up @@ -25,6 +26,9 @@ type FFIPPType<I, O> = {
...args: { [a in keyof A]: FFIPPType<A[a], A[a]> }
): FFIFunc<O, typeof args>;
symbol: string;
size: number;
serilize: (arg_0: I) => any;
deserilize: (arg_0: any) => O;
};

type FFIFunc<R, A extends FFIPPType<any, any>[]> = {
Expand Down
2 changes: 1 addition & 1 deletion src/base_utils/ffipp.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="./ffipp.d.ts"/>

import "npm:reflect-metadata";
import "https://esm.sh/reflect-metadata@0.2.2";

export function createType({
symbol,
Expand Down
43 changes: 43 additions & 0 deletions src/bindings/ranges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { GITypeTag } from "https://raw.githubusercontent.com/ahgilak/deno_gi/main/src/bindings/enums.js";

export const NumberRanges = {
[GITypeTag.INT8]: [-128, 127],
[GITypeTag.INT16]: [-32_768, 32_767],
[GITypeTag.INT32]: [-2_147_483_648, 2_147_483_647],
[GITypeTag.INT64]: [-9_223_372_036_854_775_808n, 9_223_372_036_854_775_807n],
[GITypeTag.UINT8]: [0, 255],
[GITypeTag.UINT16]: [0, 65_535],
[GITypeTag.UINT32]: [0, 4_294_967_295],
[GITypeTag.UINT64]: [0, 18_446_744_073_709_551_615n],
// TODO: not sure about these, will probably not be precise
[GITypeTag.FLOAT]: [-3.4e38, 3.4e38],
[GITypeTag.DOUBLE]: [-1.8e308, 1.8e308],
// not sure
[GITypeTag.GTYPE]: [0, 18_446_744_073_709_551_615n],
};

export function ensure_number_range(
type: number,
value: number | bigint,
): void {
const range = NumberRanges[type];
if (!range) return;

// check if the number is in the given range
const [min, max] = range;
if (value >= min && value <= max) return;

// doubles & floats accept Infinity and NaN
if (type === GITypeTag.DOUBLE || type === GITypeTag.FLOAT) {
if ([Infinity, -Infinity, NaN].includes(value as number)) return;
}

// check if the type supports BigInts
if (typeof value === "bigint" && !(typeof max === "bigint")) {
throw new TypeError("can't convert BigInt to number");
}

const tag = Object.entries(GITypeTag)
.find(([_, name]) => name === type)?.[0]?.toLowerCase() ?? "type";
throw new RangeError(`value is out of range for ${tag}`);
}
88 changes: 64 additions & 24 deletions src/types/argument.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { objectByInfo } from "../utils/gobject.js";
import { boxArray, unboxArray } from "./argument/array.js";
import { boxInterface, unboxInterface } from "./argument/interface.js";
import { unboxList } from "./argument/list.js";
import { ensure_number_range } from "../bindings/ranges.ts";

export function initArgument(type) {
const tag = g.type_info.get_tag(type);
Expand Down Expand Up @@ -115,7 +116,6 @@ export function unboxArgument(type, buffer, offset) {

export function boxArgument(type, value) {
const buffer = new ArrayBuffer(8);
if (!value) return buffer;
const dataView = new ExtendedDataView(buffer);
const tag = g.type_info.get_tag(type);

Expand All @@ -124,53 +124,83 @@ export function boxArgument(type, value) {
dataView.setInt32(value);
break;

case GITypeTag.UINT8:
dataView.setUint8(value);
case GITypeTag.UINT8: {
const normalized = normalizeNumber(value);
ensure_number_range(GITypeTag.UINT8, normalized);
dataView.setUint8(normalized);
break;
}

case GITypeTag.UNICHAR:
dataView.setUint32(String(value).codePointAt(0));
break;

case GITypeTag.INT8:
dataView.setInt8(value);
case GITypeTag.INT8: {
const normalized = normalizeNumber(value);
ensure_number_range(GITypeTag.INT8, normalized);
dataView.setInt8(normalized);
break;
}

case GITypeTag.UINT16:
dataView.setUint16(value);
case GITypeTag.UINT16: {
const normalized = normalizeNumber(value);
ensure_number_range(GITypeTag.UINT16, normalized);
dataView.setUint16(normalized);
break;
}

case GITypeTag.INT16:
dataView.setInt16(value);
case GITypeTag.INT16: {
const normalized = normalizeNumber(value);
ensure_number_range(GITypeTag.INT16, normalized);
dataView.setInt16(normalized);
break;
}

case GITypeTag.UINT32:
dataView.setUint32(value);
case GITypeTag.UINT32: {
const normalized = normalizeNumber(value);
ensure_number_range(GITypeTag.UINT32, normalized);
dataView.setUint32(normalized);
break;
}

case GITypeTag.INT32:
dataView.setInt32(value);
case GITypeTag.INT32: {
const normalized = normalizeNumber(value);
ensure_number_range(GITypeTag.INT32, normalized);
dataView.setInt32(normalized);
break;
}

case GITypeTag.UINT64:
case GITypeTag.UINT64: {
const normalized = normalizeNumber(value);
ensure_number_range(GITypeTag.UINT64, normalized);
dataView.setBigUint64(
typeof value === "bigint" ? value : Math.trunc(value),
typeof normalized === "bigint" ? normalized : Math.trunc(normalized),
);
break;
}

case GITypeTag.INT64:
case GITypeTag.INT64: {
const normalized = normalizeNumber(value);
ensure_number_range(GITypeTag.INT64, normalized);
dataView.setBigInt64(
typeof value === "bigint" ? value : Math.trunc(value),
typeof normalized === "bigint" ? normalized : Math.trunc(normalized),
);
break;
}

case GITypeTag.FLOAT:
dataView.setFloat32(value);
case GITypeTag.FLOAT: {
const normalized = normalizeNumber(value, true);
ensure_number_range(GITypeTag.FLOAT, normalized);
dataView.setFloat32(normalized);
break;
}

case GITypeTag.DOUBLE:
dataView.setFloat64(value);
case GITypeTag.DOUBLE: {
const normalized = normalizeNumber(value, true);
ensure_number_range(GITypeTag.DOUBLE, normalized);
dataView.setFloat64(normalized);
break;
}

case GITypeTag.UTF8:
case GITypeTag.FILENAME:
Expand All @@ -180,6 +210,7 @@ export function boxArgument(type, value) {
break;

case GITypeTag.GTYPE:
ensure_number_range(GITypeTag.GTYPE, value);
dataView.setBigUint64(value);
break;

Expand All @@ -190,9 +221,12 @@ export function boxArgument(type, value) {
value = boxArray(type, value);
}

dataView.setBigUint64(
cast_ptr_u64(cast_buf_ptr(value)),
);
if (value) {
dataView.setBigUint64(
cast_ptr_u64(cast_buf_ptr(value)),
);
}

break;
}

Expand All @@ -208,3 +242,9 @@ export function boxArgument(type, value) {

return buffer;
}

function normalizeNumber(value, allowNaN = false) {
if (value === undefined) return 0;
if (allowNaN && isNaN(value)) return NaN;
return value || 0;
}
17 changes: 16 additions & 1 deletion src/types/argument/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { GITypeTag } from "../../bindings/enums.js";
import g from "../../bindings/mod.js";
import { boxArgument, unboxArgument } from "../argument.js";

function getTypeSize(typeTag) {
/**
* @param {number} typeTag
* @returns {number}
*/
export function getTypeSize(typeTag) {
switch (typeTag) {
case GITypeTag.BOOLEAN:
return 1 << 2;
Expand Down Expand Up @@ -35,6 +39,12 @@ function getTypeSize(typeTag) {
}
}

/**
* @param {Deno.PointerValue} type
* @param {number | bigint} pointer
* @param {number} length
* @returns {import("../../base_utils/ffipp.d.ts").TypedArray}
*/
export function unboxArray(type, pointer, length) {
if (!pointer) {
return null;
Expand Down Expand Up @@ -64,6 +74,11 @@ export function unboxArray(type, pointer, length) {
return result;
}

/**
* @param {Deno.PointerValue} typeInfo
* @param {any[]} values
* @returns {Uint8Array}
*/
export function boxArray(typeInfo, values) {
const isZeroTerminated = g.type_info.is_zero_terminated(typeInfo);

Expand Down
23 changes: 17 additions & 6 deletions src/types/callable.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { createCallback } from "./callback.js";

export function createArg(info) {
const type = g.arg_info.get_type(info);
const name = g.base_info.get_name(info);
const arrLength = g.type_info.get_array_length(type);
const isSkip = g.arg_info.is_skip(info);
const direction = g.arg_info.get_direction(info);
Expand All @@ -24,6 +25,7 @@ export function createArg(info) {
const isReturn = g.arg_info.is_return_value(info);
return {
type,
name,
arrLength,
isSkip,
direction,
Expand Down Expand Up @@ -58,12 +60,21 @@ export function parseCallableArgs(info) {

for (let i = 0; i < inArgsDetail.length; i++) {
const detail = inArgsDetail[i];
const value = args.shift();
const pointer = new ExtendedDataView(boxArgument(detail.type, value))
.getBigUint64();
inArgs[i] = pointer;
if (detail.lengthArg >= 0) {
inArgs[detail.lengthArg] = value.length || value.byteLength || 0;

try {
const value = args.shift();
const pointer = new ExtendedDataView(boxArgument(detail.type, value))
.getBigUint64();
inArgs[i] = pointer;
if (detail.lengthArg >= 0) {
inArgs[detail.lengthArg] = value.length || value.byteLength || 0;
}
} catch (error) {
if (error instanceof RangeError) {
error.message = `Argument ${detail.name}: ${error.message}`;
}

throw error;
}
}

Expand Down
8 changes: 8 additions & 0 deletions subprojects/gobject-introspection.wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[wrap-git]
directory = gobject-introspection
url = https://gitlab.gnome.org/GNOME/gobject-introspection.git
revision = 1.80.1
depth = 1

[provide]
program_names = g-ir-scanner, g-ir-compiler
3 changes: 3 additions & 0 deletions tests/gi/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
gi_dep = subproject('gobject-introspection')

subdir('regress')
Loading

0 comments on commit 9289337

Please sign in to comment.