mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 03:27:48 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 91b0bbaae6 |
@@ -39,7 +39,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.4'
|
go-version: '1.25.1'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
@@ -96,7 +96,7 @@ jobs:
|
|||||||
- name: Build TinyGo release tarball
|
- name: Build TinyGo release tarball
|
||||||
run: make release -j3
|
run: make release -j3
|
||||||
- name: Test stdlib packages
|
- name: Test stdlib packages
|
||||||
run: make tinygo-test TEST_ADDITIONAL_FLAGS="-scheduler=tasks"
|
run: make tinygo-test
|
||||||
- name: Make release artifact
|
- name: Make release artifact
|
||||||
run: cp -p build/release.tar.gz build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
run: cp -p build/release.tar.gz build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
@@ -134,7 +134,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.4'
|
go-version: '1.25.1'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||||
run: go install -tags=llvm${{ matrix.version }}
|
run: go install -tags=llvm${{ matrix.version }}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
sudo rm -rf /usr/local/share/boost
|
sudo rm -rf /usr/local/share/boost
|
||||||
df -h
|
df -h
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
|||||||
+23
-28
@@ -16,15 +16,7 @@ jobs:
|
|||||||
# Build Linux binaries, ready for release.
|
# Build Linux binaries, ready for release.
|
||||||
# This runs inside an Alpine Linux container so we can more easily create a
|
# This runs inside an Alpine Linux container so we can more easily create a
|
||||||
# statically linked binary.
|
# statically linked binary.
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
goarch: [ amd64, arm64 ]
|
|
||||||
include:
|
|
||||||
- goarch: amd64
|
|
||||||
os: ubuntu-latest
|
|
||||||
- goarch: arm64
|
|
||||||
os: ubuntu-24.04-arm # TODO: use a -latest variant?
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
container:
|
container:
|
||||||
image: golang:1.25-alpine
|
image: golang:1.25-alpine
|
||||||
outputs:
|
outputs:
|
||||||
@@ -39,7 +31,7 @@ jobs:
|
|||||||
# We're not on a multi-user machine, so this is safe.
|
# We're not on a multi-user machine, so this is safe.
|
||||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Extract TinyGo version
|
- name: Extract TinyGo version
|
||||||
@@ -48,7 +40,7 @@ jobs:
|
|||||||
- name: Cache Go
|
- name: Cache Go
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
key: go-cache-linux-alpine-${{ matrix.goarch }}-v1-${{ hashFiles('go.mod') }}
|
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
~/go/pkg/mod
|
~/go/pkg/mod
|
||||||
@@ -81,7 +73,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-19-linux-alpine-${{ matrix.goarch }}-v1
|
key: llvm-build-20-linux-alpine-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -105,7 +97,7 @@ jobs:
|
|||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-alpine-${{ matrix.goarch }}-v1
|
key: binaryen-linux-alpine-v1
|
||||||
path: build/wasm-opt
|
path: build/wasm-opt
|
||||||
- name: Build Binaryen
|
- name: Build Binaryen
|
||||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
@@ -124,28 +116,28 @@ jobs:
|
|||||||
- name: Build TinyGo release
|
- name: Build TinyGo release
|
||||||
run: |
|
run: |
|
||||||
make release deb -j3 STATIC=1
|
make release deb -j3 STATIC=1
|
||||||
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||||
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.goarch }}.deb
|
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
name: linux-amd64-double-zipped-${{ steps.version.outputs.version }}
|
||||||
path: |
|
path: |
|
||||||
/tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
/tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||||
/tmp/tinygo_${{ steps.version.outputs.version }}-${{ matrix.goarch }}.deb
|
/tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||||
test-linux-build:
|
test-linux-build:
|
||||||
# Test the binaries built in the build-linux job by running the smoke tests.
|
# Test the binaries built in the build-linux job by running the smoke tests.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.4'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||||
@@ -172,7 +164,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
@@ -187,9 +179,9 @@ jobs:
|
|||||||
simavr \
|
simavr \
|
||||||
ninja-build
|
ninja-build
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.4'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
@@ -280,8 +272,11 @@ jobs:
|
|||||||
# part of the release tarball.
|
# part of the release tarball.
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
goarch: [ arm ]
|
goarch: [ arm, arm64 ]
|
||||||
include:
|
include:
|
||||||
|
- goarch: arm64
|
||||||
|
toolchain: aarch64-linux-gnu
|
||||||
|
libc: arm64
|
||||||
- goarch: arm
|
- goarch: arm
|
||||||
toolchain: arm-linux-gnueabihf
|
toolchain: arm-linux-gnueabihf
|
||||||
libc: armhf
|
libc: armhf
|
||||||
@@ -289,7 +284,7 @@ jobs:
|
|||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Get TinyGo version
|
- name: Get TinyGo version
|
||||||
id: version
|
id: version
|
||||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||||
@@ -301,9 +296,9 @@ jobs:
|
|||||||
g++-${{ matrix.toolchain }} \
|
g++-${{ matrix.toolchain }} \
|
||||||
libc6-dev-${{ matrix.libc }}-cross
|
libc6-dev-${{ matrix.libc }}-cross
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.4'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
target: tinygo-llvm-build
|
target: tinygo-llvm-build
|
||||||
context: .
|
context: .
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
||||||
run: sudo apt-get remove llvm-18
|
run: sudo apt-get remove llvm-18
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Pull musl, bdwgc
|
- name: Pull musl, bdwgc
|
||||||
run: |
|
run: |
|
||||||
git submodule update --init lib/musl lib/bdwgc
|
git submodule update --init lib/musl lib/bdwgc
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # fetch all history (no sparse checkout)
|
fetch-depth: 0 # fetch all history (no sparse checkout)
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
scoop install ninja binaryen
|
scoop install ninja binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Extract TinyGo version
|
- name: Extract TinyGo version
|
||||||
@@ -39,9 +39,9 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.4'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore cached LLVM source
|
- name: Restore cached LLVM source
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
@@ -143,11 +143,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
scoop install binaryen
|
scoop install binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.4'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@@ -173,11 +173,11 @@ jobs:
|
|||||||
maximum-size: 24GB
|
maximum-size: 24GB
|
||||||
disk-root: "C:"
|
disk-root: "C:"
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.4'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@@ -209,11 +209,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
scoop install binaryen && scoop install wasmtime@29.0.1
|
scoop install binaryen && scoop install wasmtime@29.0.1
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.4'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|||||||
+2
-6
@@ -363,7 +363,6 @@ TEST_PACKAGES_FAST = \
|
|||||||
path \
|
path \
|
||||||
reflect \
|
reflect \
|
||||||
sync \
|
sync \
|
||||||
testing \
|
|
||||||
testing/iotest \
|
testing/iotest \
|
||||||
text/scanner \
|
text/scanner \
|
||||||
unicode \
|
unicode \
|
||||||
@@ -480,8 +479,7 @@ TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
|||||||
TEST_IOFS := false
|
TEST_IOFS := false
|
||||||
endif
|
endif
|
||||||
|
|
||||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic'
|
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|^Fuzz'
|
||||||
TEST_ADDITIONAL_FLAGS ?=
|
|
||||||
|
|
||||||
# Test known-working standard library packages.
|
# Test known-working standard library packages.
|
||||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||||
@@ -489,7 +487,7 @@ TEST_ADDITIONAL_FLAGS ?=
|
|||||||
tinygo-test:
|
tinygo-test:
|
||||||
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
||||||
@# TestParseAndBytesRoundTrip/P256/Generic: relies on t.Skip() which is not implemented
|
@# TestParseAndBytesRoundTrip/P256/Generic: relies on t.Skip() which is not implemented
|
||||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
$(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||||
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
|
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
|
||||||
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
||||||
@# For more details, see the comments on issue #3143.
|
@# For more details, see the comments on issue #3143.
|
||||||
@@ -622,8 +620,6 @@ smoketest: testchdir
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
|
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pico2-ice examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
# test simulated boards on play.tinygo.org
|
# test simulated boards on play.tinygo.org
|
||||||
ifneq ($(WASM), 0)
|
ifneq ($(WASM), 0)
|
||||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||||
|
|||||||
+4
-7
@@ -33,13 +33,10 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
||||||
if options.GoCompatibility {
|
// Note: when this gets updated, also update the Go compatibility matrix:
|
||||||
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
||||||
// Note: when this gets updated, also update the Go compatibility matrix:
|
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
|
||||||
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
|
||||||
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the Go toolchain version isn't too new, if we haven't been
|
// Check that the Go toolchain version isn't too new, if we haven't been
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ func TestBinarySize(t *testing.T) {
|
|||||||
tests := []sizeTest{
|
tests := []sizeTest{
|
||||||
// microcontrollers
|
// microcontrollers
|
||||||
{"hifive1b", "examples/echo", 3884, 280, 0, 2268},
|
{"hifive1b", "examples/echo", 3884, 280, 0, 2268},
|
||||||
{"microbit", "examples/serial", 2844, 360, 8, 2272},
|
{"microbit", "examples/serial", 2852, 360, 8, 2272},
|
||||||
{"wioterminal", "examples/pininterrupt", 7349, 1491, 116, 6912},
|
{"wioterminal", "examples/pininterrupt", 7337, 1491, 116, 6912},
|
||||||
|
|
||||||
// TODO: also check wasm. Right now this is difficult, because
|
// TODO: also check wasm. Right now this is difficult, because
|
||||||
// wasm binaries are run through wasm-opt and therefore the
|
// wasm binaries are run through wasm-opt and therefore the
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ func (c *Config) StackSize() uint64 {
|
|||||||
|
|
||||||
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
|
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
|
||||||
func (c *Config) MaxStackAlloc() uint64 {
|
func (c *Config) MaxStackAlloc() uint64 {
|
||||||
if c.StackSize() >= 16*1024 {
|
if c.StackSize() > 32*1024 {
|
||||||
return 1024
|
return 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ type Options struct {
|
|||||||
WITPackage string // pass through to wasm-tools component embed invocation
|
WITPackage string // pass through to wasm-tools component embed invocation
|
||||||
WITWorld string // pass through to wasm-tools component embed -w option
|
WITWorld string // pass through to wasm-tools component embed -w option
|
||||||
ExtLDFlags []string
|
ExtLDFlags []string
|
||||||
GoCompatibility bool // enable to check for Go version compatibility
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||||
|
|||||||
@@ -32,9 +32,6 @@ const (
|
|||||||
// Whether this is a full or partial Go parameter (int, slice, etc).
|
// Whether this is a full or partial Go parameter (int, slice, etc).
|
||||||
// The extra context parameter is not a Go parameter.
|
// The extra context parameter is not a Go parameter.
|
||||||
paramIsGoParam = 1 << iota
|
paramIsGoParam = 1 << iota
|
||||||
|
|
||||||
// Whether this is a readonly parameter (for example, a string pointer).
|
|
||||||
paramIsReadonly
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||||
@@ -170,7 +167,6 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
suffix := strconv.Itoa(i)
|
suffix := strconv.Itoa(i)
|
||||||
isString := false
|
|
||||||
if goType != nil {
|
if goType != nil {
|
||||||
// Try to come up with a good suffix for this struct field,
|
// Try to come up with a good suffix for this struct field,
|
||||||
// depending on which Go type it's based on.
|
// depending on which Go type it's based on.
|
||||||
@@ -187,16 +183,12 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
|||||||
suffix = []string{"r", "i"}[i]
|
suffix = []string{"r", "i"}[i]
|
||||||
case types.String:
|
case types.String:
|
||||||
suffix = []string{"data", "len"}[i]
|
suffix = []string{"data", "len"}[i]
|
||||||
isString = true
|
|
||||||
}
|
}
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
suffix = []string{"context", "funcptr"}[i]
|
suffix = []string{"context", "funcptr"}[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||||
if isString {
|
|
||||||
subInfos[0].flags |= paramIsReadonly
|
|
||||||
}
|
|
||||||
paramInfos = append(paramInfos, subInfos...)
|
paramInfos = append(paramInfos, subInfos...)
|
||||||
}
|
}
|
||||||
return paramInfos
|
return paramInfos
|
||||||
|
|||||||
@@ -250,9 +250,6 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
|||||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||||
switch keyType := keyType.Underlying().(type) {
|
switch keyType := keyType.Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
// TODO: unsafe.Pointer is also a binary key, but to support that we
|
|
||||||
// need to fix an issue with interp first (see
|
|
||||||
// https://github.com/tinygo-org/tinygo/pull/4898).
|
|
||||||
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -142,11 +142,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
||||||
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
||||||
}
|
}
|
||||||
if paramInfo.flags¶mIsReadonly != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
|
|
||||||
// Readonly pointer parameters (like strings) benefit from being marked as readonly.
|
|
||||||
readonly := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)
|
|
||||||
llvmFn.AddAttributeAtIndex(i+1, readonly)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a number of function or parameter attributes, depending on the
|
// Set a number of function or parameter attributes, depending on the
|
||||||
|
|||||||
@@ -74,14 +74,6 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||||
if arch := b.archFamily(); arch != "arm" {
|
|
||||||
// Some targets pretend to be linux/arm for compatibility but aren't
|
|
||||||
// actually such a system. Make sure we emit an error instead of
|
|
||||||
// creating inline assembly that will fail to compile.
|
|
||||||
// See: https://github.com/tinygo-org/tinygo/issues/4959
|
|
||||||
return llvm.Value{}, b.makeError(call.Pos(), "system calls are not supported: target emulates a linux/arm system on "+arch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement the EABI system call convention for Linux.
|
// Implement the EABI system call convention for Linux.
|
||||||
// Source: syscall(2) man page.
|
// Source: syscall(2) man page.
|
||||||
args := []llvm.Value{}
|
args := []llvm.Value{}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -50,7 +50,7 @@ unsafe.String.throw: ; preds = %entry
|
|||||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
declare void @runtime.unsafeSlicePanic(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
|
|||||||
Vendored
+3
-3
@@ -77,7 +77,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||||
@@ -91,7 +91,7 @@ entry:
|
|||||||
ret %runtime._string %5
|
ret %runtime._string %5
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||||
@@ -116,7 +116,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||||
|
|||||||
Vendored
+8
-8
@@ -31,13 +31,13 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.stringLen(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %s.len
|
ret i32 %s.len
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.stringIndex(ptr %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%.not = icmp ult i32 %index, %s.len
|
%.not = icmp ult i32 %index, %s.len
|
||||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
@@ -55,16 +55,16 @@ lookup.throw: ; preds = %entry
|
|||||||
declare void @runtime.lookupPanic(ptr) #1
|
declare void @runtime.lookupPanic(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.stringCompareEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
declare i1 @runtime.stringEqual(ptr, i32, ptr, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.stringCompareUnequal(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
||||||
%1 = xor i1 %0, true
|
%1 = xor i1 %0, true
|
||||||
@@ -72,16 +72,16 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.stringCompareLarger(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
|
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.stringLookup(ptr %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = zext i8 %x to i32
|
%0 = zext i8 %x to i32
|
||||||
%.not = icmp ugt i32 %s.len, %0
|
%.not = icmp ugt i32 %s.len, %0
|
||||||
|
|||||||
@@ -256,7 +256,6 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
|||||||
"reflect/": false,
|
"reflect/": false,
|
||||||
"runtime/": false,
|
"runtime/": false,
|
||||||
"sync/": true,
|
"sync/": true,
|
||||||
"testing/": true,
|
|
||||||
"tinygo/": false,
|
"tinygo/": false,
|
||||||
"unique/": false,
|
"unique/": false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1635,7 +1635,6 @@ func main() {
|
|||||||
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
||||||
monitor := flag.Bool("monitor", false, "enable serial monitor")
|
monitor := flag.Bool("monitor", false, "enable serial monitor")
|
||||||
baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor")
|
baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor")
|
||||||
gocompatibility := flag.Bool("go-compatibility", true, "enable to check for Go versions compatibility, you can also configure this by setting the TINYGO_GOCOMPATIBILITY environment variable")
|
|
||||||
|
|
||||||
// Internal flags, that are only intended for TinyGo development.
|
// Internal flags, that are only intended for TinyGo development.
|
||||||
printIR := flag.Bool("internal-printir", false, "print LLVM IR")
|
printIR := flag.Bool("internal-printir", false, "print LLVM IR")
|
||||||
@@ -1713,16 +1712,6 @@ func main() {
|
|||||||
ocdCommands = strings.Split(*ocdCommandsString, ",")
|
ocdCommands = strings.Split(*ocdCommandsString, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
val, ok := os.LookupEnv("TINYGO_GOCOMPATIBILITY")
|
|
||||||
if ok {
|
|
||||||
b, err := strconv.ParseBool(val)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "could not parse TINYGO_GOCOMPATIBILITY value %q: %v\n", val, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
*gocompatibility = b
|
|
||||||
}
|
|
||||||
|
|
||||||
options := &compileopts.Options{
|
options := &compileopts.Options{
|
||||||
GOOS: goenv.Get("GOOS"),
|
GOOS: goenv.Get("GOOS"),
|
||||||
GOARCH: goenv.Get("GOARCH"),
|
GOARCH: goenv.Get("GOARCH"),
|
||||||
@@ -1759,7 +1748,6 @@ func main() {
|
|||||||
Timeout: *timeout,
|
Timeout: *timeout,
|
||||||
WITPackage: witPackage,
|
WITPackage: witPackage,
|
||||||
WITWorld: witWorld,
|
WITWorld: witWorld,
|
||||||
GoCompatibility: *gocompatibility,
|
|
||||||
}
|
}
|
||||||
if *printCommands {
|
if *printCommands {
|
||||||
options.PrintCommands = printCommand
|
options.PrintCommands = printCommand
|
||||||
|
|||||||
@@ -1,173 +0,0 @@
|
|||||||
//go:build pico2_ice
|
|
||||||
|
|
||||||
// Most of the info is from
|
|
||||||
// https://pico2-ice.tinyvision.ai/md_pinout.html although
|
|
||||||
// (2025-09-07) RP4 appears twice in that pinout - the schematic is
|
|
||||||
// more clear. Consistent with other RPi boards, we use GPn instead of
|
|
||||||
// RPn to reference the RPi connected pins.
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
// GPIO pins
|
|
||||||
const (
|
|
||||||
GP0 = GPIO0
|
|
||||||
GP1 = GPIO1
|
|
||||||
GP2 = GPIO2
|
|
||||||
GP3 = GPIO3
|
|
||||||
GP4 = GPIO4
|
|
||||||
GP5 = GPIO5
|
|
||||||
GP6 = GPIO6
|
|
||||||
GP7 = GPIO7
|
|
||||||
GP8 = GPIO8
|
|
||||||
GP9 = GPIO9
|
|
||||||
GP10 = GPIO10
|
|
||||||
GP11 = GPIO11
|
|
||||||
GP12 = GPIO12
|
|
||||||
GP13 = GPIO13
|
|
||||||
GP14 = GPIO14
|
|
||||||
GP15 = GPIO15
|
|
||||||
GP16 = GPIO16
|
|
||||||
GP17 = GPIO17
|
|
||||||
GP18 = GPIO18
|
|
||||||
GP19 = GPIO19
|
|
||||||
GP20 = GPIO20
|
|
||||||
GP21 = GPIO21
|
|
||||||
GP22 = GPIO22
|
|
||||||
GP23 = GPIO23
|
|
||||||
GP24 = GPIO24
|
|
||||||
GP25 = GPIO25
|
|
||||||
GP26 = GPIO26
|
|
||||||
GP27 = GPIO27
|
|
||||||
GP28 = GPIO28
|
|
||||||
GP29 = GPIO29
|
|
||||||
GP30 = GPIO30
|
|
||||||
GP31 = GPIO31
|
|
||||||
GP32 = GPIO32
|
|
||||||
GP33 = GPIO33
|
|
||||||
GP34 = GPIO34
|
|
||||||
GP35 = GPIO35
|
|
||||||
GP36 = GPIO36
|
|
||||||
GP37 = GPIO37
|
|
||||||
GP38 = GPIO38
|
|
||||||
GP39 = GPIO39
|
|
||||||
GP40 = GPIO40
|
|
||||||
GP41 = GPIO41
|
|
||||||
GP42 = GPIO42
|
|
||||||
GP43 = GPIO43
|
|
||||||
GP44 = GPIO44
|
|
||||||
GP45 = GPIO45
|
|
||||||
GP46 = GPIO46
|
|
||||||
GP47 = GPIO47
|
|
||||||
|
|
||||||
// RPi pins shared with ICE. The ICE number is what appears on
|
|
||||||
// the board silkscreen.
|
|
||||||
ICE9 = GP28
|
|
||||||
ICE11 = GP29
|
|
||||||
ICE14 = GP7
|
|
||||||
ICE15 = GP6
|
|
||||||
ICE16 = GP5
|
|
||||||
ICE17 = GP4
|
|
||||||
ICE18 = GP27
|
|
||||||
ICE19 = GP23
|
|
||||||
ICE20 = GP22
|
|
||||||
ICE21 = GP26
|
|
||||||
ICE23 = GP25
|
|
||||||
ICE25 = GP30
|
|
||||||
ICE26 = GP24
|
|
||||||
ICE27 = GP20
|
|
||||||
|
|
||||||
// FPGA Clock pin.
|
|
||||||
ICE35_G0 = GP21
|
|
||||||
|
|
||||||
// Silkscreen & Pinout names
|
|
||||||
ICE_SSN = ICE16
|
|
||||||
ICE_SO = ICE14
|
|
||||||
ICE_SI = ICE17
|
|
||||||
ICE_CK = ICE15
|
|
||||||
SD = GP2
|
|
||||||
SC = GP3
|
|
||||||
|
|
||||||
FPGA_RSTN = GP31
|
|
||||||
A3 = GP32
|
|
||||||
A1 = GP33
|
|
||||||
A4 = GP34
|
|
||||||
A2 = GP35
|
|
||||||
B3 = GP36
|
|
||||||
B1 = GP37
|
|
||||||
B4 = GP38
|
|
||||||
B2 = GP39
|
|
||||||
N0 = GP40 // On the board these are labeled "~0"
|
|
||||||
N1 = GP41
|
|
||||||
N2 = GP42
|
|
||||||
N3 = GP43
|
|
||||||
N4 = GP44
|
|
||||||
N5 = GP45
|
|
||||||
N6 = GP46
|
|
||||||
|
|
||||||
// Functions from Schematic.
|
|
||||||
ICE_DONE = GP40
|
|
||||||
USB_BOOT = GP42
|
|
||||||
|
|
||||||
// Button
|
|
||||||
SW1 = GP42
|
|
||||||
BOOTSEL = GP42
|
|
||||||
|
|
||||||
// Tricolor LEDs
|
|
||||||
LED_RED = GP1
|
|
||||||
LED_GREEN = GP0
|
|
||||||
LED_BLUE = GP9
|
|
||||||
|
|
||||||
// Onboard LED
|
|
||||||
LED = LED_GREEN
|
|
||||||
|
|
||||||
// Onboard crystal oscillator frequency, in MHz.
|
|
||||||
xoscFreq = 12 // MHz
|
|
||||||
)
|
|
||||||
|
|
||||||
// This board does not define default i2c pins.
|
|
||||||
const (
|
|
||||||
I2C0_SDA_PIN = NoPin
|
|
||||||
I2C0_SCL_PIN = NoPin
|
|
||||||
I2C1_SDA_PIN = NoPin
|
|
||||||
I2C1_SCL_PIN = NoPin
|
|
||||||
)
|
|
||||||
|
|
||||||
// SPI default pins
|
|
||||||
const (
|
|
||||||
// Default Serial Clock Bus 0 for SPI communications
|
|
||||||
SPI0_SCK_PIN = GPIO18
|
|
||||||
// Default Serial Out Bus 0 for SPI communications
|
|
||||||
SPI0_SDO_PIN = GPIO19 // Tx
|
|
||||||
// Default Serial In Bus 0 for SPI communications
|
|
||||||
SPI0_SDI_PIN = GPIO16 // Rx
|
|
||||||
|
|
||||||
// Default Serial Clock Bus 1 for SPI communications
|
|
||||||
SPI1_SCK_PIN = GPIO10
|
|
||||||
// Default Serial Out Bus 1 for SPI communications
|
|
||||||
SPI1_SDO_PIN = GPIO11 // Tx
|
|
||||||
// Default Serial In Bus 1 for SPI communications
|
|
||||||
SPI1_SDI_PIN = GPIO12 // Rx
|
|
||||||
)
|
|
||||||
|
|
||||||
// UART pins
|
|
||||||
const (
|
|
||||||
UART0_TX_PIN = GPIO0
|
|
||||||
UART0_RX_PIN = GPIO1
|
|
||||||
UART1_TX_PIN = GPIO8
|
|
||||||
UART1_RX_PIN = GPIO9
|
|
||||||
UART_TX_PIN = UART0_TX_PIN
|
|
||||||
UART_RX_PIN = UART0_RX_PIN
|
|
||||||
)
|
|
||||||
|
|
||||||
var DefaultUART = UART0
|
|
||||||
|
|
||||||
// USB identifiers
|
|
||||||
const (
|
|
||||||
usb_STRING_PRODUCT = "Pico2"
|
|
||||||
usb_STRING_MANUFACTURER = "Raspberry Pi"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
usb_VID uint16 = 0x2E8A
|
|
||||||
usb_PID uint16 = 0x000A
|
|
||||||
)
|
|
||||||
@@ -69,5 +69,3 @@ const eraseBlockSizeValue = 4096
|
|||||||
func eraseBlockSize() int64 {
|
func eraseBlockSize() int64 {
|
||||||
return eraseBlockSizeValue
|
return eraseBlockSizeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
const spiMaxBufferSize = 255 // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
|
||||||
|
|||||||
@@ -90,5 +90,3 @@ const eraseBlockSizeValue = 4096
|
|||||||
func eraseBlockSize() int64 {
|
func eraseBlockSize() int64 {
|
||||||
return eraseBlockSizeValue
|
return eraseBlockSizeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
const spiMaxBufferSize = 0xffff // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
|
||||||
|
|||||||
@@ -108,5 +108,3 @@ const eraseBlockSizeValue = 4096
|
|||||||
func eraseBlockSize() int64 {
|
func eraseBlockSize() int64 {
|
||||||
return eraseBlockSizeValue
|
return eraseBlockSizeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
const spiMaxBufferSize = 0xffff // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
|
||||||
|
|||||||
@@ -301,16 +301,18 @@ func (spi *SPI) Transfer(w byte) (byte, error) {
|
|||||||
// padded until they fit: if len(w) > len(r) the extra bytes received will be
|
// padded until they fit: if len(w) > len(r) the extra bytes received will be
|
||||||
// dropped and if len(w) < len(r) extra 0 bytes will be sent.
|
// dropped and if len(w) < len(r) extra 0 bytes will be sent.
|
||||||
func (spi *SPI) Tx(w, r []byte) error {
|
func (spi *SPI) Tx(w, r []byte) error {
|
||||||
// Unfortunately the hardware (on the nrf52832) only supports a limited
|
// Unfortunately the hardware (on the nrf52832) only supports up to 255
|
||||||
// amount of bytes in the buffers (depending on the chip), so if either w or
|
// bytes in the buffers, so if either w or r is longer than that the
|
||||||
// r is longer than that the transfer needs to be broken up in pieces.
|
// transfer needs to be broken up in pieces.
|
||||||
|
// The nrf52840 supports far larger buffers however, which isn't yet
|
||||||
|
// supported.
|
||||||
for len(r) != 0 || len(w) != 0 {
|
for len(r) != 0 || len(w) != 0 {
|
||||||
// Prepare the SPI transfer: set the DMA pointers and lengths.
|
// Prepare the SPI transfer: set the DMA pointers and lengths.
|
||||||
// read buffer
|
// read buffer
|
||||||
nr := uint32(len(r))
|
nr := uint32(len(r))
|
||||||
if nr > 0 {
|
if nr > 0 {
|
||||||
if nr > spiMaxBufferSize {
|
if nr > 255 {
|
||||||
nr = spiMaxBufferSize
|
nr = 255
|
||||||
}
|
}
|
||||||
spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
|
spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
|
||||||
r = r[nr:]
|
r = r[nr:]
|
||||||
@@ -320,8 +322,8 @@ func (spi *SPI) Tx(w, r []byte) error {
|
|||||||
// write buffer
|
// write buffer
|
||||||
nw := uint32(len(w))
|
nw := uint32(len(w))
|
||||||
if nw > 0 {
|
if nw > 0 {
|
||||||
if nw > spiMaxBufferSize {
|
if nw > 255 {
|
||||||
nw = spiMaxBufferSize
|
nw = 255
|
||||||
}
|
}
|
||||||
spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
|
spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
|
||||||
w = w[nw:]
|
w = w[nw:]
|
||||||
|
|||||||
@@ -124,31 +124,6 @@ const (
|
|||||||
fnXIP pinFunc = 0
|
fnXIP pinFunc = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// validPins confirms that the SPI pin selection is a legitimate one
|
|
||||||
// for the the 2040 chip.
|
|
||||||
func (spi *SPI) validPins(config SPIConfig) error {
|
|
||||||
var okSDI, okSDO, okSCK bool
|
|
||||||
switch spi.Bus {
|
|
||||||
case rp.SPI0:
|
|
||||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
|
|
||||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
|
|
||||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
|
|
||||||
case rp.SPI1:
|
|
||||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
|
|
||||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
|
|
||||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case !okSDI:
|
|
||||||
return errSPIInvalidSDI
|
|
||||||
case !okSDO:
|
|
||||||
return errSPIInvalidSDO
|
|
||||||
case !okSCK:
|
|
||||||
return errSPIInvalidSCK
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure configures the gpio pin as per mode.
|
// Configure configures the gpio pin as per mode.
|
||||||
func (p Pin) Configure(config PinConfig) {
|
func (p Pin) Configure(config PinConfig) {
|
||||||
if p == NoPin {
|
if p == NoPin {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import "device/rp"
|
|
||||||
|
|
||||||
// Analog pins on RP2350a.
|
// Analog pins on RP2350a.
|
||||||
const (
|
const (
|
||||||
ADC0 Pin = GPIO26
|
ADC0 Pin = GPIO26
|
||||||
@@ -14,28 +12,3 @@ const (
|
|||||||
// fifth ADC channel.
|
// fifth ADC channel.
|
||||||
thermADC = 30
|
thermADC = 30
|
||||||
)
|
)
|
||||||
|
|
||||||
// validPins confirms that the SPI pin selection is a legitimate one
|
|
||||||
// for the the 2350a chip.
|
|
||||||
func (spi *SPI) validPins(config SPIConfig) error {
|
|
||||||
var okSDI, okSDO, okSCK bool
|
|
||||||
switch spi.Bus {
|
|
||||||
case rp.SPI0:
|
|
||||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
|
|
||||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
|
|
||||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
|
|
||||||
case rp.SPI1:
|
|
||||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
|
|
||||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
|
|
||||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case !okSDI:
|
|
||||||
return errSPIInvalidSDI
|
|
||||||
case !okSDO:
|
|
||||||
return errSPIInvalidSDO
|
|
||||||
case !okSCK:
|
|
||||||
return errSPIInvalidSCK
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import "device/rp"
|
|
||||||
|
|
||||||
// RP2350B has additional pins.
|
// RP2350B has additional pins.
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -48,28 +46,3 @@ var (
|
|||||||
PWM10 = getPWMGroup(10)
|
PWM10 = getPWMGroup(10)
|
||||||
PWM11 = getPWMGroup(11)
|
PWM11 = getPWMGroup(11)
|
||||||
)
|
)
|
||||||
|
|
||||||
// validPins confirms that the SPI pin selection is a legitimate one
|
|
||||||
// for the the 2350b chip.
|
|
||||||
func (spi *SPI) validPins(config SPIConfig) error {
|
|
||||||
var okSDI, okSDO, okSCK bool
|
|
||||||
switch spi.Bus {
|
|
||||||
case rp.SPI0:
|
|
||||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20 || config.SDI == 32 || config.SDI == 36
|
|
||||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23 || config.SDO == 35 || config.SDO == 39
|
|
||||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22 || config.SCK == 34 || config.SCK == 38
|
|
||||||
case rp.SPI1:
|
|
||||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28 || config.SDI == 40 || config.SDI == 44
|
|
||||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27 || config.SDO == 31 || config.SDO == 43 || config.SDO == 47
|
|
||||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26 || config.SCK == 30 || config.SCK == 42 || config.SCK == 46
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case !okSDI:
|
|
||||||
return errSPIInvalidSDI
|
|
||||||
case !okSDO:
|
|
||||||
return errSPIInvalidSDO
|
|
||||||
case !okSCK:
|
|
||||||
return errSPIInvalidSCK
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ func (spi *SPI) SetBaudRate(br uint32) error {
|
|||||||
var prescale, postdiv uint32
|
var prescale, postdiv uint32
|
||||||
freq := CPUFrequency()
|
freq := CPUFrequency()
|
||||||
for prescale = 2; prescale < 255; prescale += 2 {
|
for prescale = 2; prescale < 255; prescale += 2 {
|
||||||
if uint64(freq) < uint64((prescale+2)*256)*uint64(br) {
|
if freq < (prescale+2)*256*br {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,9 +165,27 @@ func (spi *SPI) Configure(config SPIConfig) error {
|
|||||||
config.SDI = SPI1_SDI_PIN
|
config.SDI = SPI1_SDI_PIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := spi.validPins(config); err != nil {
|
var okSDI, okSDO, okSCK bool
|
||||||
return err
|
switch spi.Bus {
|
||||||
|
case rp.SPI0:
|
||||||
|
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
|
||||||
|
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
|
||||||
|
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
|
||||||
|
case rp.SPI1:
|
||||||
|
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
|
||||||
|
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
|
||||||
|
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case !okSDI:
|
||||||
|
return errSPIInvalidSDI
|
||||||
|
case !okSDO:
|
||||||
|
return errSPIInvalidSDO
|
||||||
|
case !okSCK:
|
||||||
|
return errSPIInvalidSCK
|
||||||
|
}
|
||||||
|
|
||||||
if config.Frequency == 0 {
|
if config.Frequency == 0 {
|
||||||
config.Frequency = defaultBaud
|
config.Frequency = defaultBaud
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,21 +8,16 @@ import "unsafe"
|
|||||||
// code linked from other languages can allocate memory without colliding with
|
// code linked from other languages can allocate memory without colliding with
|
||||||
// our GC allocations.
|
// our GC allocations.
|
||||||
|
|
||||||
// Map of allocations, where the key is the allocated pointer and the value is
|
var allocs = make(map[uintptr][]byte)
|
||||||
// the size of the allocation.
|
|
||||||
// TODO: make this a map[unsafe.Pointer]uintptr, since that results in slightly
|
|
||||||
// smaller binaries. But for that to work, unsafe.Pointer needs to be seen as a
|
|
||||||
// binary key (which it is not at the moment).
|
|
||||||
// See https://github.com/tinygo-org/tinygo/pull/4898 for details.
|
|
||||||
var allocs = make(map[*byte]uintptr)
|
|
||||||
|
|
||||||
//export malloc
|
//export malloc
|
||||||
func libc_malloc(size uintptr) unsafe.Pointer {
|
func libc_malloc(size uintptr) unsafe.Pointer {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ptr := alloc(size, nil)
|
buf := make([]byte, size)
|
||||||
allocs[(*byte)(ptr)] = size
|
ptr := unsafe.Pointer(&buf[0])
|
||||||
|
allocs[uintptr(ptr)] = buf
|
||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,8 +26,8 @@ func libc_free(ptr unsafe.Pointer) {
|
|||||||
if ptr == nil {
|
if ptr == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, ok := allocs[(*byte)(ptr)]; ok {
|
if _, ok := allocs[uintptr(ptr)]; ok {
|
||||||
delete(allocs, (*byte)(ptr))
|
delete(allocs, uintptr(ptr))
|
||||||
} else {
|
} else {
|
||||||
panic("free: invalid pointer")
|
panic("free: invalid pointer")
|
||||||
}
|
}
|
||||||
@@ -53,20 +48,18 @@ func libc_realloc(oldPtr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
|||||||
|
|
||||||
// It's hard to optimize this to expand the current buffer with our GC, but
|
// It's hard to optimize this to expand the current buffer with our GC, but
|
||||||
// it is theoretically possible. For now, just always allocate fresh.
|
// it is theoretically possible. For now, just always allocate fresh.
|
||||||
// TODO: we could skip this if the new allocation is smaller than the old.
|
buf := make([]byte, size)
|
||||||
ptr := alloc(size, nil)
|
|
||||||
|
|
||||||
if oldPtr != nil {
|
if oldPtr != nil {
|
||||||
if oldSize, ok := allocs[(*byte)(oldPtr)]; ok {
|
if oldBuf, ok := allocs[uintptr(oldPtr)]; ok {
|
||||||
oldBuf := unsafe.Slice((*byte)(oldPtr), oldSize)
|
copy(buf, oldBuf)
|
||||||
newBuf := unsafe.Slice((*byte)(ptr), size)
|
delete(allocs, uintptr(oldPtr))
|
||||||
copy(newBuf, oldBuf)
|
|
||||||
delete(allocs, (*byte)(oldPtr))
|
|
||||||
} else {
|
} else {
|
||||||
panic("realloc: invalid pointer")
|
panic("realloc: invalid pointer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allocs[(*byte)(ptr)] = size
|
ptr := unsafe.Pointer(&buf[0])
|
||||||
|
allocs[uintptr(ptr)] = buf
|
||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ func SetPanicOnFault(enabled bool) bool {
|
|||||||
|
|
||||||
func WriteHeapDump(fd uintptr)
|
func WriteHeapDump(fd uintptr)
|
||||||
|
|
||||||
func SetTraceback(level string)
|
// Unimplemented.
|
||||||
|
func SetTraceback(level string) {
|
||||||
|
}
|
||||||
|
|
||||||
func SetMemoryLimit(limit int64) int64 {
|
func SetMemoryLimit(limit int64) int64 {
|
||||||
return limit
|
return limit
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
func Callers(skip int, pc []uintptr) int {
|
func Callers(skip int, pc []uintptr) int {
|
||||||
|
if len(pc) > 0 {
|
||||||
|
// The testing package expects at least one caller in all cases.
|
||||||
|
pc[0] = 0
|
||||||
|
return 1
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -181,3 +181,19 @@ func getAuxv() []uintptr {
|
|||||||
func cgo_errno() uintptr {
|
func cgo_errno() uintptr {
|
||||||
return uintptr(*libc_errno_location())
|
return uintptr(*libc_errno_location())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unimplemented.
|
||||||
|
var MemProfileRate int = 0
|
||||||
|
|
||||||
|
// Unimplemented.
|
||||||
|
func SetBlockProfileRate(rate int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
var mutexProfileFraction int
|
||||||
|
|
||||||
|
// Unimplemented.
|
||||||
|
func SetMutexProfileFraction(rate int) int {
|
||||||
|
previous := mutexProfileFraction
|
||||||
|
mutexProfileFraction = rate
|
||||||
|
return previous
|
||||||
|
}
|
||||||
|
|||||||
@@ -273,9 +273,6 @@ func nanosecondsToTicks(ns int64) timeUnit {
|
|||||||
func sleepTicks(d timeUnit) {
|
func sleepTicks(d timeUnit) {
|
||||||
for d != 0 {
|
for d != 0 {
|
||||||
ticks := uint32(d)
|
ticks := uint32(d)
|
||||||
if d > 0xffff_ffff {
|
|
||||||
ticks = 0xffff_ffff
|
|
||||||
}
|
|
||||||
if !timerSleep(ticks) {
|
if !timerSleep(ticks) {
|
||||||
// Bail out early to handle a non-time interrupt.
|
// Bail out early to handle a non-time interrupt.
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -266,9 +266,6 @@ func nanosecondsToTicks(ns int64) timeUnit {
|
|||||||
func sleepTicks(d timeUnit) {
|
func sleepTicks(d timeUnit) {
|
||||||
for d != 0 {
|
for d != 0 {
|
||||||
ticks := uint32(d)
|
ticks := uint32(d)
|
||||||
if d > 0xffff_ffff {
|
|
||||||
ticks = 0xffff_ffff
|
|
||||||
}
|
|
||||||
if !timerSleep(ticks) {
|
if !timerSleep(ticks) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,9 +78,6 @@ func buffered() int {
|
|||||||
func sleepTicks(d timeUnit) {
|
func sleepTicks(d timeUnit) {
|
||||||
for d != 0 {
|
for d != 0 {
|
||||||
ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
|
ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
|
||||||
if d > 0x7fffff {
|
|
||||||
ticks = 0x7fffff
|
|
||||||
}
|
|
||||||
rtc_sleep(ticks)
|
rtc_sleep(ticks)
|
||||||
d -= timeUnit(ticks)
|
d -= timeUnit(ticks)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,9 +81,6 @@ func buffered() int {
|
|||||||
func sleepTicks(d timeUnit) {
|
func sleepTicks(d timeUnit) {
|
||||||
for d != 0 {
|
for d != 0 {
|
||||||
ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
|
ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
|
||||||
if d > 0x7fffff {
|
|
||||||
ticks = 0x7fffff
|
|
||||||
}
|
|
||||||
rtc_sleep(ticks)
|
rtc_sleep(ticks)
|
||||||
d -= timeUnit(ticks)
|
d -= timeUnit(ticks)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,7 +212,6 @@ func writeStream(stream *wasiStream, buf *byte, count uint, offset int64) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
remaining -= len
|
remaining -= len
|
||||||
src = src[len:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return int(count)
|
return int(count)
|
||||||
|
|||||||
@@ -1,521 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
//
|
|
||||||
// This file has been modified for use by the TinyGo compiler.
|
|
||||||
|
|
||||||
package testing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func initBenchmarkFlags() {
|
|
||||||
matchBenchmarks = flag.String("test.bench", "", "run only benchmarks matching `regexp`")
|
|
||||||
benchmarkMemory = flag.Bool("test.benchmem", false, "print memory allocations for benchmarks")
|
|
||||||
flag.Var(&benchTime, "test.benchtime", "run each benchmark for duration `d`")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
matchBenchmarks *string
|
|
||||||
benchmarkMemory *bool
|
|
||||||
benchTime = benchTimeFlag{d: 1 * time.Second} // changed during test of testing package
|
|
||||||
)
|
|
||||||
|
|
||||||
type benchTimeFlag struct {
|
|
||||||
d time.Duration
|
|
||||||
n int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *benchTimeFlag) String() string {
|
|
||||||
if f.n > 0 {
|
|
||||||
return fmt.Sprintf("%dx", f.n)
|
|
||||||
}
|
|
||||||
return time.Duration(f.d).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *benchTimeFlag) Set(s string) error {
|
|
||||||
if strings.HasSuffix(s, "x") {
|
|
||||||
n, err := strconv.ParseInt(s[:len(s)-1], 10, 0)
|
|
||||||
if err != nil || n <= 0 {
|
|
||||||
return fmt.Errorf("invalid count")
|
|
||||||
}
|
|
||||||
*f = benchTimeFlag{n: int(n)}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
d, err := time.ParseDuration(s)
|
|
||||||
if err != nil || d <= 0 {
|
|
||||||
return fmt.Errorf("invalid duration")
|
|
||||||
}
|
|
||||||
*f = benchTimeFlag{d: d}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InternalBenchmark is an internal type but exported because it is cross-package;
|
|
||||||
// it is part of the implementation of the "go test" command.
|
|
||||||
type InternalBenchmark struct {
|
|
||||||
Name string
|
|
||||||
F func(b *B)
|
|
||||||
}
|
|
||||||
|
|
||||||
// B is a type passed to Benchmark functions to manage benchmark
|
|
||||||
// timing and to specify the number of iterations to run.
|
|
||||||
//
|
|
||||||
// A benchmark ends when its Benchmark function returns or calls any of the methods
|
|
||||||
// FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods must be called
|
|
||||||
// only from the goroutine running the Benchmark function.
|
|
||||||
// The other reporting methods, such as the variations of Log and Error,
|
|
||||||
// may be called simultaneously from multiple goroutines.
|
|
||||||
//
|
|
||||||
// Like in tests, benchmark logs are accumulated during execution
|
|
||||||
// and dumped to standard output when done. Unlike in tests, benchmark logs
|
|
||||||
// are always printed, so as not to hide output whose existence may be
|
|
||||||
// affecting benchmark results.
|
|
||||||
type B struct {
|
|
||||||
common
|
|
||||||
context *benchContext
|
|
||||||
N int
|
|
||||||
benchFunc func(b *B)
|
|
||||||
bytes int64
|
|
||||||
missingBytes bool // one of the subbenchmarks does not have bytes set.
|
|
||||||
benchTime benchTimeFlag
|
|
||||||
timerOn bool
|
|
||||||
result BenchmarkResult
|
|
||||||
|
|
||||||
// report memory statistics
|
|
||||||
showAllocResult bool
|
|
||||||
// initial state of MemStats.Mallocs and MemStats.TotalAlloc
|
|
||||||
startAllocs uint64
|
|
||||||
startBytes uint64
|
|
||||||
// net total after running benchmar
|
|
||||||
netAllocs uint64
|
|
||||||
netBytes uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartTimer starts timing a test. This function is called automatically
|
|
||||||
// before a benchmark starts, but it can also be used to resume timing after
|
|
||||||
// a call to StopTimer.
|
|
||||||
func (b *B) StartTimer() {
|
|
||||||
if !b.timerOn {
|
|
||||||
b.start = time.Now()
|
|
||||||
b.timerOn = true
|
|
||||||
|
|
||||||
var mstats runtime.MemStats
|
|
||||||
runtime.ReadMemStats(&mstats)
|
|
||||||
b.startAllocs = mstats.Mallocs
|
|
||||||
b.startBytes = mstats.TotalAlloc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StopTimer stops timing a test. This can be used to pause the timer
|
|
||||||
// while performing complex initialization that you don't
|
|
||||||
// want to measure.
|
|
||||||
func (b *B) StopTimer() {
|
|
||||||
if b.timerOn {
|
|
||||||
b.duration += time.Since(b.start)
|
|
||||||
b.timerOn = false
|
|
||||||
|
|
||||||
var mstats runtime.MemStats
|
|
||||||
runtime.ReadMemStats(&mstats)
|
|
||||||
b.netAllocs += mstats.Mallocs - b.startAllocs
|
|
||||||
b.netBytes += mstats.TotalAlloc - b.startBytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetTimer zeroes the elapsed benchmark time and memory allocation counters
|
|
||||||
// and deletes user-reported metrics.
|
|
||||||
func (b *B) ResetTimer() {
|
|
||||||
if b.timerOn {
|
|
||||||
b.start = time.Now()
|
|
||||||
|
|
||||||
var mstats runtime.MemStats
|
|
||||||
runtime.ReadMemStats(&mstats)
|
|
||||||
b.startAllocs = mstats.Mallocs
|
|
||||||
b.startBytes = mstats.TotalAlloc
|
|
||||||
}
|
|
||||||
b.duration = 0
|
|
||||||
b.netAllocs = 0
|
|
||||||
b.netBytes = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBytes records the number of bytes processed in a single operation.
|
|
||||||
// If this is called, the benchmark will report ns/op and MB/s.
|
|
||||||
func (b *B) SetBytes(n int64) { b.bytes = n }
|
|
||||||
|
|
||||||
// ReportAllocs enables malloc statistics for this benchmark.
|
|
||||||
// It is equivalent to setting -test.benchmem, but it only affects the
|
|
||||||
// benchmark function that calls ReportAllocs.
|
|
||||||
func (b *B) ReportAllocs() {
|
|
||||||
b.showAllocResult = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// runN runs a single benchmark for the specified number of iterations.
|
|
||||||
func (b *B) runN(n int) {
|
|
||||||
b.N = n
|
|
||||||
runtime.GC()
|
|
||||||
b.ResetTimer()
|
|
||||||
b.StartTimer()
|
|
||||||
b.benchFunc(b)
|
|
||||||
b.StopTimer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func min(x, y int64) int64 {
|
|
||||||
if x > y {
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
func max(x, y int64) int64 {
|
|
||||||
if x < y {
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// run1 runs the first iteration of benchFunc. It reports whether more
|
|
||||||
// iterations of this benchmarks should be run.
|
|
||||||
func (b *B) run1() bool {
|
|
||||||
if ctx := b.context; ctx != nil {
|
|
||||||
// Extend maxLen, if needed.
|
|
||||||
if n := len(b.name); n > ctx.maxLen {
|
|
||||||
ctx.maxLen = n + 8 // Add additional slack to avoid too many jumps in size.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.runN(1)
|
|
||||||
return !b.hasSub
|
|
||||||
}
|
|
||||||
|
|
||||||
// run executes the benchmark.
|
|
||||||
func (b *B) run() {
|
|
||||||
if b.context != nil {
|
|
||||||
// Running go test --test.bench
|
|
||||||
b.processBench(b.context) // calls doBench and prints results
|
|
||||||
} else {
|
|
||||||
// Running func Benchmark.
|
|
||||||
b.doBench()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *B) doBench() BenchmarkResult {
|
|
||||||
// in upstream, this uses a goroutine
|
|
||||||
b.launch()
|
|
||||||
return b.result
|
|
||||||
}
|
|
||||||
|
|
||||||
// launch launches the benchmark function. It gradually increases the number
|
|
||||||
// of benchmark iterations until the benchmark runs for the requested benchtime.
|
|
||||||
// run1 must have been called on b.
|
|
||||||
func (b *B) launch() {
|
|
||||||
// Run the benchmark for at least the specified amount of time.
|
|
||||||
if b.benchTime.n > 0 {
|
|
||||||
b.runN(b.benchTime.n)
|
|
||||||
} else {
|
|
||||||
d := b.benchTime.d
|
|
||||||
b.failed = false
|
|
||||||
b.duration = 0
|
|
||||||
for n := int64(1); !b.failed && b.duration < d && n < 1e9; {
|
|
||||||
last := n
|
|
||||||
// Predict required iterations.
|
|
||||||
goalns := d.Nanoseconds()
|
|
||||||
prevIters := int64(b.N)
|
|
||||||
prevns := b.duration.Nanoseconds()
|
|
||||||
if prevns <= 0 {
|
|
||||||
// Round up, to avoid div by zero.
|
|
||||||
prevns = 1
|
|
||||||
}
|
|
||||||
// Order of operations matters.
|
|
||||||
// For very fast benchmarks, prevIters ~= prevns.
|
|
||||||
// If you divide first, you get 0 or 1,
|
|
||||||
// which can hide an order of magnitude in execution time.
|
|
||||||
// So multiply first, then divide.
|
|
||||||
n = goalns * prevIters / prevns
|
|
||||||
// Run more iterations than we think we'll need (1.2x).
|
|
||||||
n += n / 5
|
|
||||||
// Don't grow too fast in case we had timing errors previously.
|
|
||||||
n = min(n, 100*last)
|
|
||||||
// Be sure to run at least one more than last time.
|
|
||||||
n = max(n, last+1)
|
|
||||||
// Don't run more than 1e9 times. (This also keeps n in int range on 32 bit platforms.)
|
|
||||||
n = min(n, 1e9)
|
|
||||||
b.runN(int(n))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkResult contains the results of a benchmark run.
|
|
||||||
type BenchmarkResult struct {
|
|
||||||
N int // The number of iterations.
|
|
||||||
T time.Duration // The total time taken.
|
|
||||||
Bytes int64 // Bytes processed in one iteration.
|
|
||||||
|
|
||||||
MemAllocs uint64 // The total number of memory allocations.
|
|
||||||
MemBytes uint64 // The total number of bytes allocated.
|
|
||||||
}
|
|
||||||
|
|
||||||
// NsPerOp returns the "ns/op" metric.
|
|
||||||
func (r BenchmarkResult) NsPerOp() int64 {
|
|
||||||
if r.N <= 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return r.T.Nanoseconds() / int64(r.N)
|
|
||||||
}
|
|
||||||
|
|
||||||
// mbPerSec returns the "MB/s" metric.
|
|
||||||
func (r BenchmarkResult) mbPerSec() float64 {
|
|
||||||
if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllocsPerOp returns the "allocs/op" metric,
|
|
||||||
// which is calculated as r.MemAllocs / r.N.
|
|
||||||
func (r BenchmarkResult) AllocsPerOp() int64 {
|
|
||||||
if r.N <= 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int64(r.MemAllocs) / int64(r.N)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllocedBytesPerOp returns the "B/op" metric,
|
|
||||||
// which is calculated as r.MemBytes / r.N.
|
|
||||||
func (r BenchmarkResult) AllocedBytesPerOp() int64 {
|
|
||||||
if r.N <= 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int64(r.MemBytes) / int64(r.N)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a summary of the benchmark results.
|
|
||||||
// It follows the benchmark result line format from
|
|
||||||
// https://golang.org/design/14313-benchmark-format, not including the
|
|
||||||
// benchmark name.
|
|
||||||
// Extra metrics override built-in metrics of the same name.
|
|
||||||
// String does not include allocs/op or B/op, since those are reported
|
|
||||||
// by MemString.
|
|
||||||
func (r BenchmarkResult) String() string {
|
|
||||||
buf := new(strings.Builder)
|
|
||||||
fmt.Fprintf(buf, "%8d", r.N)
|
|
||||||
|
|
||||||
// Get ns/op as a float.
|
|
||||||
ns := float64(r.T.Nanoseconds()) / float64(r.N)
|
|
||||||
if ns != 0 {
|
|
||||||
buf.WriteByte('\t')
|
|
||||||
prettyPrint(buf, ns, "ns/op")
|
|
||||||
}
|
|
||||||
|
|
||||||
if mbs := r.mbPerSec(); mbs != 0 {
|
|
||||||
fmt.Fprintf(buf, "\t%7.2f MB/s", mbs)
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MemString returns r.AllocedBytesPerOp and r.AllocsPerOp in the same format as 'go test'.
|
|
||||||
func (r BenchmarkResult) MemString() string {
|
|
||||||
return fmt.Sprintf("%8d B/op\t%8d allocs/op",
|
|
||||||
r.AllocedBytesPerOp(), r.AllocsPerOp())
|
|
||||||
}
|
|
||||||
|
|
||||||
func prettyPrint(w io.Writer, x float64, unit string) {
|
|
||||||
// Print all numbers with 10 places before the decimal point
|
|
||||||
// and small numbers with four sig figs. Field widths are
|
|
||||||
// chosen to fit the whole part in 10 places while aligning
|
|
||||||
// the decimal point of all fractional formats.
|
|
||||||
var format string
|
|
||||||
switch y := math.Abs(x); {
|
|
||||||
case y == 0 || y >= 999.95:
|
|
||||||
format = "%10.0f %s"
|
|
||||||
case y >= 99.995:
|
|
||||||
format = "%12.1f %s"
|
|
||||||
case y >= 9.9995:
|
|
||||||
format = "%13.2f %s"
|
|
||||||
case y >= 0.99995:
|
|
||||||
format = "%14.3f %s"
|
|
||||||
case y >= 0.099995:
|
|
||||||
format = "%15.4f %s"
|
|
||||||
case y >= 0.0099995:
|
|
||||||
format = "%16.5f %s"
|
|
||||||
case y >= 0.00099995:
|
|
||||||
format = "%17.6f %s"
|
|
||||||
default:
|
|
||||||
format = "%18.7f %s"
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, format, x, unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
type benchContext struct {
|
|
||||||
match *matcher
|
|
||||||
|
|
||||||
maxLen int // The largest recorded benchmark name.
|
|
||||||
}
|
|
||||||
|
|
||||||
func runBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) bool {
|
|
||||||
// If no flag was specified, don't run benchmarks.
|
|
||||||
if len(*matchBenchmarks) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
ctx := &benchContext{
|
|
||||||
match: newMatcher(matchString, *matchBenchmarks, "-test.bench", flagSkipRegexp),
|
|
||||||
}
|
|
||||||
var bs []InternalBenchmark
|
|
||||||
for _, Benchmark := range benchmarks {
|
|
||||||
if _, matched, _ := ctx.match.fullName(nil, Benchmark.Name); matched {
|
|
||||||
bs = append(bs, Benchmark)
|
|
||||||
benchName := Benchmark.Name
|
|
||||||
if l := len(benchName); l > ctx.maxLen {
|
|
||||||
ctx.maxLen = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
main := &B{
|
|
||||||
common: common{
|
|
||||||
output: &logger{},
|
|
||||||
name: "Main",
|
|
||||||
},
|
|
||||||
benchTime: benchTime,
|
|
||||||
benchFunc: func(b *B) {
|
|
||||||
for _, Benchmark := range bs {
|
|
||||||
b.Run(Benchmark.Name, Benchmark.F)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
context: ctx,
|
|
||||||
}
|
|
||||||
|
|
||||||
main.runN(1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// processBench runs bench b and prints the results.
|
|
||||||
func (b *B) processBench(ctx *benchContext) {
|
|
||||||
benchName := b.name
|
|
||||||
|
|
||||||
for i := 0; i < flagCount; i++ {
|
|
||||||
if ctx != nil {
|
|
||||||
fmt.Printf("%-*s\t", ctx.maxLen, benchName)
|
|
||||||
}
|
|
||||||
r := b.doBench()
|
|
||||||
if b.failed {
|
|
||||||
// The output could be very long here, but probably isn't.
|
|
||||||
// We print it all, regardless, because we don't want to trim the reason
|
|
||||||
// the benchmark failed.
|
|
||||||
fmt.Printf("--- FAIL: %s\n%s", benchName, "") // b.output)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ctx != nil {
|
|
||||||
results := r.String()
|
|
||||||
|
|
||||||
if *benchmarkMemory || b.showAllocResult {
|
|
||||||
results += "\t" + r.MemString()
|
|
||||||
}
|
|
||||||
fmt.Println(results)
|
|
||||||
|
|
||||||
// Print any benchmark output
|
|
||||||
if b.output.Len() > 0 {
|
|
||||||
fmt.Printf("--- BENCH: %s\n", benchName)
|
|
||||||
b.output.WriteTo(os.Stdout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run benchmarks f as a subbenchmark with the given name. It reports
|
|
||||||
// true if the subbenchmark succeeded.
|
|
||||||
//
|
|
||||||
// A subbenchmark is like any other benchmark. A benchmark that calls Run at
|
|
||||||
// least once will not be measured itself and will be called once with N=1.
|
|
||||||
func (b *B) Run(name string, f func(b *B)) bool {
|
|
||||||
benchName, ok, partial := b.name, true, false
|
|
||||||
if b.context != nil {
|
|
||||||
benchName, ok, partial = b.context.match.fullName(&b.common, name)
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
b.hasSub = true
|
|
||||||
sub := &B{
|
|
||||||
common: common{
|
|
||||||
output: &logger{},
|
|
||||||
name: benchName,
|
|
||||||
level: b.level + 1,
|
|
||||||
},
|
|
||||||
benchFunc: f,
|
|
||||||
benchTime: b.benchTime,
|
|
||||||
context: b.context,
|
|
||||||
}
|
|
||||||
if partial {
|
|
||||||
// Partial name match, like -bench=X/Y matching BenchmarkX.
|
|
||||||
// Only process sub-benchmarks, if any.
|
|
||||||
sub.hasSub = true
|
|
||||||
}
|
|
||||||
if sub.run1() {
|
|
||||||
sub.run()
|
|
||||||
}
|
|
||||||
b.add(sub.result)
|
|
||||||
return !sub.failed
|
|
||||||
}
|
|
||||||
|
|
||||||
// add simulates running benchmarks in sequence in a single iteration. It is
|
|
||||||
// used to give some meaningful results in case func Benchmark is used in
|
|
||||||
// combination with Run.
|
|
||||||
func (b *B) add(other BenchmarkResult) {
|
|
||||||
r := &b.result
|
|
||||||
// The aggregated BenchmarkResults resemble running all subbenchmarks as
|
|
||||||
// in sequence in a single benchmark.
|
|
||||||
r.N = 1
|
|
||||||
r.T += time.Duration(other.NsPerOp())
|
|
||||||
if other.Bytes == 0 {
|
|
||||||
// Summing Bytes is meaningless in aggregate if not all subbenchmarks
|
|
||||||
// set it.
|
|
||||||
b.missingBytes = true
|
|
||||||
r.Bytes = 0
|
|
||||||
}
|
|
||||||
if !b.missingBytes {
|
|
||||||
r.Bytes += other.Bytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A PB is used by RunParallel for running parallel benchmarks.
|
|
||||||
type PB struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next reports whether there are more iterations to execute.
|
|
||||||
func (pb *PB) Next() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunParallel runs a benchmark in parallel.
|
|
||||||
//
|
|
||||||
// Not implemented
|
|
||||||
func (b *B) RunParallel(body func(*PB)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *B) Loop() bool {
|
|
||||||
panic("unimplemented: testing.B.Loop")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmark benchmarks a single function. It is useful for creating
|
|
||||||
// custom benchmarks that do not use the "go test" command.
|
|
||||||
//
|
|
||||||
// If f calls Run, the result will be an estimate of running all its
|
|
||||||
// subbenchmarks that don't call Run in sequence in a single benchmark.
|
|
||||||
func Benchmark(f func(b *B)) BenchmarkResult {
|
|
||||||
b := &B{
|
|
||||||
benchFunc: f,
|
|
||||||
benchTime: benchTime,
|
|
||||||
}
|
|
||||||
if b.run1() {
|
|
||||||
b.run()
|
|
||||||
}
|
|
||||||
return b.result
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package testing_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var buf = make([]byte, 13579)
|
|
||||||
|
|
||||||
func NonASCII(b []byte, i int, offset int) int {
|
|
||||||
for i = offset; i < len(b)+offset; i++ {
|
|
||||||
if b[i%len(b)] >= 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkFastNonASCII(b *testing.B) {
|
|
||||||
var val int
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
val += NonASCII(buf, 0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSlowNonASCII(b *testing.B) {
|
|
||||||
var val int
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
val += NonASCII(buf, 0, 0)
|
|
||||||
val += NonASCII(buf, 0, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestBenchmark simply uses Benchmark twice and makes sure it does not crash.
|
|
||||||
func TestBenchmark(t *testing.T) {
|
|
||||||
// FIXME: reduce runtime from the current 3 seconds.
|
|
||||||
rslow := testing.Benchmark(BenchmarkSlowNonASCII)
|
|
||||||
rfast := testing.Benchmark(BenchmarkFastNonASCII)
|
|
||||||
tslow := rslow.NsPerOp()
|
|
||||||
tfast := rfast.NsPerOp()
|
|
||||||
|
|
||||||
// Be exceedingly forgiving; do not fail even if system gets busy.
|
|
||||||
speedup := float64(tslow) / float64(tfast)
|
|
||||||
if speedup < 0.3 {
|
|
||||||
t.Errorf("Expected speedup >= 0.3, got %f", speedup)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSub(b *testing.B) {
|
|
||||||
b.Run("Fast", func(b *testing.B) { BenchmarkFastNonASCII(b) })
|
|
||||||
b.Run("Slow", func(b *testing.B) { BenchmarkSlowNonASCII(b) })
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package testing
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is a sad stub of the upstream testing package because it doesn't compile
|
|
||||||
with tinygo right now.
|
|
||||||
*/
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
package testing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InternalFuzzTarget is an internal type but exported because it is
|
|
||||||
// cross-package; it is part of the implementation of the "go test" command.
|
|
||||||
type InternalFuzzTarget struct {
|
|
||||||
Name string
|
|
||||||
Fn func(f *F)
|
|
||||||
}
|
|
||||||
|
|
||||||
// F is a type passed to fuzz tests.
|
|
||||||
//
|
|
||||||
// Fuzz tests run generated inputs against a provided fuzz target, which can
|
|
||||||
// find and report potential bugs in the code being tested.
|
|
||||||
//
|
|
||||||
// A fuzz test runs the seed corpus by default, which includes entries provided
|
|
||||||
// by (*F).Add and entries in the testdata/fuzz/<FuzzTestName> directory. After
|
|
||||||
// any necessary setup and calls to (*F).Add, the fuzz test must then call
|
|
||||||
// (*F).Fuzz to provide the fuzz target. See the testing package documentation
|
|
||||||
// for an example, and see the F.Fuzz and F.Add method documentation for
|
|
||||||
// details.
|
|
||||||
//
|
|
||||||
// *F methods can only be called before (*F).Fuzz. Once the test is
|
|
||||||
// executing the fuzz target, only (*T) methods can be used. The only *F methods
|
|
||||||
// that are allowed in the (*F).Fuzz function are (*F).Failed and (*F).Name.
|
|
||||||
type F struct {
|
|
||||||
common
|
|
||||||
fuzzContext *fuzzContext
|
|
||||||
testContext *testContext
|
|
||||||
|
|
||||||
// inFuzzFn is true when the fuzz function is running. Most F methods cannot
|
|
||||||
// be called when inFuzzFn is true.
|
|
||||||
inFuzzFn bool
|
|
||||||
|
|
||||||
// corpus is a set of seed corpus entries, added with F.Add and loaded
|
|
||||||
// from testdata.
|
|
||||||
corpus []corpusEntry
|
|
||||||
|
|
||||||
result fuzzResult
|
|
||||||
fuzzCalled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// corpusEntry is an alias to the same type as internal/fuzz.CorpusEntry.
|
|
||||||
// We use a type alias because we don't want to export this type, and we can't
|
|
||||||
// import internal/fuzz from testing.
|
|
||||||
type corpusEntry = struct {
|
|
||||||
Parent string
|
|
||||||
Path string
|
|
||||||
Data []byte
|
|
||||||
Values []interface{}
|
|
||||||
Generation int
|
|
||||||
IsSeed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add will add the arguments to the seed corpus for the fuzz test. This will be
|
|
||||||
// a no-op if called after or within the fuzz target, and args must match the
|
|
||||||
// arguments for the fuzz target.
|
|
||||||
func (f *F) Add(args ...interface{}) {
|
|
||||||
var values []interface{}
|
|
||||||
for i := range args {
|
|
||||||
if t := reflect.TypeOf(args[i]); !supportedTypes[t] {
|
|
||||||
panic(fmt.Sprintf("testing: unsupported type to Add %v", t))
|
|
||||||
}
|
|
||||||
values = append(values, args[i])
|
|
||||||
}
|
|
||||||
f.corpus = append(f.corpus, corpusEntry{Values: values, IsSeed: true, Path: fmt.Sprintf("seed#%d", len(f.corpus))})
|
|
||||||
}
|
|
||||||
|
|
||||||
// supportedTypes represents all of the supported types which can be fuzzed.
|
|
||||||
var supportedTypes = map[reflect.Type]bool{
|
|
||||||
reflect.TypeOf(([]byte)("")): true,
|
|
||||||
reflect.TypeOf((string)("")): true,
|
|
||||||
reflect.TypeOf((bool)(false)): true,
|
|
||||||
reflect.TypeOf((byte)(0)): true,
|
|
||||||
reflect.TypeOf((rune)(0)): true,
|
|
||||||
reflect.TypeOf((float32)(0)): true,
|
|
||||||
reflect.TypeOf((float64)(0)): true,
|
|
||||||
reflect.TypeOf((int)(0)): true,
|
|
||||||
reflect.TypeOf((int8)(0)): true,
|
|
||||||
reflect.TypeOf((int16)(0)): true,
|
|
||||||
reflect.TypeOf((int32)(0)): true,
|
|
||||||
reflect.TypeOf((int64)(0)): true,
|
|
||||||
reflect.TypeOf((uint)(0)): true,
|
|
||||||
reflect.TypeOf((uint8)(0)): true,
|
|
||||||
reflect.TypeOf((uint16)(0)): true,
|
|
||||||
reflect.TypeOf((uint32)(0)): true,
|
|
||||||
reflect.TypeOf((uint64)(0)): true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of
|
|
||||||
// arguments, those arguments will be added to the seed corpus.
|
|
||||||
//
|
|
||||||
// ff must be a function with no return value whose first argument is *T and
|
|
||||||
// whose remaining arguments are the types to be fuzzed.
|
|
||||||
// For example:
|
|
||||||
//
|
|
||||||
// f.Fuzz(func(t *testing.T, b []byte, i int) { ... })
|
|
||||||
//
|
|
||||||
// The following types are allowed: []byte, string, bool, byte, rune, float32,
|
|
||||||
// float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64.
|
|
||||||
// More types may be supported in the future.
|
|
||||||
//
|
|
||||||
// ff must not call any *F methods, e.g. (*F).Log, (*F).Error, (*F).Skip. Use
|
|
||||||
// the corresponding *T method instead. The only *F methods that are allowed in
|
|
||||||
// the (*F).Fuzz function are (*F).Failed and (*F).Name.
|
|
||||||
//
|
|
||||||
// This function should be fast and deterministic, and its behavior should not
|
|
||||||
// depend on shared state. No mutatable input arguments, or pointers to them,
|
|
||||||
// should be retained between executions of the fuzz function, as the memory
|
|
||||||
// backing them may be mutated during a subsequent invocation. ff must not
|
|
||||||
// modify the underlying data of the arguments provided by the fuzzing engine.
|
|
||||||
//
|
|
||||||
// When fuzzing, F.Fuzz does not return until a problem is found, time runs out
|
|
||||||
// (set with -fuzztime), or the test process is interrupted by a signal. F.Fuzz
|
|
||||||
// should be called exactly once, unless F.Skip or F.Fail is called beforehand.
|
|
||||||
func (f *F) Fuzz(ff interface{}) {
|
|
||||||
f.failed = true
|
|
||||||
f.result.N = 0
|
|
||||||
f.result.T = 0
|
|
||||||
f.result.Error = errors.New("operation not implemented")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// fuzzContext holds fields common to all fuzz tests.
|
|
||||||
type fuzzContext struct {
|
|
||||||
deps testDeps
|
|
||||||
mode fuzzMode
|
|
||||||
}
|
|
||||||
|
|
||||||
type fuzzMode uint8
|
|
||||||
|
|
||||||
// fuzzResult contains the results of a fuzz run.
|
|
||||||
type fuzzResult struct {
|
|
||||||
N int // The number of iterations.
|
|
||||||
T time.Duration // The total time taken.
|
|
||||||
Error error // Error is the error from the failing input
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
//
|
|
||||||
//go:build baremetal
|
|
||||||
|
|
||||||
package testing
|
|
||||||
|
|
||||||
const isBaremetal = true
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
//
|
|
||||||
//go:build !baremetal
|
|
||||||
|
|
||||||
package testing
|
|
||||||
|
|
||||||
const isBaremetal = false
|
|
||||||
@@ -1,323 +0,0 @@
|
|||||||
// Copyright 2015 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package testing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// matcher sanitizes, uniques, and filters names of subtests and subbenchmarks.
|
|
||||||
type matcher struct {
|
|
||||||
filter filterMatch
|
|
||||||
skip filterMatch
|
|
||||||
matchFunc func(pat, str string) (bool, error)
|
|
||||||
|
|
||||||
mu sync.Mutex
|
|
||||||
|
|
||||||
// subNames is used to deduplicate subtest names.
|
|
||||||
// Each key is the subtest name joined to the deduplicated name of the parent test.
|
|
||||||
// Each value is the count of the number of occurrences of the given subtest name
|
|
||||||
// already seen.
|
|
||||||
subNames map[string]int32
|
|
||||||
}
|
|
||||||
|
|
||||||
type filterMatch interface {
|
|
||||||
// matches checks the name against the receiver's pattern strings using the
|
|
||||||
// given match function.
|
|
||||||
matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool)
|
|
||||||
|
|
||||||
// verify checks that the receiver's pattern strings are valid filters by
|
|
||||||
// calling the given match function.
|
|
||||||
verify(name string, matchString func(pat, str string) (bool, error)) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// simpleMatch matches a test name if all of the pattern strings match in
|
|
||||||
// sequence.
|
|
||||||
type simpleMatch []string
|
|
||||||
|
|
||||||
// alternationMatch matches a test name if one of the alternations match.
|
|
||||||
type alternationMatch []filterMatch
|
|
||||||
|
|
||||||
// TODO: fix test_main to avoid race and improve caching, also allowing to
|
|
||||||
// eliminate this Mutex.
|
|
||||||
var matchMutex sync.Mutex
|
|
||||||
|
|
||||||
func allMatcher() *matcher {
|
|
||||||
return newMatcher(nil, "", "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMatcher(matchString func(pat, str string) (bool, error), patterns, name, skips string) *matcher {
|
|
||||||
if isBaremetal {
|
|
||||||
matchString = fakeMatchString
|
|
||||||
}
|
|
||||||
|
|
||||||
var filter, skip filterMatch
|
|
||||||
if patterns == "" {
|
|
||||||
filter = simpleMatch{} // always partial true
|
|
||||||
} else {
|
|
||||||
filter = splitRegexp(patterns)
|
|
||||||
if err := filter.verify(name, matchString); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if skips == "" {
|
|
||||||
skip = alternationMatch{} // always false
|
|
||||||
} else {
|
|
||||||
skip = splitRegexp(skips)
|
|
||||||
if err := skip.verify("-test.skip", matchString); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "testing: invalid regexp for %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &matcher{
|
|
||||||
filter: filter,
|
|
||||||
skip: skip,
|
|
||||||
matchFunc: matchString,
|
|
||||||
subNames: map[string]int32{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *matcher) fullName(c *common, subname string) (name string, ok, partial bool) {
|
|
||||||
name = subname
|
|
||||||
|
|
||||||
m.mu.Lock()
|
|
||||||
defer m.mu.Unlock()
|
|
||||||
|
|
||||||
if c != nil && c.level > 0 {
|
|
||||||
name = m.unique(c.name, rewrite(subname))
|
|
||||||
}
|
|
||||||
|
|
||||||
matchMutex.Lock()
|
|
||||||
defer matchMutex.Unlock()
|
|
||||||
|
|
||||||
// We check the full array of paths each time to allow for the case that a pattern contains a '/'.
|
|
||||||
elem := strings.Split(name, "/")
|
|
||||||
|
|
||||||
// filter must match.
|
|
||||||
// accept partial match that may produce full match later.
|
|
||||||
ok, partial = m.filter.matches(elem, m.matchFunc)
|
|
||||||
if !ok {
|
|
||||||
return name, false, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip must not match.
|
|
||||||
// ignore partial match so we can get to more precise match later.
|
|
||||||
skip, partialSkip := m.skip.matches(elem, m.matchFunc)
|
|
||||||
if skip && !partialSkip {
|
|
||||||
return name, false, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return name, ok, partial
|
|
||||||
}
|
|
||||||
|
|
||||||
// clearSubNames clears the matcher's internal state, potentially freeing
|
|
||||||
// memory. After this is called, T.Name may return the same strings as it did
|
|
||||||
// for earlier subtests.
|
|
||||||
func (m *matcher) clearSubNames() {
|
|
||||||
m.mu.Lock()
|
|
||||||
defer m.mu.Unlock()
|
|
||||||
for key := range m.subNames {
|
|
||||||
delete(m.subNames, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m simpleMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
|
|
||||||
for i, s := range name {
|
|
||||||
if i >= len(m) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if ok, _ := matchString(m[i], s); !ok {
|
|
||||||
return false, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, len(name) < len(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m simpleMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
|
|
||||||
for i, s := range m {
|
|
||||||
m[i] = rewrite(s)
|
|
||||||
}
|
|
||||||
// Verify filters before doing any processing.
|
|
||||||
for i, s := range m {
|
|
||||||
if _, err := matchString(s, "non-empty"); err != nil {
|
|
||||||
return fmt.Errorf("element %d of %s (%q): %s", i, name, s, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m alternationMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
|
|
||||||
for _, m := range m {
|
|
||||||
if ok, partial = m.matches(name, matchString); ok {
|
|
||||||
return ok, partial
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m alternationMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
|
|
||||||
for i, m := range m {
|
|
||||||
if err := m.verify(name, matchString); err != nil {
|
|
||||||
return fmt.Errorf("alternation %d of %s", i, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitRegexp(s string) filterMatch {
|
|
||||||
a := make(simpleMatch, 0, strings.Count(s, "/"))
|
|
||||||
b := make(alternationMatch, 0, strings.Count(s, "|"))
|
|
||||||
cs := 0
|
|
||||||
cp := 0
|
|
||||||
for i := 0; i < len(s); {
|
|
||||||
switch s[i] {
|
|
||||||
case '[':
|
|
||||||
cs++
|
|
||||||
case ']':
|
|
||||||
if cs--; cs < 0 { // An unmatched ']' is legal.
|
|
||||||
cs = 0
|
|
||||||
}
|
|
||||||
case '(':
|
|
||||||
if cs == 0 {
|
|
||||||
cp++
|
|
||||||
}
|
|
||||||
case ')':
|
|
||||||
if cs == 0 {
|
|
||||||
cp--
|
|
||||||
}
|
|
||||||
case '\\':
|
|
||||||
i++
|
|
||||||
case '/':
|
|
||||||
if cs == 0 && cp == 0 {
|
|
||||||
a = append(a, s[:i])
|
|
||||||
s = s[i+1:]
|
|
||||||
i = 0
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case '|':
|
|
||||||
if cs == 0 && cp == 0 {
|
|
||||||
a = append(a, s[:i])
|
|
||||||
s = s[i+1:]
|
|
||||||
i = 0
|
|
||||||
b = append(b, a)
|
|
||||||
a = make(simpleMatch, 0, len(a))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
a = append(a, s)
|
|
||||||
if len(b) == 0 {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return append(b, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unique creates a unique name for the given parent and subname by affixing it
|
|
||||||
// with one or more counts, if necessary.
|
|
||||||
func (m *matcher) unique(parent, subname string) string {
|
|
||||||
base := parent + "/" + subname
|
|
||||||
|
|
||||||
for {
|
|
||||||
n := m.subNames[base]
|
|
||||||
if n < 0 {
|
|
||||||
panic("subtest count overflow")
|
|
||||||
}
|
|
||||||
m.subNames[base] = n + 1
|
|
||||||
|
|
||||||
if n == 0 && subname != "" {
|
|
||||||
prefix, nn := parseSubtestNumber(base)
|
|
||||||
if len(prefix) < len(base) && nn < m.subNames[prefix] {
|
|
||||||
// This test is explicitly named like "parent/subname#NN",
|
|
||||||
// and #NN was already used for the NNth occurrence of "parent/subname".
|
|
||||||
// Loop to add a disambiguating suffix.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return base
|
|
||||||
}
|
|
||||||
|
|
||||||
name := fmt.Sprintf("%s#%02d", base, n)
|
|
||||||
if m.subNames[name] != 0 {
|
|
||||||
// This is the nth occurrence of base, but the name "parent/subname#NN"
|
|
||||||
// collides with the first occurrence of a subtest *explicitly* named
|
|
||||||
// "parent/subname#NN". Try the next number.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseSubtestNumber splits a subtest name into a "#%02d"-formatted int32
|
|
||||||
// suffix (if present), and a prefix preceding that suffix (always).
|
|
||||||
func parseSubtestNumber(s string) (prefix string, nn int32) {
|
|
||||||
i := strings.LastIndex(s, "#")
|
|
||||||
if i < 0 {
|
|
||||||
return s, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix, suffix := s[:i], s[i+1:]
|
|
||||||
if len(suffix) < 2 || (len(suffix) > 2 && suffix[0] == '0') {
|
|
||||||
// Even if suffix is numeric, it is not a possible output of a "%02" format
|
|
||||||
// string: it has either too few digits or too many leading zeroes.
|
|
||||||
return s, 0
|
|
||||||
}
|
|
||||||
if suffix == "00" {
|
|
||||||
if !strings.HasSuffix(prefix, "/") {
|
|
||||||
// We only use "#00" as a suffix for subtests named with the empty
|
|
||||||
// string — it isn't a valid suffix if the subtest name is non-empty.
|
|
||||||
return s, 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := strconv.ParseInt(suffix, 10, 32)
|
|
||||||
if err != nil || n < 0 {
|
|
||||||
return s, 0
|
|
||||||
}
|
|
||||||
return prefix, int32(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// rewrite rewrites a subname to having only printable characters and no white
|
|
||||||
// space.
|
|
||||||
func rewrite(s string) string {
|
|
||||||
b := []byte{}
|
|
||||||
for _, r := range s {
|
|
||||||
switch {
|
|
||||||
case isSpace(r):
|
|
||||||
b = append(b, '_')
|
|
||||||
case !strconv.IsPrint(r):
|
|
||||||
s := strconv.QuoteRune(r)
|
|
||||||
b = append(b, s[1:len(s)-1]...)
|
|
||||||
default:
|
|
||||||
b = append(b, string(r)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSpace(r rune) bool {
|
|
||||||
if r < 0x2000 {
|
|
||||||
switch r {
|
|
||||||
// Note: not the same as Unicode Z class.
|
|
||||||
case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if r <= 0x200a {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
// Copyright 2015 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package testing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Verify that our IsSpace agrees with unicode.IsSpace.
|
|
||||||
func TestIsSpace(t *T) {
|
|
||||||
n := 0
|
|
||||||
for r := rune(0); r <= unicode.MaxRune; r++ {
|
|
||||||
if isSpace(r) != unicode.IsSpace(r) {
|
|
||||||
t.Errorf("IsSpace(%U)=%t incorrect", r, isSpace(r))
|
|
||||||
n++
|
|
||||||
if n > 10 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitRegexp(t *T) {
|
|
||||||
res := func(s ...string) filterMatch { return simpleMatch(s) }
|
|
||||||
alt := func(m ...filterMatch) filterMatch { return alternationMatch(m) }
|
|
||||||
testCases := []struct {
|
|
||||||
pattern string
|
|
||||||
result filterMatch
|
|
||||||
}{
|
|
||||||
// Correct patterns
|
|
||||||
// If a regexp pattern is correct, all split regexps need to be correct
|
|
||||||
// as well.
|
|
||||||
{"", res("")},
|
|
||||||
{"/", res("", "")},
|
|
||||||
{"//", res("", "", "")},
|
|
||||||
{"A", res("A")},
|
|
||||||
{"A/B", res("A", "B")},
|
|
||||||
{"A/B/", res("A", "B", "")},
|
|
||||||
{"/A/B/", res("", "A", "B", "")},
|
|
||||||
{"[A]/(B)", res("[A]", "(B)")},
|
|
||||||
{"[/]/[/]", res("[/]", "[/]")},
|
|
||||||
{"[/]/[:/]", res("[/]", "[:/]")},
|
|
||||||
{"/]", res("", "]")},
|
|
||||||
{"]/", res("]", "")},
|
|
||||||
{"]/[/]", res("]", "[/]")},
|
|
||||||
{`([)/][(])`, res(`([)/][(])`)},
|
|
||||||
{"[(]/[)]", res("[(]", "[)]")},
|
|
||||||
|
|
||||||
{"A/B|C/D", alt(res("A", "B"), res("C", "D"))},
|
|
||||||
|
|
||||||
// Faulty patterns
|
|
||||||
// Errors in original should produce at least one faulty regexp in results.
|
|
||||||
{")/", res(")/")},
|
|
||||||
{")/(/)", res(")/(", ")")},
|
|
||||||
{"a[/)b", res("a[/)b")},
|
|
||||||
{"(/]", res("(/]")},
|
|
||||||
{"(/", res("(/")},
|
|
||||||
{"[/]/[/", res("[/]", "[/")},
|
|
||||||
{`\p{/}`, res(`\p{`, "}")},
|
|
||||||
{`\p/`, res(`\p`, "")},
|
|
||||||
{`[[:/:]]`, res(`[[:/:]]`)},
|
|
||||||
}
|
|
||||||
for _, tc := range testCases {
|
|
||||||
a := splitRegexp(tc.pattern)
|
|
||||||
if !reflect.DeepEqual(a, tc.result) {
|
|
||||||
t.Errorf("splitRegexp(%q) = %#v; want %#v", tc.pattern, a, tc.result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is any error in the pattern, one of the returned subpatterns
|
|
||||||
// needs to have an error as well.
|
|
||||||
if _, err := regexp.Compile(tc.pattern); err != nil {
|
|
||||||
ok := true
|
|
||||||
if err := a.verify("", regexp.MatchString); err != nil {
|
|
||||||
ok = false
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
t.Errorf("%s: expected error in any of %q", tc.pattern, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMatcher(t *T) {
|
|
||||||
testCases := []struct {
|
|
||||||
pattern string
|
|
||||||
skip string
|
|
||||||
parent, sub string
|
|
||||||
ok bool
|
|
||||||
partial bool
|
|
||||||
}{
|
|
||||||
// Behavior without subtests.
|
|
||||||
{"", "", "", "TestFoo", true, false},
|
|
||||||
{"TestFoo", "", "", "TestFoo", true, false},
|
|
||||||
{"TestFoo/", "", "", "TestFoo", true, true},
|
|
||||||
{"TestFoo/bar/baz", "", "", "TestFoo", true, true},
|
|
||||||
{"TestFoo", "", "", "TestBar", false, false},
|
|
||||||
{"TestFoo/", "", "", "TestBar", false, false},
|
|
||||||
{"TestFoo/bar/baz", "", "", "TestBar/bar/baz", false, false},
|
|
||||||
{"", "TestBar", "", "TestFoo", true, false},
|
|
||||||
{"", "TestBar", "", "TestBar", false, false},
|
|
||||||
|
|
||||||
// Skipping a non-existent test doesn't change anything.
|
|
||||||
{"", "TestFoo/skipped", "", "TestFoo", true, false},
|
|
||||||
{"TestFoo", "TestFoo/skipped", "", "TestFoo", true, false},
|
|
||||||
{"TestFoo/", "TestFoo/skipped", "", "TestFoo", true, true},
|
|
||||||
{"TestFoo/bar/baz", "TestFoo/skipped", "", "TestFoo", true, true},
|
|
||||||
{"TestFoo", "TestFoo/skipped", "", "TestBar", false, false},
|
|
||||||
{"TestFoo/", "TestFoo/skipped", "", "TestBar", false, false},
|
|
||||||
{"TestFoo/bar/baz", "TestFoo/skipped", "", "TestBar/bar/baz", false, false},
|
|
||||||
|
|
||||||
// with subtests
|
|
||||||
{"", "", "TestFoo", "x", true, false},
|
|
||||||
{"TestFoo", "", "TestFoo", "x", true, false},
|
|
||||||
{"TestFoo/", "", "TestFoo", "x", true, false},
|
|
||||||
{"TestFoo/bar/baz", "", "TestFoo", "bar", true, true},
|
|
||||||
|
|
||||||
{"", "TestFoo/skipped", "TestFoo", "x", true, false},
|
|
||||||
{"TestFoo", "TestFoo/skipped", "TestFoo", "x", true, false},
|
|
||||||
{"TestFoo", "TestFoo/skipped", "TestFoo", "skipped", false, false},
|
|
||||||
{"TestFoo/", "TestFoo/skipped", "TestFoo", "x", true, false},
|
|
||||||
{"TestFoo/bar/baz", "TestFoo/skipped", "TestFoo", "bar", true, true},
|
|
||||||
|
|
||||||
// Subtest with a '/' in its name still allows for copy and pasted names
|
|
||||||
// to match.
|
|
||||||
{"TestFoo/bar/baz", "", "TestFoo", "bar/baz", true, false},
|
|
||||||
{"TestFoo/bar/baz", "TestFoo/bar/baz", "TestFoo", "bar/baz", false, false},
|
|
||||||
{"TestFoo/bar/baz", "TestFoo/bar/baz/skip", "TestFoo", "bar/baz", true, false},
|
|
||||||
{"TestFoo/bar/baz", "", "TestFoo/bar", "baz", true, false},
|
|
||||||
{"TestFoo/bar/baz", "", "TestFoo", "x", false, false},
|
|
||||||
{"TestFoo", "", "TestBar", "x", false, false},
|
|
||||||
{"TestFoo/", "", "TestBar", "x", false, false},
|
|
||||||
{"TestFoo/bar/baz", "", "TestBar", "x/bar/baz", false, false},
|
|
||||||
|
|
||||||
{"A/B|C/D", "", "TestA", "B", true, false},
|
|
||||||
{"A/B|C/D", "", "TestC", "D", true, false},
|
|
||||||
{"A/B|C/D", "", "TestA", "C", false, false},
|
|
||||||
|
|
||||||
// subtests only
|
|
||||||
{"", "", "TestFoo", "x", true, false},
|
|
||||||
{"/", "", "TestFoo", "x", true, false},
|
|
||||||
{"./", "", "TestFoo", "x", true, false},
|
|
||||||
{"./.", "", "TestFoo", "x", true, false},
|
|
||||||
{"/bar/baz", "", "TestFoo", "bar", true, true},
|
|
||||||
{"/bar/baz", "", "TestFoo", "bar/baz", true, false},
|
|
||||||
{"//baz", "", "TestFoo", "bar/baz", true, false},
|
|
||||||
{"//", "", "TestFoo", "bar/baz", true, false},
|
|
||||||
{"/bar/baz", "", "TestFoo/bar", "baz", true, false},
|
|
||||||
{"//foo", "", "TestFoo", "bar/baz", false, false},
|
|
||||||
{"/bar/baz", "", "TestFoo", "x", false, false},
|
|
||||||
{"/bar/baz", "", "TestBar", "x/bar/baz", false, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
m := newMatcher(regexp.MatchString, tc.pattern, "-test.run", tc.skip)
|
|
||||||
|
|
||||||
parent := &common{name: tc.parent}
|
|
||||||
if tc.parent != "" {
|
|
||||||
parent.level = 1
|
|
||||||
}
|
|
||||||
if n, ok, partial := m.fullName(parent, tc.sub); ok != tc.ok || partial != tc.partial {
|
|
||||||
t.Errorf("for pattern %q, fullName(parent=%q, sub=%q) = %q, ok %v partial %v; want ok %v partial %v",
|
|
||||||
tc.pattern, tc.parent, tc.sub, n, ok, partial, tc.ok, tc.partial)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var namingTestCases = []struct{ name, want string }{
|
|
||||||
// Uniqueness
|
|
||||||
{"", "x/#00"},
|
|
||||||
{"", "x/#01"},
|
|
||||||
{"#0", "x/#0"}, // Doesn't conflict with #00 because the number of digits differs.
|
|
||||||
{"#00", "x/#00#01"}, // Conflicts with implicit #00 (used above), so add a suffix.
|
|
||||||
{"#", "x/#"},
|
|
||||||
{"#", "x/##01"},
|
|
||||||
|
|
||||||
{"t", "x/t"},
|
|
||||||
{"t", "x/t#01"},
|
|
||||||
{"t", "x/t#02"},
|
|
||||||
{"t#00", "x/t#00"}, // Explicit "#00" doesn't conflict with the unsuffixed first subtest.
|
|
||||||
|
|
||||||
{"a#01", "x/a#01"}, // user has subtest with this name.
|
|
||||||
{"a", "x/a"}, // doesn't conflict with this name.
|
|
||||||
{"a", "x/a#02"}, // This string is claimed now, so resume
|
|
||||||
{"a", "x/a#03"}, // with counting.
|
|
||||||
{"a#02", "x/a#02#01"}, // We already used a#02 once, so add a suffix.
|
|
||||||
|
|
||||||
{"b#00", "x/b#00"},
|
|
||||||
{"b", "x/b"}, // Implicit 0 doesn't conflict with explicit "#00".
|
|
||||||
{"b", "x/b#01"},
|
|
||||||
{"b#9223372036854775807", "x/b#9223372036854775807"}, // MaxInt64
|
|
||||||
{"b", "x/b#02"},
|
|
||||||
{"b", "x/b#03"},
|
|
||||||
|
|
||||||
// Sanitizing
|
|
||||||
{"A:1 B:2", "x/A:1_B:2"},
|
|
||||||
{"s\t\r\u00a0", "x/s___"},
|
|
||||||
{"\x01", `x/\x01`},
|
|
||||||
{"\U0010ffff", `x/\U0010ffff`},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNaming(t *T) {
|
|
||||||
m := newMatcher(regexp.MatchString, "", "", "")
|
|
||||||
parent := &common{name: "x", level: 1} // top-level test.
|
|
||||||
|
|
||||||
for i, tc := range namingTestCases {
|
|
||||||
if got, _, _ := m.fullName(parent, tc.name); got != tc.want {
|
|
||||||
t.Errorf("%d:%s: got %q; want %q", i, tc.name, got, tc.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FuzzNaming(f *F) {
|
|
||||||
for _, tc := range namingTestCases {
|
|
||||||
f.Add(tc.name)
|
|
||||||
}
|
|
||||||
parent := &common{name: "x", level: 1}
|
|
||||||
var m *matcher
|
|
||||||
var seen map[string]string
|
|
||||||
reset := func() {
|
|
||||||
m = allMatcher()
|
|
||||||
seen = make(map[string]string)
|
|
||||||
}
|
|
||||||
reset()
|
|
||||||
|
|
||||||
f.Fuzz(func(t *T, subname string) {
|
|
||||||
if len(subname) > 10 {
|
|
||||||
// Long names attract the OOM killer.
|
|
||||||
t.Skip()
|
|
||||||
}
|
|
||||||
name := m.unique(parent.name, subname)
|
|
||||||
if !strings.Contains(name, "/"+subname) {
|
|
||||||
t.Errorf("name %q does not contain subname %q", name, subname)
|
|
||||||
}
|
|
||||||
if prev, ok := seen[name]; ok {
|
|
||||||
t.Errorf("name %q generated by both %q and %q", name, prev, subname)
|
|
||||||
}
|
|
||||||
if len(seen) > 1e6 {
|
|
||||||
// Free up memory.
|
|
||||||
reset()
|
|
||||||
}
|
|
||||||
seen[name] = subname
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GoString returns a string that is more readable than the default, which makes
|
|
||||||
// it easier to read test errors.
|
|
||||||
func (m alternationMatch) GoString() string {
|
|
||||||
s := make([]string, len(m))
|
|
||||||
for i, m := range m {
|
|
||||||
s[i] = fmt.Sprintf("%#v", m)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("(%s)", strings.Join(s, " | "))
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package testing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCleanup(t *T) {
|
|
||||||
var cleanups []int
|
|
||||||
t.Run("test", func(t *T) {
|
|
||||||
t.Cleanup(func() { cleanups = append(cleanups, 1) })
|
|
||||||
t.Cleanup(func() { cleanups = append(cleanups, 2) })
|
|
||||||
})
|
|
||||||
if got, want := cleanups, []int{2, 1}; !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("unexpected cleanup record; got %v want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunCleanup(t *T) {
|
|
||||||
outerCleanup := 0
|
|
||||||
innerCleanup := 0
|
|
||||||
t.Run("test", func(t *T) {
|
|
||||||
t.Cleanup(func() { outerCleanup++ })
|
|
||||||
t.Run("x", func(t *T) {
|
|
||||||
t.Cleanup(func() { innerCleanup++ })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if innerCleanup != 1 {
|
|
||||||
t.Errorf("unexpected inner cleanup count; got %d want 1", innerCleanup)
|
|
||||||
}
|
|
||||||
if outerCleanup != 1 {
|
|
||||||
t.Errorf("unexpected outer cleanup count; got %d want 1", outerCleanup) // wrong upstream!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCleanupParallelSubtests(t *T) {
|
|
||||||
ranCleanup := 0
|
|
||||||
t.Run("test", func(t *T) {
|
|
||||||
t.Cleanup(func() { ranCleanup++ })
|
|
||||||
t.Run("x", func(t *T) {
|
|
||||||
t.Parallel()
|
|
||||||
if ranCleanup > 0 {
|
|
||||||
t.Error("outer cleanup ran before parallel subtest")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if ranCleanup != 1 {
|
|
||||||
t.Errorf("unexpected cleanup count; got %d want 1", ranCleanup)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNestedCleanup(t *T) {
|
|
||||||
ranCleanup := 0
|
|
||||||
t.Run("test", func(t *T) {
|
|
||||||
t.Cleanup(func() {
|
|
||||||
if ranCleanup != 2 {
|
|
||||||
t.Errorf("unexpected cleanup count in first cleanup: got %d want 2", ranCleanup)
|
|
||||||
}
|
|
||||||
ranCleanup++
|
|
||||||
})
|
|
||||||
t.Cleanup(func() {
|
|
||||||
if ranCleanup != 0 {
|
|
||||||
t.Errorf("unexpected cleanup count in second cleanup: got %d want 0", ranCleanup)
|
|
||||||
}
|
|
||||||
ranCleanup++
|
|
||||||
t.Cleanup(func() {
|
|
||||||
if ranCleanup != 1 {
|
|
||||||
t.Errorf("unexpected cleanup count in nested cleanup: got %d want 1", ranCleanup)
|
|
||||||
}
|
|
||||||
ranCleanup++
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if ranCleanup != 3 {
|
|
||||||
t.Errorf("unexpected cleanup count: got %d want 3", ranCleanup)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,709 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
//
|
|
||||||
// This file has been modified for use by the TinyGo compiler.
|
|
||||||
// src: https://github.com/golang/go/blob/61bb56ad/src/testing/testing.go
|
|
||||||
|
|
||||||
// Package testing provides support for automated testing of Go packages.
|
|
||||||
package testing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/fs"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Testing flags.
|
|
||||||
var (
|
|
||||||
flagVerbose bool
|
|
||||||
flagShort bool
|
|
||||||
flagRunRegexp string
|
|
||||||
flagSkipRegexp string
|
|
||||||
flagShuffle string
|
|
||||||
flagCount int
|
|
||||||
)
|
|
||||||
|
|
||||||
var initRan bool
|
|
||||||
|
|
||||||
// Init registers testing flags. It has no effect if it has already run.
|
|
||||||
func Init() {
|
|
||||||
if initRan {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
initRan = true
|
|
||||||
|
|
||||||
flag.BoolVar(&flagVerbose, "test.v", false, "verbose: print additional output")
|
|
||||||
flag.BoolVar(&flagShort, "test.short", false, "short: run smaller test suite to save time")
|
|
||||||
flag.StringVar(&flagRunRegexp, "test.run", "", "run: regexp of tests to run")
|
|
||||||
flag.StringVar(&flagSkipRegexp, "test.skip", "", "skip: regexp of tests to run")
|
|
||||||
flag.StringVar(&flagShuffle, "test.shuffle", "off", "shuffle: off, on, <numeric-seed>")
|
|
||||||
|
|
||||||
flag.IntVar(&flagCount, "test.count", 1, "run each test or benchmark `count` times")
|
|
||||||
|
|
||||||
initBenchmarkFlags()
|
|
||||||
}
|
|
||||||
|
|
||||||
// common holds the elements common between T and B and
|
|
||||||
// captures common methods such as Errorf.
|
|
||||||
type common struct {
|
|
||||||
output *logger
|
|
||||||
indent string
|
|
||||||
ran bool // Test or benchmark (or one of its subtests) was executed.
|
|
||||||
failed bool // Test or benchmark has failed.
|
|
||||||
skipped bool // Test of benchmark has been skipped.
|
|
||||||
cleanups []func() // optional functions to be called at the end of the test
|
|
||||||
finished bool // Test function has completed.
|
|
||||||
|
|
||||||
hasSub bool // TODO: should be atomic
|
|
||||||
|
|
||||||
parent *common
|
|
||||||
level int // Nesting depth of test or benchmark.
|
|
||||||
name string // Name of test or benchmark.
|
|
||||||
start time.Time // Time test or benchmark started
|
|
||||||
duration time.Duration
|
|
||||||
|
|
||||||
tempDir string
|
|
||||||
tempDirErr error
|
|
||||||
tempDirSeq int32
|
|
||||||
|
|
||||||
ctx context.Context
|
|
||||||
cancelCtx context.CancelFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
type logger struct {
|
|
||||||
logToStdout bool
|
|
||||||
b bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *logger) Write(p []byte) (int, error) {
|
|
||||||
if l.logToStdout {
|
|
||||||
return os.Stdout.Write(p)
|
|
||||||
}
|
|
||||||
return l.b.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *logger) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
if l.logToStdout {
|
|
||||||
// We've already been logging to stdout; nothing to do.
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
return l.b.WriteTo(w)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *logger) Len() int {
|
|
||||||
return l.b.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Short reports whether the -test.short flag is set.
|
|
||||||
func Short() bool {
|
|
||||||
return flagShort
|
|
||||||
}
|
|
||||||
|
|
||||||
// CoverMode reports what the test coverage mode is set to.
|
|
||||||
//
|
|
||||||
// Test coverage is not supported; this returns the empty string.
|
|
||||||
func CoverMode() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verbose reports whether the -test.v flag is set.
|
|
||||||
func Verbose() bool {
|
|
||||||
return flagVerbose
|
|
||||||
}
|
|
||||||
|
|
||||||
// String constant that is being set when running a test.
|
|
||||||
var testBinary string
|
|
||||||
|
|
||||||
// Testing returns whether the program was compiled as a test, using "tinygo
|
|
||||||
// test". It returns false when built using "tinygo build", "tinygo flash", etc.
|
|
||||||
func Testing() bool {
|
|
||||||
return testBinary == "1"
|
|
||||||
}
|
|
||||||
|
|
||||||
// flushToParent writes c.output to the parent after first writing the header
|
|
||||||
// with the given format and arguments.
|
|
||||||
func (c *common) flushToParent(testName, format string, args ...interface{}) {
|
|
||||||
if c.parent == nil {
|
|
||||||
// The fake top-level test doesn't want a FAIL or PASS banner.
|
|
||||||
// Not quite sure how this works upstream.
|
|
||||||
c.output.WriteTo(os.Stdout)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(c.parent.output, format, args...)
|
|
||||||
c.output.WriteTo(c.parent.output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fmtDuration returns a string representing d in the form "87.00s".
|
|
||||||
func fmtDuration(d time.Duration) string {
|
|
||||||
return fmt.Sprintf("%.2fs", d.Seconds())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TB is the interface common to T and B.
|
|
||||||
type TB interface {
|
|
||||||
Cleanup(func())
|
|
||||||
Context() context.Context
|
|
||||||
Error(args ...interface{})
|
|
||||||
Errorf(format string, args ...interface{})
|
|
||||||
Fail()
|
|
||||||
FailNow()
|
|
||||||
Failed() bool
|
|
||||||
Fatal(args ...interface{})
|
|
||||||
Fatalf(format string, args ...interface{})
|
|
||||||
Helper()
|
|
||||||
Log(args ...interface{})
|
|
||||||
Logf(format string, args ...interface{})
|
|
||||||
Name() string
|
|
||||||
Setenv(key, value string)
|
|
||||||
Skip(args ...interface{})
|
|
||||||
SkipNow()
|
|
||||||
Skipf(format string, args ...interface{})
|
|
||||||
Skipped() bool
|
|
||||||
TempDir() string
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ TB = (*T)(nil)
|
|
||||||
var _ TB = (*B)(nil)
|
|
||||||
|
|
||||||
// T is a type passed to Test functions to manage test state and support formatted test logs.
|
|
||||||
// Logs are accumulated during execution and dumped to standard output when done.
|
|
||||||
type T struct {
|
|
||||||
common
|
|
||||||
context *testContext // For running tests and subtests.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of the running test or benchmark.
|
|
||||||
func (c *common) Name() string {
|
|
||||||
return c.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *common) setRan() {
|
|
||||||
if c.parent != nil {
|
|
||||||
c.parent.setRan()
|
|
||||||
}
|
|
||||||
c.ran = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fail marks the function as having failed but continues execution.
|
|
||||||
func (c *common) Fail() {
|
|
||||||
c.failed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed reports whether the function has failed.
|
|
||||||
func (c *common) Failed() bool {
|
|
||||||
failed := c.failed
|
|
||||||
return failed
|
|
||||||
}
|
|
||||||
|
|
||||||
// FailNow marks the function as having failed and stops its execution
|
|
||||||
// by calling runtime.Goexit (which then runs all deferred calls in the
|
|
||||||
// current goroutine).
|
|
||||||
func (c *common) FailNow() {
|
|
||||||
c.Fail()
|
|
||||||
|
|
||||||
c.finished = true
|
|
||||||
c.Error("FailNow is incomplete, requires runtime.Goexit()")
|
|
||||||
}
|
|
||||||
|
|
||||||
// log generates the output.
|
|
||||||
func (c *common) log(s string) {
|
|
||||||
// This doesn't print the same as in upstream go, but works for now.
|
|
||||||
if len(s) != 0 && s[len(s)-1] == '\n' {
|
|
||||||
s = s[:len(s)-1]
|
|
||||||
}
|
|
||||||
lines := strings.Split(s, "\n")
|
|
||||||
// First line.
|
|
||||||
fmt.Fprintf(c.output, "%s %s\n", c.indent, lines[0])
|
|
||||||
// More lines.
|
|
||||||
for _, line := range lines[1:] {
|
|
||||||
fmt.Fprintf(c.output, "%s %s\n", c.indent, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log formats its arguments using default formatting, analogous to Println,
|
|
||||||
// and records the text in the error log. For tests, the text will be printed only if
|
|
||||||
// the test fails or the -test.v flag is set. For benchmarks, the text is always
|
|
||||||
// printed to avoid having performance depend on the value of the -test.v flag.
|
|
||||||
func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) }
|
|
||||||
|
|
||||||
// Logf formats its arguments according to the format, analogous to Printf, and
|
|
||||||
// records the text in the error log. A final newline is added if not provided. For
|
|
||||||
// tests, the text will be printed only if the test fails or the -test.v flag is
|
|
||||||
// set. For benchmarks, the text is always printed to avoid having performance
|
|
||||||
// depend on the value of the -test.v flag.
|
|
||||||
func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) }
|
|
||||||
|
|
||||||
// Error is equivalent to Log followed by Fail.
|
|
||||||
func (c *common) Error(args ...interface{}) {
|
|
||||||
c.log(fmt.Sprintln(args...))
|
|
||||||
c.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf is equivalent to Logf followed by Fail.
|
|
||||||
func (c *common) Errorf(format string, args ...interface{}) {
|
|
||||||
c.log(fmt.Sprintf(format, args...))
|
|
||||||
c.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatal is equivalent to Log followed by FailNow.
|
|
||||||
func (c *common) Fatal(args ...interface{}) {
|
|
||||||
c.log(fmt.Sprintln(args...))
|
|
||||||
c.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalf is equivalent to Logf followed by FailNow.
|
|
||||||
func (c *common) Fatalf(format string, args ...interface{}) {
|
|
||||||
c.log(fmt.Sprintf(format, args...))
|
|
||||||
c.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip is equivalent to Log followed by SkipNow.
|
|
||||||
func (c *common) Skip(args ...interface{}) {
|
|
||||||
c.log(fmt.Sprintln(args...))
|
|
||||||
c.SkipNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skipf is equivalent to Logf followed by SkipNow.
|
|
||||||
func (c *common) Skipf(format string, args ...interface{}) {
|
|
||||||
c.log(fmt.Sprintf(format, args...))
|
|
||||||
c.SkipNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SkipNow marks the test as having been skipped and stops its execution
|
|
||||||
// by calling runtime.Goexit.
|
|
||||||
func (c *common) SkipNow() {
|
|
||||||
c.skip()
|
|
||||||
c.finished = true
|
|
||||||
c.Error("SkipNow is incomplete, requires runtime.Goexit()")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *common) skip() {
|
|
||||||
c.skipped = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skipped reports whether the test was skipped.
|
|
||||||
func (c *common) Skipped() bool {
|
|
||||||
return c.skipped
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper is not implemented, it is only provided for compatibility.
|
|
||||||
func (c *common) Helper() {
|
|
||||||
// Unimplemented.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup registers a function to be called when the test (or subtest) and all its
|
|
||||||
// subtests complete. Cleanup functions will be called in last added,
|
|
||||||
// first called order.
|
|
||||||
func (c *common) Cleanup(f func()) {
|
|
||||||
c.cleanups = append(c.cleanups, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Context returns a context that is canceled just before
|
|
||||||
// Cleanup-registered functions are called.
|
|
||||||
//
|
|
||||||
// Cleanup functions can wait for any resources
|
|
||||||
// that shut down on [context.Context.Done] before the test or benchmark completes.
|
|
||||||
func (c *common) Context() context.Context {
|
|
||||||
return c.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
// TempDir returns a temporary directory for the test to use.
|
|
||||||
// The directory is automatically removed by Cleanup when the test and
|
|
||||||
// all its subtests complete.
|
|
||||||
// Each subsequent call to t.TempDir returns a unique directory;
|
|
||||||
// if the directory creation fails, TempDir terminates the test by calling Fatal.
|
|
||||||
func (c *common) TempDir() string {
|
|
||||||
// Use a single parent directory for all the temporary directories
|
|
||||||
// created by a test, each numbered sequentially.
|
|
||||||
var nonExistent bool
|
|
||||||
if c.tempDir == "" { // Usually the case with js/wasm
|
|
||||||
nonExistent = true
|
|
||||||
} else {
|
|
||||||
_, err := os.Stat(c.tempDir)
|
|
||||||
nonExistent = errors.Is(err, fs.ErrNotExist)
|
|
||||||
if err != nil && !nonExistent {
|
|
||||||
c.Fatalf("TempDir: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if nonExistent {
|
|
||||||
c.Helper()
|
|
||||||
|
|
||||||
// Drop unusual characters (such as path separators or
|
|
||||||
// characters interacting with globs) from the directory name to
|
|
||||||
// avoid surprising os.MkdirTemp behavior.
|
|
||||||
mapper := func(r rune) rune {
|
|
||||||
if r < utf8.RuneSelf {
|
|
||||||
const allowed = "!#$%&()+,-.=@^_{}~ "
|
|
||||||
if '0' <= r && r <= '9' ||
|
|
||||||
'a' <= r && r <= 'z' ||
|
|
||||||
'A' <= r && r <= 'Z' {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
if strings.ContainsRune(allowed, r) {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
} else if unicode.IsLetter(r) || unicode.IsNumber(r) {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
pattern := strings.Map(mapper, c.Name())
|
|
||||||
c.tempDir, c.tempDirErr = os.MkdirTemp("", pattern)
|
|
||||||
if c.tempDirErr == nil {
|
|
||||||
c.Cleanup(func() {
|
|
||||||
if err := os.RemoveAll(c.tempDir); err != nil {
|
|
||||||
c.Errorf("TempDir RemoveAll cleanup: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.tempDirErr != nil {
|
|
||||||
c.Fatalf("TempDir: %v", c.tempDirErr)
|
|
||||||
}
|
|
||||||
seq := c.tempDirSeq
|
|
||||||
c.tempDirSeq++
|
|
||||||
dir := fmt.Sprintf("%s%c%03d", c.tempDir, os.PathSeparator, seq)
|
|
||||||
if err := os.Mkdir(dir, 0777); err != nil {
|
|
||||||
c.Fatalf("TempDir: %v", err)
|
|
||||||
}
|
|
||||||
return dir
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setenv calls os.Setenv(key, value) and uses Cleanup to
|
|
||||||
// restore the environment variable to its original value
|
|
||||||
// after the test.
|
|
||||||
func (c *common) Setenv(key, value string) {
|
|
||||||
prevValue, ok := os.LookupEnv(key)
|
|
||||||
|
|
||||||
if err := os.Setenv(key, value); err != nil {
|
|
||||||
c.Fatalf("cannot set environment variable: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
c.Cleanup(func() {
|
|
||||||
os.Setenv(key, prevValue)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
c.Cleanup(func() {
|
|
||||||
os.Unsetenv(key)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chdir calls os.Chdir(dir) and uses Cleanup to restore the current
|
|
||||||
// working directory to its original value after the test. On Unix, it
|
|
||||||
// also sets PWD environment variable for the duration of the test.
|
|
||||||
//
|
|
||||||
// Because Chdir affects the whole process, it cannot be used
|
|
||||||
// in parallel tests or tests with parallel ancestors.
|
|
||||||
func (c *common) Chdir(dir string) {
|
|
||||||
// Note: function copied from the Go 1.24.0 source tree.
|
|
||||||
|
|
||||||
oldwd, err := os.Open(".")
|
|
||||||
if err != nil {
|
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Chdir(dir); err != nil {
|
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
// On POSIX platforms, PWD represents “an absolute pathname of the
|
|
||||||
// current working directory.” Since we are changing the working
|
|
||||||
// directory, we should also set or update PWD to reflect that.
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "windows", "plan9":
|
|
||||||
// Windows and Plan 9 do not use the PWD variable.
|
|
||||||
default:
|
|
||||||
if !filepath.IsAbs(dir) {
|
|
||||||
dir, err = os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.Setenv("PWD", dir)
|
|
||||||
}
|
|
||||||
c.Cleanup(func() {
|
|
||||||
err := oldwd.Chdir()
|
|
||||||
oldwd.Close()
|
|
||||||
if err != nil {
|
|
||||||
// It's not safe to continue with tests if we can't
|
|
||||||
// get back to the original working directory. Since
|
|
||||||
// we are holding a dirfd, this is highly unlikely.
|
|
||||||
panic("testing.Chdir: " + err.Error())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// runCleanup is called at the end of the test.
|
|
||||||
func (c *common) runCleanup() {
|
|
||||||
for {
|
|
||||||
var cleanup func()
|
|
||||||
if len(c.cleanups) > 0 {
|
|
||||||
last := len(c.cleanups) - 1
|
|
||||||
cleanup = c.cleanups[last]
|
|
||||||
c.cleanups = c.cleanups[:last]
|
|
||||||
}
|
|
||||||
if cleanup == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c.cancelCtx != nil {
|
|
||||||
c.cancelCtx()
|
|
||||||
}
|
|
||||||
cleanup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parallel is not implemented, it is only provided for compatibility.
|
|
||||||
func (t *T) Parallel() {
|
|
||||||
// Unimplemented.
|
|
||||||
}
|
|
||||||
|
|
||||||
// InternalTest is a reference to a test that should be called during a test suite run.
|
|
||||||
type InternalTest struct {
|
|
||||||
Name string
|
|
||||||
F func(*T)
|
|
||||||
}
|
|
||||||
|
|
||||||
func tRunner(t *T, fn func(t *T)) {
|
|
||||||
defer func() {
|
|
||||||
t.runCleanup()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Run the test.
|
|
||||||
t.start = time.Now()
|
|
||||||
fn(t)
|
|
||||||
t.duration += time.Since(t.start) // TODO: capture cleanup time, too.
|
|
||||||
|
|
||||||
t.report() // Report after all subtests have finished.
|
|
||||||
if t.parent != nil && !t.hasSub {
|
|
||||||
t.setRan()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run runs f as a subtest of t called name. It waits until the subtest is finished
|
|
||||||
// and returns whether the subtest succeeded.
|
|
||||||
func (t *T) Run(name string, f func(t *T)) bool {
|
|
||||||
t.hasSub = true
|
|
||||||
testName, ok, _ := t.context.match.fullName(&t.common, name)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a subtest.
|
|
||||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
|
||||||
sub := T{
|
|
||||||
common: common{
|
|
||||||
output: &logger{logToStdout: flagVerbose},
|
|
||||||
name: testName,
|
|
||||||
parent: &t.common,
|
|
||||||
level: t.level + 1,
|
|
||||||
ctx: ctx,
|
|
||||||
cancelCtx: cancelCtx,
|
|
||||||
},
|
|
||||||
context: t.context,
|
|
||||||
}
|
|
||||||
if t.level > 0 {
|
|
||||||
sub.indent = sub.indent + " "
|
|
||||||
}
|
|
||||||
if flagVerbose {
|
|
||||||
fmt.Fprintf(t.output, "=== RUN %s\n", sub.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
tRunner(&sub, f)
|
|
||||||
return !sub.failed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deadline reports the time at which the test binary will have
|
|
||||||
// exceeded the timeout specified by the -timeout flag.
|
|
||||||
//
|
|
||||||
// The ok result is false if the -timeout flag indicates “no timeout” (0).
|
|
||||||
// For now tinygo always return 0, false.
|
|
||||||
//
|
|
||||||
// Not Implemented.
|
|
||||||
func (t *T) Deadline() (deadline time.Time, ok bool) {
|
|
||||||
deadline = t.context.deadline
|
|
||||||
return deadline, !deadline.IsZero()
|
|
||||||
}
|
|
||||||
|
|
||||||
// testContext holds all fields that are common to all tests. This includes
|
|
||||||
// synchronization primitives to run at most *parallel tests.
|
|
||||||
type testContext struct {
|
|
||||||
match *matcher
|
|
||||||
deadline time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestContext(m *matcher) *testContext {
|
|
||||||
return &testContext{
|
|
||||||
match: m,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// M is a test suite.
|
|
||||||
type M struct {
|
|
||||||
// tests is a list of the test names to execute
|
|
||||||
Tests []InternalTest
|
|
||||||
Benchmarks []InternalBenchmark
|
|
||||||
|
|
||||||
deps testDeps
|
|
||||||
|
|
||||||
// value to pass to os.Exit, the outer test func main
|
|
||||||
// harness calls os.Exit with this code. See #34129.
|
|
||||||
exitCode int
|
|
||||||
}
|
|
||||||
|
|
||||||
type testDeps interface {
|
|
||||||
MatchString(pat, str string) (bool, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *M) shuffle() error {
|
|
||||||
var n int64
|
|
||||||
|
|
||||||
if flagShuffle == "on" {
|
|
||||||
n = time.Now().UnixNano()
|
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
n, err = strconv.ParseInt(flagShuffle, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
m.exitCode = 2
|
|
||||||
return fmt.Errorf(`testing: -shuffle should be "off", "on", or a valid integer: %v`, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("-test.shuffle", n)
|
|
||||||
rng := rand.New(rand.NewSource(n))
|
|
||||||
rng.Shuffle(len(m.Tests), func(i, j int) { m.Tests[i], m.Tests[j] = m.Tests[j], m.Tests[i] })
|
|
||||||
rng.Shuffle(len(m.Benchmarks), func(i, j int) { m.Benchmarks[i], m.Benchmarks[j] = m.Benchmarks[j], m.Benchmarks[i] })
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run runs the tests. It returns an exit code to pass to os.Exit.
|
|
||||||
func (m *M) Run() (code int) {
|
|
||||||
defer func() {
|
|
||||||
code = m.exitCode
|
|
||||||
}()
|
|
||||||
|
|
||||||
if !flag.Parsed() {
|
|
||||||
flag.Parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
if flagShuffle != "off" {
|
|
||||||
if err := m.shuffle(); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testRan, testOk := runTests(m.deps.MatchString, m.Tests)
|
|
||||||
if !testRan && *matchBenchmarks == "" {
|
|
||||||
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
|
|
||||||
}
|
|
||||||
if !testOk || !runBenchmarks(m.deps.MatchString, m.Benchmarks) {
|
|
||||||
fmt.Println("FAIL")
|
|
||||||
m.exitCode = 1
|
|
||||||
} else {
|
|
||||||
fmt.Println("PASS")
|
|
||||||
m.exitCode = 0
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ran, ok bool) {
|
|
||||||
ok = true
|
|
||||||
|
|
||||||
ctx := newTestContext(newMatcher(matchString, flagRunRegexp, "-test.run", flagSkipRegexp))
|
|
||||||
runCtx, cancelCtx := context.WithCancel(context.Background())
|
|
||||||
t := &T{
|
|
||||||
common: common{
|
|
||||||
output: &logger{logToStdout: flagVerbose},
|
|
||||||
ctx: runCtx,
|
|
||||||
cancelCtx: cancelCtx,
|
|
||||||
},
|
|
||||||
context: ctx,
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < flagCount; i++ {
|
|
||||||
tRunner(t, func(t *T) {
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.Name, test.F)
|
|
||||||
ok = ok && !t.Failed()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.ran, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *T) report() {
|
|
||||||
dstr := fmtDuration(t.duration)
|
|
||||||
format := t.indent + "--- %s: %s (%s)\n"
|
|
||||||
if t.Failed() {
|
|
||||||
if t.parent != nil {
|
|
||||||
t.parent.failed = true
|
|
||||||
}
|
|
||||||
t.flushToParent(t.name, format, "FAIL", t.name, dstr)
|
|
||||||
} else if flagVerbose {
|
|
||||||
if t.Skipped() {
|
|
||||||
t.flushToParent(t.name, format, "SKIP", t.name, dstr)
|
|
||||||
} else {
|
|
||||||
t.flushToParent(t.name, format, "PASS", t.name, dstr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllocsPerRun returns the average number of allocations during calls to f.
|
|
||||||
// Although the return value has type float64, it will always be an integral
|
|
||||||
// value.
|
|
||||||
//
|
|
||||||
// Not implemented.
|
|
||||||
func AllocsPerRun(runs int, f func()) (avg float64) {
|
|
||||||
f()
|
|
||||||
for i := 0; i < runs; i++ {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type InternalExample struct {
|
|
||||||
Name string
|
|
||||||
F func()
|
|
||||||
Output string
|
|
||||||
Unordered bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// MainStart is meant for use by tests generated by 'go test'.
|
|
||||||
// It is not meant to be called directly and is not subject to the Go 1 compatibility document.
|
|
||||||
// It may change signature from release to release.
|
|
||||||
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
|
|
||||||
Init()
|
|
||||||
return &M{
|
|
||||||
Tests: tests,
|
|
||||||
Benchmarks: benchmarks,
|
|
||||||
deps: deps.(testDeps),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A fake regexp matcher.
|
|
||||||
// Inflexible, but saves 50KB of flash and 50KB of RAM per -size full,
|
|
||||||
// and lets tests pass on cortex-m.
|
|
||||||
func fakeMatchString(pat, str string) (bool, error) {
|
|
||||||
if pat == ".*" {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
matched := strings.Contains(str, pat)
|
|
||||||
return matched, nil
|
|
||||||
}
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
//go:build !windows
|
|
||||||
|
|
||||||
// TODO: implement readdir for windows, then enable this file
|
|
||||||
|
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package testing_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io/fs"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is exactly what a test would do without a TestMain.
|
|
||||||
// It's here only so that there is at least one package in the
|
|
||||||
// standard library with a TestMain, so that code is executed.
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
os.Exit(m.Run())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTempDirInCleanup(t *testing.T) {
|
|
||||||
if runtime.GOOS == "wasip1" || runtime.GOOS == "wasip2" {
|
|
||||||
t.Log("Skipping. TODO: implement RemoveAll for wasi")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var dir string
|
|
||||||
|
|
||||||
t.Run("test", func(t *testing.T) {
|
|
||||||
t.Cleanup(func() {
|
|
||||||
dir = t.TempDir()
|
|
||||||
})
|
|
||||||
_ = t.TempDir()
|
|
||||||
})
|
|
||||||
|
|
||||||
fi, err := os.Stat(dir)
|
|
||||||
if fi != nil {
|
|
||||||
t.Fatalf("Directory %q from user Cleanup still exists", dir)
|
|
||||||
}
|
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTempDirInBenchmark(t *testing.T) {
|
|
||||||
testing.Benchmark(func(b *testing.B) {
|
|
||||||
if !b.Run("test", func(b *testing.B) {
|
|
||||||
// Add a loop so that the test won't fail. See issue 38677.
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_ = b.TempDir()
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
t.Fatal("Sub test failure in a benchmark")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTempDir(t *testing.T) {
|
|
||||||
if runtime.GOOS == "wasip1" || runtime.GOOS == "wasip2" {
|
|
||||||
t.Log("Skipping. TODO: implement RemoveAll for wasi")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
testTempDir(t)
|
|
||||||
t.Run("InSubtest", testTempDir)
|
|
||||||
t.Run("test/subtest", testTempDir)
|
|
||||||
t.Run("test\\subtest", testTempDir)
|
|
||||||
t.Run("test:subtest", testTempDir)
|
|
||||||
t.Run("test/..", testTempDir)
|
|
||||||
t.Run("../test", testTempDir)
|
|
||||||
t.Run("test[]", testTempDir)
|
|
||||||
t.Run("test*", testTempDir)
|
|
||||||
t.Run("äöüéè", testTempDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testTempDir(t *testing.T) {
|
|
||||||
dirCh := make(chan string, 1)
|
|
||||||
t.Cleanup(func() {
|
|
||||||
// Verify directory has been removed.
|
|
||||||
select {
|
|
||||||
case dir := <-dirCh:
|
|
||||||
fi, err := os.Stat(dir)
|
|
||||||
if errors.Is(err, fs.ErrNotExist) {
|
|
||||||
// All good
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Errorf("directory %q still exists: %v, isDir=%v", dir, fi, fi.IsDir())
|
|
||||||
default:
|
|
||||||
if !t.Failed() {
|
|
||||||
t.Fatal("never received dir channel")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
if dir == "" {
|
|
||||||
t.Fatal("expected dir")
|
|
||||||
}
|
|
||||||
dir2 := t.TempDir()
|
|
||||||
if dir == dir2 {
|
|
||||||
t.Fatal("subsequent calls to TempDir returned the same directory")
|
|
||||||
}
|
|
||||||
if filepath.Dir(dir) != filepath.Dir(dir2) {
|
|
||||||
t.Fatalf("calls to TempDir do not share a parent; got %q, %q", dir, dir2)
|
|
||||||
}
|
|
||||||
dirCh <- dir
|
|
||||||
fi, err := os.Stat(dir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !fi.IsDir() {
|
|
||||||
t.Errorf("dir %q is not a dir", dir)
|
|
||||||
}
|
|
||||||
files, err := os.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(files) > 0 {
|
|
||||||
t.Errorf("unexpected %d files in TempDir: %v", len(files), files)
|
|
||||||
}
|
|
||||||
|
|
||||||
glob := filepath.Join(dir, "*.txt")
|
|
||||||
if _, err := filepath.Glob(glob); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.Remove(dir)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected files in TempDir")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetenv(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
key string
|
|
||||||
initialValueExists bool
|
|
||||||
initialValue string
|
|
||||||
newValue string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "initial value exists",
|
|
||||||
key: "GO_TEST_KEY_1",
|
|
||||||
initialValueExists: true,
|
|
||||||
initialValue: "111",
|
|
||||||
newValue: "222",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "initial value exists but empty",
|
|
||||||
key: "GO_TEST_KEY_2",
|
|
||||||
initialValueExists: true,
|
|
||||||
initialValue: "",
|
|
||||||
newValue: "222",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "initial value is not exists",
|
|
||||||
key: "GO_TEST_KEY_3",
|
|
||||||
initialValueExists: false,
|
|
||||||
initialValue: "",
|
|
||||||
newValue: "222",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
if test.initialValueExists {
|
|
||||||
if err := os.Setenv(test.key, test.initialValue); err != nil {
|
|
||||||
t.Fatalf("unable to set env: got %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
os.Unsetenv(test.key)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
t.Setenv(test.key, test.newValue)
|
|
||||||
if os.Getenv(test.key) != test.newValue {
|
|
||||||
t.Fatalf("unexpected value after t.Setenv: got %s, want %s", os.Getenv(test.key), test.newValue)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
got, exists := os.LookupEnv(test.key)
|
|
||||||
if got != test.initialValue {
|
|
||||||
t.Fatalf("unexpected value after t.Setenv cleanup: got %s, want %s", got, test.initialValue)
|
|
||||||
}
|
|
||||||
if exists != test.initialValueExists {
|
|
||||||
t.Fatalf("unexpected value after t.Setenv cleanup: got %t, want %t", exists, test.initialValueExists)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTesting(t *testing.T) {
|
|
||||||
if !testing.Testing() {
|
|
||||||
t.Error("Expected testing.Testing() to return true while in a test")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"inherits": [
|
|
||||||
"rp2350b"
|
|
||||||
],
|
|
||||||
"build-tags": ["pico2_ice"],
|
|
||||||
"serial-port": ["2e8a:000A"],
|
|
||||||
"default-stack-size": 8192,
|
|
||||||
"ldflags": [
|
|
||||||
"--defsym=__flash_size=4M"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user