Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1007a0a
docs: Core page on how Line, Circle, PSet and Scale syntax becomes a …
KubaO Oct 3, 2026
01b3673
BUGS: Clear All Breakpoints undone by a compiler restart; a watch on …
KubaO Oct 3, 2026
d1ca988
docs: how Line, Circle and PSet read a colour value (BETA 984), with …
KubaO Oct 3, 2026
5bbbfc7
docs: LSet pads since BETA 984, UDT copy rules; RSet truncation; StrC…
KubaO Oct 3, 2026
73c7ac1
bug_repro vb6: build a project with a form (no Unattended Execution, …
KubaO Oct 3, 2026
cc1b9af
BUGS: system colour extra bytes draw black (984); colour properties s…
KubaO Oct 3, 2026
75b9a54
Authoring rule: a known defect that silently gives a wrong result is …
KubaO Oct 3, 2026
ca9ad67
docs: silent twinBASIC defects become WARNINGs (system colour bytes, …
KubaO Oct 3, 2026
e87bbd1
Authoring rule: a crash, or an error that hides a silent part, is a W…
KubaO Oct 3, 2026
f8ce7a6
docs: crash and run-twice defects become WARNINGs (Advise Nothing, IU…
KubaO Oct 3, 2026
fa8d22c
docs: colour properties of every drawing surface and control store in…
KubaO Oct 3, 2026
4087b66
bug_repro: a reproducer's VB6 project may hold a user control (.ctl, …
KubaO Oct 4, 2026
2abbc26
docs, tbbuild: --buildAndExit32 exits 0 only for an error in code not…
KubaO Oct 4, 2026
7edf9d9
bugs: damaged-project box re-measured on 995 on a private desktop: al…
KubaO Oct 4, 2026
7c00ff0
BUGS: the Debug Console offers no completion for a name typed after ?…
KubaO Oct 4, 2026
26f3727
addin-test: env lane holds P10, TB_ADDIN_TEST reaches the add-in in t…
KubaO Oct 4, 2026
8f18a1b
bug_repro: cli mode runs each command on a private desktop, records a…
KubaO Oct 4, 2026
a8c68eb
docs: addin-test has eleven lanes, the env lane among them, and takes…
KubaO Oct 4, 2026
22c59d2
BUGS: a space typed in the Debug Console accepts the IntelliSense lis…
KubaO Oct 4, 2026
ddb8fea
bug_repro: pictures for graphical defects (PngDump in tB and VB6, ima…
KubaO Oct 4, 2026
1118cbe
docs: Circle's angles on six surfaces; BUGS: angle above 2 pi uncheck…
KubaO Oct 4, 2026
3deaf28
docs: Point's NOTE says it returns 0, warns TB0009, and how to read a…
KubaO Oct 4, 2026
67ab2c7
BUGS: entries embed their compare pictures; file rewrites the path; p…
KubaO Oct 4, 2026
e57e0ba
check_examples: a form template for check_run; vb6run builds form sam…
KubaO Oct 4, 2026
48ce16b
docs: Circle samples on a form, read back with GetPixel, checked agai…
KubaO Oct 4, 2026
9958163
BUGS: file circle-positive-angle-unchecked.
KubaO Oct 4, 2026
4aae81a
BUGS: file proper-case-even-separators.
KubaO Oct 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ bugs/** -text
# The VB6 template `bug_repro.mjs new --with-vb6` copies into a reproducer's vb6/
# is CRLF in every checkout, as the bugs/** files it becomes are, and VB6 needs it.
test/repro-templates/vb6/** -text

# The PngDump modules `bug_repro.mjs new --with-images` copies into a reproducer are CRLF
# in every checkout for the same reason: PngDump.bas is VB6's, and the copy is compared
# with the template line by line.
test/repro-templates/png/** -text
8 changes: 8 additions & 0 deletions .github/actions/run-gates/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ runs:
- name: Unit-test the date formatter (test/strftime.test.mjs)
shell: bash
run: node --test test/strftime.test.mjs
# Unit tests for the pictures of a bug reproducer: PNG decoding, comparing
# and the side-by-side image (scripts/lib/png.mjs), the files behind
# repro.json's images and expect.imagesDiffer, and bug_repro.mjs's own
# refusals over fixtures. Starts no IDE and no VB6. No browser, no built
# tree, about 4 s.
- name: Unit-test the bug reproducers' pictures (test/png.test.mjs)
shell: bash
run: node --test test/png.test.mjs
# check_examples.mjs's probes: batching, crash isolation through a fake
# lane, the canaries and the fence classifier. check_examples.mjs needs a
# twinBASIC install, which CI has not; its probes need none.
Expand Down
507 changes: 487 additions & 20 deletions BUGS-TO-REPORT.md

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions WIP.ExamplesBuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,39 @@ already consumes. Two exist:
|---|---|---|
| `console` | stdole, VB (Forms), AppGlobalClassObject | everything by default |
| `packages` | the above plus all eleven other shipped packages | `Reference/Built-In/` and the package tutorials |
| `form` | `console`, a delta | a `check_run` sample that needs a real form: drawing, `Cls`, `Print`, `Scale`, a form property. Never inferred: a fence says `project=form` |

(The table is the two whole templates. The deltas over them --- `vb-private`, `cc-private`,
`wnc-private`, `cef-private`, `implicit`, `cef`, `webview2` --- are `TEMPLATE_BASE` in
`scripts/lib/example-batches.mjs`, with `form`.)

**`form` is `console` plus a real `Form1`**: `Sources/Form1.twin` (a predeclared class with the
`FormDesignerId` attribute) and `Sources/Form1.tbform` (its designer file), and its own
`tbxStage.twin`, which is console's less the line `Public Form1 As Form`. A form made with
`New Form` in code has no designer file and cannot be drawn on (`Load` raises 361, `AutoRedraw`
raises -2147467259), so the form has to be a file of the project. Four facts about it, all
measured on BETA 995:

- **An empty form has no `_children` key.** `"_children": []` in `Form1.tbform` fails the build with
`TB5247 unable to find matching form designer JSON via FormDesignerId`, and `Form1` is then
unrecognised everywhere.
- **The stand-in makes no difference to `Load Form1`.** With `Public Form1 As Form` in the stage
beside a real `Form1` class, `Form1` reaches the class's predeclared instance
(`TypeName(Form1)` is `Form1`, `Load Form1` and `AutoRedraw` work), and the stand-in stays
`Nothing`. In the `console` template, with no class, `Form1` is the stand-in and is `Nothing` at
run time. The `form` stage drops the line anyway, so that no second `Form1` exists at project
scope. A probe holds the two stage sets to "console's less that line", which is the only
thing that keeps the lists from drifting apart.
- **Fences for `form` go in a run project of their own.** Batches are grouped by template, so a
`project=form` run fence is never packed with a `console` one; a `projname=` group whose fences
all say `project=form` is one form project (`checkGroups` refuses a group of two templates, so
the `slot=file` module fence that declares `GetPixel` says `project=form` too).
- **The dispatcher unloads every form each sample leaves loaded.** A loaded form ends the
run with `[DEBUGGER] Waiting for remaining forms to close...` and keeps it waiting, and the next
sample finds the form with what the last one drew (a second sample's `GetPixel` on a form the
first had drawn on read red before it drew anything). `tbxRun` therefore calls `tbxUnloadForms`
after each sample and before its end marker; a sample that forgets `Unload Form1` costs nothing,
and the samples share no form. The VB6 harness does the same.

**`project.references` is a plain JSON array, and a hand-written entry works.** Measured:
an entry composed by hand from a package's own `Settings` --- id, name, version,
Expand Down Expand Up @@ -429,6 +462,18 @@ less two; and `Err.Source` of an error raised in the exe is the project name, wh
`ErrObject/Raise.md` differs. `Close` with no argument in a sample also closes the file the
output goes to, so the sample's next `Debug.Print` raises error 52.

**A `project=form` fence is built in VB6 with a blank `Form1.frm`.** The project then has a
`Form=Form1.frm` line and no `Unattended=-1`, which VB6 refuses for a project with a form, so
its exe is run on a private desktop (`runOnDesktop`, as a reproducer's is) and the time limit ends
one that waits on a box. A `projname=` group takes the form when any of its fences says
`project=form`; plain fences that say it are one project of their own. The harness unloads each
sample's forms before the end marker, as the twinBASIC dispatcher does, because a loaded form keeps a
VB6 exe running after `Main` returns. `Declare PtrSafe` is not VB6: `declaresForVb6` drops `PtrSafe`
and reads `LongPtr` as `Long` on a Declare statement's lines (and its continuations) and touches
nothing else, which is what lets the documentation's `GetPixel` module build. Checked on BETA 995
and in VB6: both form samples print what the pages say (`Graphics-Methods.md`'s three lines, and
the Form page's `True`).

Two things a batch runner must do that a single-fence runner need not:

- **Keep a source map.** Errors return against a generated file and line; the report names
Expand Down
48 changes: 40 additions & 8 deletions WIP.Harness.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,12 @@ Two bugs came out of the move, and neither had been noticed:

**Do not reach for `--buildAndExit32` instead.** It exists, it is real (`parseCommandLine()`
reads it, and Personal Edition is refused by name), and it is useless unattended: **nothing
is written to stdout or stderr, ever**, it exits 0 on a project the IDE flags, and when the
build genuinely fails it does not exit at all --- it sits on a "Please wait…" dialog at 100%
forever. Silent, falsely green, and hanging on the one case worth catching. Measured all
three ways.
is written to stdout or stderr, ever**, it exits 0 with the `.exe` written when the error is
in code nothing calls, and for an error the build reaches --- an unknown procedure or type,
an undeclared variable, no `Sub Main` --- it does not exit at all: it sits on a "Please
wait…" dialog at 100% forever. Silent, falsely green, and hanging on the one case worth
catching. Measured all three ways, on BETA 983 and 995 (the BUGS-TO-REPORT entry on
`--buildAndExit32`).

**It runs the IDE on a private Windows desktop, and that is not decoration.** A build tool
that seizes the keyboard mid-sentence is a build tool nobody runs while working. No window
Expand All @@ -360,11 +362,22 @@ That is the one piece of the harness that cannot be JavaScript, because it is
started running inside a job, the job object calls ([The IDE runs inside a
job](#the-ide-runs-inside-a-job)) --- and Node has no FFI without a native addon.
[scripts/lib/tb-launch.ps1](scripts/lib/tb-launch.ps1) holds those calls. It is **not run
as a file**: `tb-ide.mjs` reads the text and passes it through
as a file**: `tb-ide.mjs` reads the text and runs it through
`-EncodedCommand`, so the default execution policy --- which refuses `.ps1` files on this
machine, and which is the same policy [BOOKPLAN.md](BOOKPLAN.md) records blocking `npx.ps1`
--- never comes into it, and no `-ExecutionPolicy Bypass` has to be recommended to anyone.
Its inputs arrive as environment variables, so there is no argument quoting to get wrong.
**The script itself arrives in one too** (`TBBUILD_SCRIPT`), and `-EncodedCommand` carries only a
one-line bootstrap that takes it out of the environment, so the launched program does not
inherit it, and runs it. The script went through `-EncodedCommand` whole until it grew past about
12,000 characters: UTF-16 in base64 is 2.7 characters to each, a command line stops at 32,767,
and every launch then failed with `spawn ENAMETOOLONG`. An environment variable stops at 32,767
characters of its own; `launchOnDesktop` refuses a script over 30,000. The script also takes
`TBBUILD_STDOUT` / `TBBUILD_STDERR` (files for the program's standard output and error),
`TBBUILD_ARGS` (more of the command line, already quoted) and `TBBUILD_DIALOGS=close`, which
polls the private desktop every 250 ms for visible `#32770` boxes belonging to the program or
its job, prints each as `dialog <base64 of JSON {title, text}>` and presses its OK button:
`bug_repro`'s `cli` mode uses all four for a compiler that opens a modal box on a damaged project.

A launch that fails prints no pid, and its cause as one line on stderr, which `launchIde`
reports. Two things used to hide the cause. With its streams redirected, PowerShell writes
Expand Down Expand Up @@ -1224,9 +1237,15 @@ anybody watches. A caller can still set the variable otherwise, or leave it out
`undefined` as its value: Node leaves such a variable out of a child's environment even
when its own environment has it (measured).

**Measured on BETA 983 (P10 in WIP.HelpAddin.md):** a probe add-in printed the variable
from `Host_OnProjectLoaded`. Through `launchIde` it read `1`, from `Environ$` and from
`GetEnvironmentVariableW` alike, and with the variable left out both said it was unset. The
**Measured on BETA 983 and 995 (P10 in WIP.HelpAddin.md), and held by
[test/addin/env.test.mjs](test/addin/env.test.mjs):** the EnvProbe add-in
([probes/env](test/addin/probes/env)) prints the variable from `Host_OnProjectLoaded`.
Through `launchIde` it read `1`, from `Environ$` and from
`GetEnvironmentVariableW` alike, and with the variable left out (`env: { TB_ADDIN_TEST:
undefined }` to `Lane.open`, after `closeProject`) `Environ$` was empty and the Win32 call
said unset. The lane asserts all of that, and that the process id the add-in read is
`compilerPid`'s, that a restart prints from a different process, and that it reads `1`
again. The
path it travels: `tb-launch.ps1` calls `CreateProcess` with no environment block of its own,
so the IDE inherits the launcher's; the IDE starts the compiler, `twinBASIC_win32_noDEP.exe`,
as a direct child; and the add-in runs inside the compiler's process --- the process id it
Expand Down Expand Up @@ -1398,6 +1417,19 @@ probe lanes:
name, `_v2`, `_v3` as built, and `_v4`, none longer than the original, with NULs after
it. The export table has one name, so its sorted order, which `GetProcAddress`
searches, cannot change.
- [test/addin/env.test.mjs](test/addin/env.test.mjs), P10: it builds the EnvProbe add-in
from [test/addin/probes/env](test/addin/probes/env), which prints one line as the project
loads: `Environ$("TB_ADDIN_TEST")`, the same through `GetEnvironmentVariableW` (`(unset)`
when absent) and its process id. The first test asserts `1` twice and that the id is the
compiler's; the second restarts the compiler with `Lane.restartCompiler` and asserts `1`
twice and a different id; the third is the control, `closeProject` and then `Lane.open`
with `env: { TB_ADDIN_TEST: undefined }`, and asserts an empty `Environ$` and `(unset)`.
The probe opens nothing, so running an IDE without the variable is safe. Two IDEs in one
lane need no change to the runner, and the second takes the same port once the first has
been ended.

**Measured on BETA 995:** the eleven lanes, with the env lane (16 s alone) added, take
1 min 43 s at the default two at a time.

**Measured on BETA 983:**

Expand Down
27 changes: 16 additions & 11 deletions WIP.HelpAddin.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ tests IDE add-ins by machine, which the add-in is developed against.

**Status: Stages 1 and 2 are done, and Stage 3's index is built** --- `addin-test.bat`
operates Samples 10 and 15 end to end and leaves the registry as it found it; all fourteen
of Stage 2's questions are answered, twelve of them by eight probe lanes that fail when a
of Stage 2's questions are answered, thirteen of them by nine probe lanes that fail when a
later IDE build behaves differently; and every build publishes `tB/symbols.json`, 5,536
names at 4,086 URLs, under a drift guard that fails the build when one of its URLs goes.
Stage 3's embedded mode, a `theme` parameter the site would read, waits until the add-in
Expand Down Expand Up @@ -630,12 +630,16 @@ Everything after this stage is developed against it.
read as one. `PrintText` stores its text escaped (`<b>` as `&lt;b&gt;`), so a URL comes
back exactly as printed, `&` included.

**The probe stayed in scratch.** It was thirty lines: `Host_OnProjectLoaded` printing
`Environ$("TB_ADDIN_TEST")`, the same through `GetEnvironmentVariableW`, and its own process
id. What it measured matters only while the add-in depends on it, and the add-in checks it
itself from Stage 4 on (increment 1 below). `add-in/` holds the add-in's tree and nothing
else: `stageProject` copies the whole folder it is given and packs the copy, so a probe
kept inside it would be packed into the add-in's project.
**The probe is a lane.** It was thirty lines in scratch at first (`Host_OnProjectLoaded`
printing `Environ$("TB_ADDIN_TEST")`, the same through `GetEnvironmentVariableW`, and its
own process id), and nothing guarded the answer although the rule above depends on it.
It is now [test/addin/probes/env](test/addin/probes/env) with
[env.test.mjs](test/addin/env.test.mjs): the variable reads `1` both ways in the
compiler's process, again in the process a restart starts, and unset in an IDE started
without it. The add-in checks the variable itself from Stage 4 on (increment 1 below).
`add-in/` holds the add-in's tree and nothing else: `stageProject` copies the whole
folder it is given and packs the copy, so a probe kept inside it would be packed into
the add-in's project.
7. **A runner.** Scenarios in JavaScript under `node:test`, one IDE per test project, lanes
by `--port` as today. The add-in's pure twinBASIC logic --- word extraction, lookup --- is
tested without loading any add-in: a test project holds those modules and a
Expand Down Expand Up @@ -695,11 +699,12 @@ offline route, and [appdata.test.mjs](test/addin/appdata.test.mjs) (P6) the lane
`APPDATA` and the FAQ's answer on where add-ins go. [arch.test.mjs](test/addin/arch.test.mjs)
(P7) holds up the Add Ins page's account of which folder loads when, and the win64 builds
`buildAddin` now makes; [reload.test.mjs](test/addin/reload.test.mjs) (P8, P9) the account
of a compiler restart on the tbIDE package page and the ToolWindows page; and
of a compiler restart on the tbIDE package page and the ToolWindows page;
[entry.test.mjs](test/addin/entry.test.mjs) (P14) the entry point's NOTE on the tbIDE
package page. P9 turned up the shared id `""` of windows given none, and the test of it
package page; and [env.test.mjs](test/addin/env.test.mjs) (P10) the rule that an add-in under
test opens nothing, which reads `TB_ADDIN_TEST`. P9 turned up the shared id `""` of windows given none, and the test of it
went in the panes lane, with the rest of what a tool window does. A probe that settles a
question once, as P10's did, stays in scratch; so did the two P3 checks that need the
question once stays in scratch; so did the two P3 checks that need the
network or a changed WebView2, the live site in the frame and the colour scheme with
WebView2 preferring light.

Expand All @@ -714,7 +719,7 @@ WebView2 preferring light.
| P7 | Which bitness does the compiler start in, and does switching the build target restart it in the other one and load the other `addins` folder? **Answered, BETA 983 and 995: yes, and yes.** The target a project opens in picks the compiler --- win32 when the IDE remembers none, `twinBASIC_win64_noDEP.exe` for a project remembered as win64 --- and each compiler loads the folders of its own bitness alone, the install's and `%APPDATA%`'s. Switching the target of an open project restarts the compiler in the other bitness, and the new one loads the other folders: a 64-bit build of the probe in each win64 folder loaded, and ran 64-bit, once the project was switched to win64, and the 32-bit ones again after a switch back. | building and testing both bitnesses --- `buildAddin` builds either, and a shipped add-in needs both |
| P8 | Is a loaded add-in DLL locked against being overwritten? **Answered, BETA 983 and 995: yes.** While its IDE runs, overwriting fails (`EBUSY`) and deleting fails (`EPERM`), though renaming works; the hold outlasts the compiler's exit by a few tens of milliseconds. The P9 lane checks all three again. | the rebuild loop --- the DLL is built outside `addins`, and copied in once the IDE has ended, or renamed aside first (P9) |
| P9 | Does a compiler restart reload add-ins from disk? **Answered, BETA 983 and 995: yes.** A restart --- the restart button, a switch of build target, or the IDE's own after a crash --- removes every add-in's buttons and shortcuts, leaves its windows showing `(currently unavailable)`, kills the old compiler, so that no `Class_Terminate` runs, and starts a compiler that loads whatever file is in the folders then. With the loaded DLL renamed aside, a restart loaded nothing; with a new build put in its place, the next restart loaded it, about a second after the click, and it got its old windows back by their ids. | a rebuild loop without restarting the IDE --- it works: rename aside, copy in, restart |
| P10 | Does an environment variable set by the harness reach the add-in (`Environ$`)? **Answered, BETA 983: yes**, through the launcher, the IDE and the compiler the IDE starts. With `TB_ADDIN_TEST=1` in `launchIde`'s environment, `Environ$` and `GetEnvironmentVariableW` both returned `1` in the add-in, and a compiler started by the restart button returned it too; left out, both said it was unset. `WEBVIEW2_USER_DATA_FOLDER`, which `launchIde` always sets, arrived with the lane's port in it. | the side-effect switch |
| P10 | Does an environment variable set by the harness reach the add-in (`Environ$`)? **Answered, BETA 983 and 995: yes**, through the launcher, the IDE and the compiler the IDE starts. With `TB_ADDIN_TEST=1` in `launchIde`'s environment, `Environ$` and `GetEnvironmentVariableW` both returned `1` in the add-in, whose process id was the compiler's, and a compiler started by the restart button, a new process, returned it too; left out, `Environ$` was empty and the Win32 call said unset. Held by [test/addin/env.test.mjs](test/addin/env.test.mjs). `WEBVIEW2_USER_DATA_FOLDER`, which `launchIde` always sets, arrived with the lane's port in it. | the side-effect switch |
| P11 | Does the IDE write into its own install folder during a session? **Answered, BETA 983 and 995: no.** On 983 a compile, a compiler crash and a `tbrun` build-and-run left all 233 files byte-identical, mtimes included. On 995 the install (235 files) was byte-identical after exports, a `tbbuild` and two `tbrun` runs; `%APPDATA%` was not re-checked. | hardlinks or copies --- copies, for safety, at 380 ms |
| P12 | Does `raiseEvent` from plain tool-window HTML throw? **Answered, BETA 983 and 995: yes** --- `TypeError: Cannot read properties of null (reading 'rootEventHandler')`, and the listener is not called. An inline handler that calls the listener `AddEventListener` stored on its parent, `this.parentNode.<name>(event)`, reaches the add-in. | how the pane's events are written |
| P13 | Does the compiler's HTTP server serve any file placed under `ide\`? **Answered, BETA 983 and 995: yes**, and it is the page server, `twinBASIC_win32.exe --ide=<pid>`, not the compiler. Any file, byte for byte, below the page's own path, including one written after the IDE started; a frame with a relative `src` shows it on the IDE page's own origin. A query string makes a 404, and `.html` has no `Content-Type`. | an offline route --- it exists ([Offline](#ways-to-show-a-page)) |
Expand Down
Loading
Loading