Compare commits
40 Commits
b10cf3e0f4
...
12ec3b9d53
Author | SHA1 | Date |
---|---|---|
WoodyAtHome | 12ec3b9d53 | |
Alexander Medvednikov | d118af0dac | |
playX | 15b84567ec | |
Delyan Angelov | ec5559a379 | |
Delyan Angelov | 972c1855bc | |
yuyi | 57055a9c63 | |
Delyan Angelov | 1064085656 | |
Delyan Angelov | e814497831 | |
yuyi | cdfdf19697 | |
Larpon | f1b35aff22 | |
yuyi | 9eba367a46 | |
spaceface | 4179e1cac2 | |
WoodyAtHome | 9e179ab11f | |
Delyan Angelov | ff02e5667b | |
Delyan Angelov | baf72a7459 | |
Ben | 6a87650935 | |
yuyi | 689f3f7128 | |
Larpon | cf2f34cf3f | |
Jah-On | cd43bae0b4 | |
WoodyAtHome | 8ce7860e8a | |
spaceface | ec2346d45c | |
Delyan Angelov | a133f038bd | |
crthpl | 82018034ef | |
David 'Epper' Marshall | fd17b62ea6 | |
Delyan Angelov | 771ec47a04 | |
Delyan Angelov | 3f2bb98152 | |
Delyan Angelov | 4f1f764485 | |
Larpon | 7492907e77 | |
David 'Epper' Marshall | 153b095518 | |
Delyan Angelov | 46ee0ff572 | |
Delyan Angelov | 1f6849e89c | |
yuyi | f983dd8a95 | |
Alexander Medvednikov | 5a427907d5 | |
Alexander Medvednikov | 58c54a5c05 | |
Daniel Däschle | c6b0ebe06f | |
JalonSolov | b2f520028f | |
j. redhead | 9abaadb690 | |
Delyan Angelov | 638b267e4e | |
Delyan Angelov | b74461f255 | |
Delyan Angelov | d1b8a67bca |
|
@ -33,6 +33,7 @@ jobs:
|
|||
run: |
|
||||
echo $VFLAGS
|
||||
make
|
||||
./v test-cleancode
|
||||
./v -d debug_malloc -d debug_realloc -o v cmd/v
|
||||
./v -cg -cstrict -o v cmd/v
|
||||
# Test v -realloc arena allocation
|
||||
|
@ -609,40 +610,40 @@ jobs:
|
|||
## run: .\v.exe -o v2.exe cmd/v && .\v2.exe -o v3.exe cmd/v
|
||||
|
||||
|
||||
# ubuntu-autofree-selfcompile:
|
||||
# runs-on: ubuntu-20.04
|
||||
# timeout-minutes: 121
|
||||
# env:
|
||||
# VFLAGS: -cc gcc
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Build V
|
||||
# run: make -j4
|
||||
# - name: V self compilation with -autofree
|
||||
# run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
|
||||
# ubuntu-autofree-selfcompile:
|
||||
# runs-on: ubuntu-20.04
|
||||
# timeout-minutes: 121
|
||||
# env:
|
||||
# VFLAGS: -cc gcc
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Build V
|
||||
# run: make -j4
|
||||
# - name: V self compilation with -autofree
|
||||
# run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
|
||||
|
||||
|
||||
# ubuntu-musl:
|
||||
# runs-on: ubuntu-20.04
|
||||
# timeout-minutes: 121
|
||||
# env:
|
||||
# VFLAGS: -cc musl-gcc
|
||||
# V_CI_MUSL: 1
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: actions/setup-node@v1
|
||||
# with:
|
||||
# node-version: 12.x
|
||||
# - name: Install dependencies
|
||||
# run: |
|
||||
# sudo apt-get install --quiet -y musl musl-tools libssl-dev sqlite3 libsqlite3-dev valgrind
|
||||
# - name: Build v
|
||||
# run: echo $VFLAGS && make -j4 && ./v -cg -o v cmd/v
|
||||
# # - name: Test v binaries
|
||||
# # run: ./v build-vbinaries
|
||||
# ## - name: Test v->js
|
||||
# ## run: ./v -o hi.js examples/hello_v_js.v && node hi.js
|
||||
# - name: quick debug
|
||||
# run: ./v -stats vlib/strconv/format_test.v
|
||||
# - name: Self tests
|
||||
# run: ./v test-self
|
||||
# ubuntu-musl:
|
||||
# runs-on: ubuntu-20.04
|
||||
# timeout-minutes: 121
|
||||
# env:
|
||||
# VFLAGS: -cc musl-gcc
|
||||
# V_CI_MUSL: 1
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: actions/setup-node@v1
|
||||
# with:
|
||||
# node-version: 12.x
|
||||
# - name: Install dependencies
|
||||
# run: |
|
||||
# sudo apt-get install --quiet -y musl musl-tools libssl-dev sqlite3 libsqlite3-dev valgrind
|
||||
# - name: Build v
|
||||
# run: echo $VFLAGS && make -j4 && ./v -cg -o v cmd/v
|
||||
# # - name: Test v binaries
|
||||
# # run: ./v build-vbinaries
|
||||
# ## - name: Test v->js
|
||||
# ## run: ./v -o hi.js examples/hello_v_js.v && node hi.js
|
||||
# - name: quick debug
|
||||
# run: ./v -stats vlib/strconv/format_test.v
|
||||
# - name: Self tests
|
||||
# run: ./v test-self
|
||||
|
|
|
@ -16,7 +16,7 @@ on:
|
|||
paths:
|
||||
- '!**'
|
||||
- 'cmd/tools/vtest*'
|
||||
- 'cmd/tools/builders/**.v'
|
||||
- 'cmd/tools/builders/**.v'
|
||||
- 'vlib/builtin/**.v'
|
||||
- 'vlib/strconv/**.v'
|
||||
- 'vlib/strings/**.v'
|
||||
|
@ -43,7 +43,7 @@ on:
|
|||
paths:
|
||||
- '!**'
|
||||
- 'cmd/tools/vtest*'
|
||||
- 'cmd/tools/builders/**.v'
|
||||
- 'cmd/tools/builders/**.v'
|
||||
- 'vlib/builtin/**.v'
|
||||
- 'vlib/strconv/**.v'
|
||||
- 'vlib/strings/**.v'
|
||||
|
@ -91,7 +91,9 @@ jobs:
|
|||
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
|
||||
sudo apt-get install clang
|
||||
- name: Build V
|
||||
run: make -j4 && ./v -cg -cstrict -o v cmd/v
|
||||
run: make && ./v -cg -cstrict -o v cmd/v
|
||||
- name: Ensure code is well formatted
|
||||
run: ./v test-cleancode
|
||||
- name: Self tests (-fsanitize=undefined)
|
||||
run: ./v -cflags "-fsanitize=undefined" -o v2 cmd/v && ./v2 -cflags -fsanitize=undefined test-self
|
||||
- name: Build examples (V compiled with -fsanitize=undefined)
|
||||
|
@ -115,7 +117,9 @@ jobs:
|
|||
sudo apt-get install --quiet -y postgresql libpq-dev libssl-dev sqlite3 libsqlite3-dev valgrind
|
||||
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
|
||||
- name: Build V
|
||||
run: make -j4 && ./v -cg -cstrict -o v cmd/v
|
||||
run: make && ./v -cg -cstrict -o v cmd/v
|
||||
- name: Ensure code is well formatted
|
||||
run: ./v test-cleancode
|
||||
- name: Self tests (-fsanitize=undefined)
|
||||
run: ./v -cflags "-fsanitize=undefined" -o v2 cmd/v && ./v2 -cflags -fsanitize=undefined test-self
|
||||
- name: Build examples (V compiled with -fsanitize=undefined)
|
||||
|
@ -140,7 +144,9 @@ jobs:
|
|||
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
|
||||
sudo apt-get install clang
|
||||
- name: Build V
|
||||
run: make -j4 && ./v -cg -cstrict -o v cmd/v
|
||||
run: make && ./v -cg -cstrict -o v cmd/v
|
||||
- name: Ensure code is well formatted
|
||||
run: ./v test-cleancode
|
||||
- name: Self tests (-fsanitize=address)
|
||||
run: ASAN_OPTIONS=detect_leaks=0 ./v -cflags "-fsanitize=address,pointer-compare,pointer-subtract" test-self
|
||||
- name: Self tests (V compiled with -fsanitize=address)
|
||||
|
@ -168,6 +174,11 @@ jobs:
|
|||
echo $VFLAGS
|
||||
.\make.bat -msvc
|
||||
.\v.exe self
|
||||
- name: Ensure code is well formatted
|
||||
run: |
|
||||
.\v.exe fmt -verify vlib/builtin/ vlib/v/scanner/ vlib/v/parser/ vlib/v/gen/
|
||||
## TODO: check to see why `v test-cleancode` does not work with msvc on windows
|
||||
|
||||
## - name: Install dependencies
|
||||
## run: |
|
||||
## .\v.exe setup-freetype
|
||||
|
@ -195,7 +206,9 @@ jobs:
|
|||
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
|
||||
sudo apt-get install clang
|
||||
- name: Build V
|
||||
run: make -j4 && ./v -cg -cstrict -o v cmd/v
|
||||
run: make && ./v -cg -cstrict -o v cmd/v
|
||||
- name: Ensure code is well formatted
|
||||
run: ./v test-cleancode
|
||||
- name: Self tests (-fsanitize=address)
|
||||
run: ASAN_OPTIONS=detect_leaks=0 ./v -cflags -fsanitize=address test-self
|
||||
- name: Self tests (V compiled with -fsanitize=address)
|
||||
|
@ -224,11 +237,14 @@ jobs:
|
|||
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
|
||||
sudo apt-get install clang
|
||||
- name: Build V
|
||||
run: make -j4 && ./v -cc clang -cg -cstrict -o v cmd/v
|
||||
run: make && ./v -cc clang -cg -cstrict -o v cmd/v
|
||||
- name: Ensure code is well formatted
|
||||
run: ./v test-cleancode
|
||||
- name: Self tests (-fsanitize=memory)
|
||||
run: ./v -cflags -fsanitize=memory test-self
|
||||
- name: Self tests (V compiled with -fsanitize=memory)
|
||||
run:
|
||||
./v -cflags -fsanitize=memory -o v cmd/v && ./v -cc tcc test-self -msan-compiler
|
||||
run: |
|
||||
./v -cflags -fsanitize=memory -o v cmd/v
|
||||
./v -cc tcc test-self -msan-compiler
|
||||
- name: Build examples (V compiled with -fsanitize=memory)
|
||||
run: ./v build-examples
|
||||
|
|
|
@ -85,6 +85,7 @@ jobs:
|
|||
- name: g++ version
|
||||
run: g++-9 --version
|
||||
- name: V self compilation with g++
|
||||
continue-on-error: true
|
||||
run: ./v -cc g++-9 -no-std -cflags -std=c++11 -o v2 cmd/v && ./v2 -cc g++-9 -no-std -cflags -std=c++11 -o v3 cmd/v
|
||||
## - name: Running tests with g++
|
||||
## run: ./v -cc g++-9 test-self
|
||||
|
@ -93,6 +94,7 @@ jobs:
|
|||
run: ./v -autofree -o v2 cmd/v ## NB: this does not mean it runs, but at least keeps it from regressing
|
||||
|
||||
- name: Shader examples can be build
|
||||
continue-on-error: true
|
||||
run: |
|
||||
wget https://github.com/floooh/sokol-tools-bin/raw/33d2e4cc26088c6c28eaef5467990f8940d15aab/bin/linux/sokol-shdc
|
||||
chmod +x ./sokol-shdc
|
||||
|
@ -146,117 +148,3 @@ jobs:
|
|||
./v test-parser -S examples/regex_example_fuzz.v
|
||||
./v test-parser -S examples/2048/2048_fuzz.v
|
||||
|
||||
v-apps-compile:
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 121
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build V
|
||||
run: make && sudo ./v symlink
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --quiet -y libgc-dev
|
||||
sudo apt-get install --quiet -y libsodium-dev libssl-dev sqlite3 libsqlite3-dev valgrind
|
||||
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
|
||||
sudo apt-get install --quiet -y xfonts-75dpi xfonts-base
|
||||
|
||||
## vls
|
||||
- name: Clone VLS
|
||||
run: git clone --depth 1 https://github.com/vlang/vls
|
||||
- name: Build VLS
|
||||
run: pushd vls; v cmd/vls ; popd
|
||||
- name: Build VLS with -prod
|
||||
run: pushd vls; v -prod cmd/vls; popd
|
||||
- name: Build VLS with -gc boehm -skip-unused
|
||||
run: pushd vls; v -gc boehm -skip-unused cmd/vls; popd
|
||||
|
||||
## vsl
|
||||
- name: Clone VSL
|
||||
run: git clone --depth 1 https://github.com/vlang/vsl ~/.vmodules/vsl
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install --quiet -y --no-install-recommends gfortran liblapacke-dev libopenblas-dev libgc-dev
|
||||
- name: Execute Tests using Pure V Backend
|
||||
run: ~/.vmodules/vsl/bin/test
|
||||
- name: Execute Tests using Pure V Backend with Pure V Math
|
||||
run: ~/.vmodules/vsl/bin/test --use-cblas
|
||||
- name: Execute Tests using Pure V Backend and Garbage Collection enabled
|
||||
run: ~/.vmodules/vsl/bin/test --use-gc boehm
|
||||
- name: Execute Tests using Pure V Backend with Pure V Math and Garbage Collection enabled
|
||||
run: ~/.vmodules/vsl/bin/test --use-cblas --use-gc boehm
|
||||
|
||||
## vtl
|
||||
- name: Clone VTL
|
||||
run: git clone --depth 1 https://github.com/vlang/vtl ~/.vmodules/vtl
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install --quiet -y --no-install-recommends gfortran liblapacke-dev libopenblas-dev libgc-dev
|
||||
- name: Execute Tests using Pure V Backend
|
||||
run: ~/.vmodules/vtl/bin/test
|
||||
- name: Execute Tests using Pure V Backend with Pure V Math
|
||||
run: ~/.vmodules/vtl/bin/test --use-cblas
|
||||
- name: Execute Tests using Pure V Backend and Garbage Collection enabled
|
||||
run: ~/.vmodules/vtl/bin/test --use-gc boehm
|
||||
- name: Execute Tests using Pure V Backend with Pure V Math and Garbage Collection enabled
|
||||
run: ~/.vmodules/vtl/bin/test --use-cblas --use-gc boehm
|
||||
|
||||
## vab
|
||||
- name: Clone vab
|
||||
run: git clone --depth 1 https://github.com/vlang/vab
|
||||
- name: Build vab
|
||||
run: cd vab; ../v ./vab.v ; cd ..
|
||||
- name: Build vab with -gc boehm -skip-unused
|
||||
run: cd vab; ../v -gc boehm -skip-unused ./vab.v ; cd ..
|
||||
|
||||
## gitly
|
||||
- name: Install markdown
|
||||
run: git clone https://github.com/vlang/markdown ~/.vmodules/markdown
|
||||
- name: Build Gitly
|
||||
run: |
|
||||
git clone --depth 1 https://github.com/vlang/gitly
|
||||
cd gitly
|
||||
../v .
|
||||
# ./gitly -ci_run
|
||||
../v -autofree .
|
||||
../v -o x tests/first_run.v
|
||||
./x
|
||||
cd ..
|
||||
|
||||
## libsodium
|
||||
- name: Install libsodium-dev package
|
||||
run: sudo apt-get install --quiet -y libsodium-dev
|
||||
- name: Install the libsodium wrapper
|
||||
run: git clone https://github.com/vlang/libsodium ~/.vmodules/libsodium
|
||||
- name: Test libsodium
|
||||
run: VJOBS=1 ./v -stats test ~/.vmodules/libsodium
|
||||
|
||||
|
||||
## vex
|
||||
- name: Install Vex dependencies
|
||||
run: sudo apt-get install --quiet -y libsodium-dev libssl-dev sqlite3 libsqlite3-dev
|
||||
- name: Install Vex
|
||||
run: mkdir -p ~/.vmodules/nedpals; git clone https://github.com/nedpals/vex ~/.vmodules/nedpals/vex
|
||||
- name: Compile all of the Vex examples
|
||||
run: ./v should-compile-all ~/.vmodules/nedpals/vex/examples
|
||||
- name: Compile the simple Vex example with -gc boehm -skip-unused
|
||||
run: ./v -gc boehm -skip-unused ~/.vmodules/nedpals/vex/examples/simple_example.v
|
||||
- name: Run Vex Tests
|
||||
run: ./v test ~/.vmodules/nedpals/vex
|
||||
|
||||
## Go2V
|
||||
- name: Clone & Build go2v
|
||||
run: git clone --depth=1 https://github.com/vlang/go2v go2v/
|
||||
- name: Build go2v
|
||||
run: ./v go2v/
|
||||
## - name: Run tests for go2v
|
||||
## run: VJOBS=1 ./v -stats test go2v/
|
||||
|
||||
## vlang/pdf
|
||||
- name: Clone & Build vlang/pdf
|
||||
run: git clone --depth=1 https://github.com/vlang/pdf ~/.vmodules/pdf/
|
||||
- name: PDF examples should compile
|
||||
run: ./v should-compile-all ~/.vmodules/pdf/examples
|
||||
|
||||
## vpm modules
|
||||
- name: Install UI through VPM
|
||||
run: ./v install ui
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
name: V Apps and Modules
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
|
||||
concurrency:
|
||||
group: build-other-${{ github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
v-apps-compile:
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 121
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build V
|
||||
run: make && sudo ./v symlink
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --quiet -y libgc-dev
|
||||
sudo apt-get install --quiet -y libsodium-dev libssl-dev sqlite3 libsqlite3-dev valgrind
|
||||
sudo apt-get install --quiet -y libfreetype6-dev libxi-dev libxcursor-dev libgl-dev
|
||||
sudo apt-get install --quiet -y xfonts-75dpi xfonts-base
|
||||
|
||||
- name: Install UI through VPM
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "Official VPM modules should be installable"
|
||||
./v install ui
|
||||
|
||||
- name: Build V Language Server (VLS)
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "Clone VLS"
|
||||
git clone --depth 1 https://github.com/vlang/vls
|
||||
echo "Build VLS"
|
||||
pushd vls; v cmd/vls ; popd
|
||||
echo "Build VLS with -prod"
|
||||
pushd vls; v -prod cmd/vls; popd
|
||||
echo "Build VLS with -gc boehm -skip-unused"
|
||||
pushd vls; v -gc boehm -skip-unused cmd/vls; popd
|
||||
|
||||
- name: Build VSL
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git clone --depth 1 https://github.com/vlang/vsl ~/.vmodules/vsl
|
||||
sudo apt-get install --quiet -y --no-install-recommends gfortran liblapacke-dev libopenblas-dev libgc-dev
|
||||
echo "Execute Tests using Pure V Backend"
|
||||
~/.vmodules/vsl/bin/test
|
||||
echo "Execute Tests using Pure V Backend with Pure V Math"
|
||||
~/.vmodules/vsl/bin/test --use-cblas
|
||||
echo "Execute Tests using Pure V Backend and Garbage Collection enabled"
|
||||
~/.vmodules/vsl/bin/test --use-gc boehm
|
||||
echo "Execute Tests using Pure V Backend with Pure V Math and Garbage Collection enabled"
|
||||
~/.vmodules/vsl/bin/test --use-cblas --use-gc boehm
|
||||
|
||||
- name: Build VTL
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "Clone VTL"
|
||||
git clone --depth 1 https://github.com/vlang/vtl ~/.vmodules/vtl
|
||||
echo "Install dependencies"
|
||||
sudo apt-get install --quiet -y --no-install-recommends gfortran liblapacke-dev libopenblas-dev libgc-dev
|
||||
echo "Execute Tests using Pure V Backend"
|
||||
~/.vmodules/vtl/bin/test
|
||||
echo "Execute Tests using Pure V Backend with Pure V Math"
|
||||
~/.vmodules/vtl/bin/test --use-cblas
|
||||
echo "Execute Tests using Pure V Backend and Garbage Collection enabled"
|
||||
~/.vmodules/vtl/bin/test --use-gc boehm
|
||||
echo "Execute Tests using Pure V Backend with Pure V Math and Garbage Collection enabled"
|
||||
~/.vmodules/vtl/bin/test --use-cblas --use-gc boehm
|
||||
|
||||
- name: Build VAB
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "Clone vab"
|
||||
git clone --depth 1 https://github.com/vlang/vab
|
||||
echo "Build vab"
|
||||
cd vab; ../v ./vab.v ; cd ..
|
||||
echo "Build vab with -gc boehm -skip-unused"
|
||||
cd vab; ../v -gc boehm -skip-unused ./vab.v ; cd ..
|
||||
|
||||
- name: Build Gitly
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "Clone markdown"
|
||||
git clone https://github.com/vlang/markdown ~/.vmodules/markdown
|
||||
echo "Clone Gitly"
|
||||
git clone --depth 1 https://github.com/vlang/gitly
|
||||
cd gitly
|
||||
echo "Build Gitly"
|
||||
../v .
|
||||
echo "Build Gitly with -autofree"
|
||||
../v -autofree .
|
||||
echo "Run first_run.v"
|
||||
../v run tests/first_run.v
|
||||
# ./gitly -ci_run
|
||||
|
||||
- name: Build libsodium
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "Install libsodium-dev package"
|
||||
sudo apt-get install --quiet -y libsodium-dev
|
||||
echo "Clone the libsodium wrapper"
|
||||
git clone https://github.com/vlang/libsodium ~/.vmodules/libsodium
|
||||
echo "Test libsodium"
|
||||
VJOBS=1 ./v -stats test ~/.vmodules/libsodium
|
||||
|
||||
- name: Build VEX
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "Install Vex dependencies"
|
||||
sudo apt-get install --quiet -y libsodium-dev libssl-dev sqlite3 libsqlite3-dev
|
||||
echo "Clone Vex"
|
||||
mkdir -p ~/.vmodules/nedpals; git clone https://github.com/nedpals/vex ~/.vmodules/nedpals/vex
|
||||
echo "Compile all of the Vex examples"
|
||||
./v should-compile-all ~/.vmodules/nedpals/vex/examples
|
||||
echo "Compile the simple Vex example with -gc boehm -skip-unused"
|
||||
./v -gc boehm -skip-unused ~/.vmodules/nedpals/vex/examples/simple_example.v
|
||||
echo "Run Vex Tests"
|
||||
./v test ~/.vmodules/nedpals/vex
|
||||
|
||||
- name: Build go2v
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "Clone go2v"
|
||||
clone --depth=1 https://github.com/vlang/go2v go2v/
|
||||
echo "Build go2v"
|
||||
./v go2v/
|
||||
## echo "Run tests for go2v"
|
||||
## VJOBS=1 ./v -stats test go2v/
|
||||
|
||||
- name: Build vlang/pdf
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git clone --depth=1 https://github.com/vlang/pdf ~/.vmodules/pdf/
|
||||
echo "PDF examples should compile"
|
||||
./v should-compile-all ~/.vmodules/pdf/examples
|
|
@ -37,7 +37,6 @@ pub mut:
|
|||
vroot string
|
||||
vtmp_dir string
|
||||
vargs string
|
||||
failed bool
|
||||
fail_fast bool
|
||||
benchmark benchmark.Benchmark
|
||||
rm_binaries bool = true
|
||||
|
@ -288,7 +287,7 @@ pub fn (mut ts TestSession) test() {
|
|||
fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
|
||||
mut ts := &TestSession(p.get_shared_context())
|
||||
if ts.fail_fast {
|
||||
if ts.failed {
|
||||
if ts.failed_cmds.len > 0 {
|
||||
return pool.no_result
|
||||
}
|
||||
}
|
||||
|
@ -380,7 +379,6 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
|
|||
goto test_passed_system
|
||||
}
|
||||
}
|
||||
ts.failed = true
|
||||
ts.benchmark.fail()
|
||||
tls_bench.fail()
|
||||
ts.add_failed_cmd(cmd)
|
||||
|
@ -396,7 +394,6 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
|
|||
}
|
||||
mut r := os.execute(cmd)
|
||||
if r.exit_code < 0 {
|
||||
ts.failed = true
|
||||
ts.benchmark.fail()
|
||||
tls_bench.fail()
|
||||
ts.append_message(.fail, tls_bench.step_message_fail(normalised_relative_file))
|
||||
|
@ -422,7 +419,6 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
|
|||
goto test_passed_execute
|
||||
}
|
||||
}
|
||||
ts.failed = true
|
||||
ts.benchmark.fail()
|
||||
tls_bench.fail()
|
||||
ending_newline := if r.output.ends_with('\n') { '\n' } else { '' }
|
||||
|
@ -510,7 +506,7 @@ pub fn v_build_failing_skipped(zargs string, folder string, oskipped []string, c
|
|||
cb(mut session)
|
||||
session.test()
|
||||
eprintln(session.benchmark.total_message(finish_label))
|
||||
return session.failed
|
||||
return session.failed_cmds.len > 0
|
||||
}
|
||||
|
||||
pub fn build_v_cmd_failed(cmd string) bool {
|
||||
|
|
|
@ -43,7 +43,7 @@ fn main() {
|
|||
// eprintln('> session.skip_files: $session.skip_files')
|
||||
session.test()
|
||||
eprintln(session.benchmark.total_message(finish_label))
|
||||
if session.failed {
|
||||
if session.failed_cmds.len > 0 {
|
||||
exit(1)
|
||||
}
|
||||
//
|
||||
|
|
|
@ -8,6 +8,8 @@ const github_job = os.getenv('GITHUB_JOB')
|
|||
|
||||
const (
|
||||
skip_test_files = [
|
||||
'cmd/tools/vdoc/html_tag_escape_test.v', /* can't locate local module: markdown */
|
||||
'cmd/tools/vdoc/tests/vdoc_file_test.v', /* fails on Windows; order of output is not as expected */
|
||||
'vlib/context/onecontext/onecontext_test.v',
|
||||
'vlib/context/deadline_test.v' /* sometimes blocks */,
|
||||
'vlib/mysql/mysql_orm_test.v' /* mysql not installed */,
|
||||
|
@ -164,6 +166,7 @@ fn main() {
|
|||
cmd_prefix := args_string.all_before('test-self')
|
||||
title := 'testing vlib'
|
||||
mut all_test_files := os.walk_ext(os.join_path(vroot, 'vlib'), '_test.v')
|
||||
all_test_files << os.walk_ext(os.join_path(vroot, 'cmd'), '_test.v')
|
||||
test_js_files := os.walk_ext(os.join_path(vroot, 'vlib'), '_test.js.v')
|
||||
all_test_files << test_js_files
|
||||
testing.eheader(title)
|
||||
|
|
|
@ -69,7 +69,7 @@ fn main() {
|
|||
testing.header('Testing...')
|
||||
ts.test()
|
||||
println(ts.benchmark.total_message('all V _test.v files'))
|
||||
if ts.failed {
|
||||
if ts.failed_cmds.len > 0 {
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
cmd/tools/vvet/tests/array_init_one_val.vv:2: error: Use `var == value` instead of `var in [value]`
|
||||
NB: You can run `v fmt -w file.v` to fix these errors automatically
|
||||
Note: You can run `v fmt -w file.v` to fix these errors automatically
|
||||
|
|
|
@ -3,4 +3,4 @@ cmd/tools/vvet/tests/indent_with_space.vv:10: error: Looks like you are using sp
|
|||
cmd/tools/vvet/tests/indent_with_space.vv:17: error: Looks like you are using spaces for indentation.
|
||||
cmd/tools/vvet/tests/indent_with_space.vv:20: error: Looks like you are using spaces for indentation.
|
||||
cmd/tools/vvet/tests/indent_with_space.vv:22: error: Looks like you are using spaces for indentation.
|
||||
NB: You can run `v fmt -w file.v` to fix these errors automatically
|
||||
Note: You can run `v fmt -w file.v` to fix these errors automatically
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
// Some header comment
|
||||
|
||||
// read_response is a carefully constructed comment.
|
||||
// read_response_body. <-- this would earlier trigger a false
|
||||
// postive.
|
||||
pub fn read_response() ?(string, string) {}
|
|
@ -1,2 +1,2 @@
|
|||
cmd/tools/vvet/tests/parens_space_a.vv:1: error: Looks like you are adding a space after `(`
|
||||
NB: You can run `v fmt -w file.v` to fix these errors automatically
|
||||
Note: You can run `v fmt -w file.v` to fix these errors automatically
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
cmd/tools/vvet/tests/parens_space_b.vv:1: error: Looks like you are adding a space before `)`
|
||||
NB: You can run `v fmt -w file.v` to fix these errors automatically
|
||||
Note: You can run `v fmt -w file.v` to fix these errors automatically
|
||||
|
|
|
@ -182,13 +182,17 @@ fn (mut vt Vet) vet_fn_documentation(lines []string, line string, lnumber int) {
|
|||
fn_name := ident_fn_name(line)
|
||||
mut grab := true
|
||||
for j := lnumber - 1; j >= 0; j-- {
|
||||
mut prev_prev_line := ''
|
||||
if j - 1 >= 0 {
|
||||
prev_prev_line = lines[j - 1]
|
||||
}
|
||||
prev_line := lines[j]
|
||||
if prev_line.contains('}') { // We've looked back to the above scope, stop here
|
||||
break
|
||||
} else if prev_line.starts_with('// $fn_name ') {
|
||||
grab = false
|
||||
break
|
||||
} else if prev_line.starts_with('// $fn_name') {
|
||||
} else if prev_line.starts_with('// $fn_name') && !prev_prev_line.starts_with('//') {
|
||||
grab = false
|
||||
clean_line := line.all_before_last('{').trim(' ')
|
||||
vt.warn('The documentation for "$clean_line" seems incomplete.', lnumber,
|
||||
|
|
|
@ -556,11 +556,11 @@ fn (mut app App) set_theme(idx int) {
|
|||
}
|
||||
|
||||
fn (mut app App) resize() {
|
||||
mut s := gg.dpi_scale()
|
||||
mut s := app.gg.scale
|
||||
if s == 0.0 {
|
||||
s = 1.0
|
||||
}
|
||||
window_size := gg.window_size()
|
||||
window_size := app.gg.window_size()
|
||||
w := window_size.width
|
||||
h := window_size.height
|
||||
m := f32(math.min(w, h))
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
Module {
|
||||
name: 'submodule'
|
||||
description: ''
|
||||
version: ''
|
||||
license: ''
|
||||
dependencies: []
|
||||
}
|
|
@ -2002,3 +2002,8 @@ pub fn (name string) match_glob(pattern string) bool {
|
|||
// Matched all of `pattern` to all of `name`
|
||||
return true
|
||||
}
|
||||
|
||||
// is_ascii returns true if all characters belong to the US-ASCII set ([` `..`~`])
|
||||
pub fn (s string) is_ascii() bool {
|
||||
return !s.bytes().any(it < u8(` `) || it > u8(`~`))
|
||||
}
|
||||
|
|
|
@ -989,6 +989,16 @@ fn test_string_f32() {
|
|||
assert '-123.456'.f32() - (-123.456) <= f32_epsilon
|
||||
}
|
||||
|
||||
fn test_string_is_ascii() {
|
||||
assert ''.is_ascii() == true
|
||||
assert ' '.is_ascii() == true
|
||||
assert '~~'.is_ascii() == true
|
||||
assert ' Az~'.is_ascii() == true
|
||||
assert ' Aö~'.is_ascii() == false
|
||||
assert '👋'.is_ascii() == false
|
||||
assert 'a👋bc'.is_ascii() == false
|
||||
}
|
||||
|
||||
fn test_string_with_zero_byte_escape() {
|
||||
assert '\x00'.bytes() == [u8(0)]
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ module clipboard
|
|||
// Clipboard represents a system clipboard.
|
||||
//
|
||||
// System "copy" and "paste" actions utilize the clipboard for temporary storage.
|
||||
[heap]
|
||||
pub struct Clipboard {
|
||||
pb voidptr
|
||||
last_cb_serial i64
|
||||
|
|
|
@ -54,6 +54,7 @@ fn C.DestroyWindow(hwnd C.HWND)
|
|||
// Clipboard represents a system clipboard.
|
||||
//
|
||||
// System "copy" and "paste" actions utilize the clipboard for temporary storage.
|
||||
[heap]
|
||||
struct Clipboard {
|
||||
max_retries int
|
||||
retry_delay int
|
||||
|
|
|
@ -3,6 +3,7 @@ module dummy
|
|||
// Clipboard represents a system clipboard.
|
||||
//
|
||||
// System "copy" and "paste" actions utilize the clipboard for temporary storage.
|
||||
[heap]
|
||||
pub struct Clipboard {
|
||||
mut:
|
||||
text string // text data sent or received
|
||||
|
|
|
@ -138,6 +138,7 @@ enum AtomType {
|
|||
text_html = 9
|
||||
}
|
||||
|
||||
[heap]
|
||||
pub struct Clipboard {
|
||||
display &C.Display
|
||||
mut:
|
||||
|
@ -291,6 +292,7 @@ fn (mut cb Clipboard) start_listener() {
|
|||
mut sent_request := false
|
||||
mut to_be_requested := Atom(0)
|
||||
for {
|
||||
time.sleep(1 * time.millisecond)
|
||||
C.XNextEvent(cb.display, &event)
|
||||
if unsafe { event.@type == 0 } {
|
||||
println('error')
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
module math
|
||||
|
||||
// The vlang code is a modified version of the original C code from
|
||||
// http://www.netlib.org/fdlibm/s_cbrt.c and came with this notice.
|
||||
//
|
||||
// ====================================================
|
||||
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
//
|
||||
// Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
// Permission to use, copy, modify, and distribute this
|
||||
// software is freely granted, provided that this notice
|
||||
// is preserved.
|
||||
// ====================================================
|
||||
|
||||
// cbrt returns the cube root of a.
|
||||
//
|
||||
// special cases are:
|
||||
|
@ -25,12 +37,12 @@ pub fn cbrt(a f64) f64 {
|
|||
sign = true
|
||||
}
|
||||
// rough cbrt to 5 bits
|
||||
mut t := f64_from_bits(f64_bits(x) / u64(3 + (u64(b1) << 32)))
|
||||
mut t := f64_from_bits(f64_bits(x) / u64(3) + (u64(b1) << 32))
|
||||
if x < smallest_normal {
|
||||
// subnormal number
|
||||
t = f64(u64(1) << 54) // set t= 2**54
|
||||
t *= x
|
||||
t = f64_from_bits(f64_bits(t) / u64(3 + (u64(b2) << 32)))
|
||||
t = f64_from_bits(f64_bits(t) / u64(3) + (u64(b2) << 32))
|
||||
}
|
||||
// new cbrt to 23 bits
|
||||
mut r := t * t / x
|
||||
|
|
|
@ -506,6 +506,13 @@ fn test_mod() {
|
|||
assert (0.6447968302508578) == f
|
||||
}
|
||||
|
||||
fn test_cbrt() {
|
||||
cbrts := [2.0, 10, 56]
|
||||
for idx, i in [8.0, 1000, 175_616] {
|
||||
assert cbrt(i) == cbrts[idx]
|
||||
}
|
||||
}
|
||||
|
||||
fn test_exp() {
|
||||
for i := 0; i < math.vf_.len; i++ {
|
||||
f := exp(math.vf_[i])
|
||||
|
|
|
@ -66,6 +66,7 @@ mut:
|
|||
buffer_size int
|
||||
}
|
||||
|
||||
// new returns an `FTP` instance.
|
||||
pub fn new() FTP {
|
||||
mut f := FTP{
|
||||
conn: 0
|
||||
|
@ -101,6 +102,7 @@ fn (mut zftp FTP) read() ?(int, string) {
|
|||
return code, data
|
||||
}
|
||||
|
||||
// connect establishes an FTP connection to the host at `ip` port 21.
|
||||
pub fn (mut zftp FTP) connect(ip string) ?bool {
|
||||
zftp.conn = net.dial_tcp('$ip:21')?
|
||||
zftp.reader = io.new_buffered_reader(reader: zftp.conn)
|
||||
|
@ -111,6 +113,7 @@ pub fn (mut zftp FTP) connect(ip string) ?bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// login sends the "USER `user`" and "PASS `passwd`" commands to the remote host.
|
||||
pub fn (mut zftp FTP) login(user string, passwd string) ?bool {
|
||||
zftp.write('USER $user') or {
|
||||
$if debug {
|
||||
|
@ -138,11 +141,13 @@ pub fn (mut zftp FTP) login(user string, passwd string) ?bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// close closes the FTP connection.
|
||||
pub fn (mut zftp FTP) close() ? {
|
||||
zftp.write('QUIT')?
|
||||
zftp.conn.close()?
|
||||
}
|
||||
|
||||
// pwd returns the current working directory on the remote host for the logged in user.
|
||||
pub fn (mut zftp FTP) pwd() ?string {
|
||||
zftp.write('PWD')?
|
||||
_, data := zftp.read()?
|
||||
|
@ -153,6 +158,7 @@ pub fn (mut zftp FTP) pwd() ?string {
|
|||
return data
|
||||
}
|
||||
|
||||
// cd changes the current working directory to the specified remote directory `dir`.
|
||||
pub fn (mut zftp FTP) cd(dir string) ? {
|
||||
zftp.write('CWD $dir') or { return }
|
||||
mut code, mut data := zftp.read()?
|
||||
|
@ -201,6 +207,7 @@ fn (mut zftp FTP) pasv() ?&DTP {
|
|||
return dtp
|
||||
}
|
||||
|
||||
// dir returns a list of the files in the current working directory.
|
||||
pub fn (mut zftp FTP) dir() ?[]string {
|
||||
mut dtp := zftp.pasv() or { return error('Cannot establish data connection') }
|
||||
zftp.write('LIST')?
|
||||
|
@ -227,6 +234,7 @@ pub fn (mut zftp FTP) dir() ?[]string {
|
|||
return dir
|
||||
}
|
||||
|
||||
// get retrieves `file` from the remote host.
|
||||
pub fn (mut zftp FTP) get(file string) ?[]u8 {
|
||||
mut dtp := zftp.pasv() or { return error('Cannot stablish data connection') }
|
||||
zftp.write('RETR $file')?
|
||||
|
|
|
@ -151,30 +151,31 @@ pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &u8, len int) ?int {
|
|||
mut res := 0
|
||||
for {
|
||||
res = C.SSL_read(voidptr(s.ssl), buf_ptr, len)
|
||||
if res < 0 {
|
||||
if res >= 0 {
|
||||
return res
|
||||
} else {
|
||||
err_res := ssl_error(res, s.ssl)?
|
||||
if err_res == .ssl_error_want_read {
|
||||
for {
|
||||
match err_res {
|
||||
.ssl_error_want_read {
|
||||
ready := @select(s.handle, .read, s.duration)?
|
||||
if ready {
|
||||
break
|
||||
if !ready {
|
||||
return net.err_timed_out
|
||||
}
|
||||
}
|
||||
continue
|
||||
} else if err_res == .ssl_error_want_write {
|
||||
for {
|
||||
.ssl_error_want_write {
|
||||
ready := @select(s.handle, .write, s.duration)?
|
||||
if ready {
|
||||
break
|
||||
if !ready {
|
||||
return net.err_timed_out
|
||||
}
|
||||
}
|
||||
continue
|
||||
} else if err_res == .ssl_error_zero_return {
|
||||
return 0
|
||||
.ssl_error_zero_return {
|
||||
return 0
|
||||
}
|
||||
else {
|
||||
return error('Could not read using SSL. ($err_res)')
|
||||
}
|
||||
}
|
||||
return error('Could not read using SSL. ($err_res)')
|
||||
}
|
||||
break
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -228,13 +228,20 @@ fn (mut c Client) send_data() ? {
|
|||
fn (mut c Client) send_body(cfg Mail) ? {
|
||||
is_html := cfg.body_type == .html
|
||||
date := cfg.date.custom_format('ddd, D MMM YYYY HH:mm ZZ')
|
||||
nonascii_subject := cfg.subject.bytes().any(it < u8(` `) || it > u8(`~`))
|
||||
mut sb := strings.new_builder(200)
|
||||
sb.write_string('From: $cfg.from\r\n')
|
||||
sb.write_string('To: <$cfg.to>\r\n')
|
||||
sb.write_string('Cc: <$cfg.cc>\r\n')
|
||||
sb.write_string('Bcc: <$cfg.bcc>\r\n')
|
||||
sb.write_string('Date: $date\r\n')
|
||||
sb.write_string('Subject: $cfg.subject\r\n')
|
||||
if nonascii_subject {
|
||||
// handle UTF-8 subjects according RFC 1342
|
||||
sb.write_string('Subject: =?utf-8?B?' + base64.encode_str(cfg.subject) + '?=\r\n')
|
||||
} else {
|
||||
sb.write_string('Subject: $cfg.subject\r\n')
|
||||
}
|
||||
|
||||
if is_html {
|
||||
sb.write_string('Content-Type: text/html; charset=UTF-8')
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
module os
|
||||
|
||||
// Collection of useful functions for manipulation, validation and analysis of system paths.
|
||||
// The following functions handle paths depending on the operating system,
|
||||
// therefore results may be different for certain operating systems.
|
||||
|
||||
const (
|
||||
fslash = `/`
|
||||
bslash = `\\`
|
||||
dot = `.`
|
||||
)
|
||||
|
||||
// is_abs_path returns `true` if the given `path` is absolute.
|
||||
pub fn is_abs_path(path string) bool {
|
||||
if path.len == 0 {
|
||||
return false
|
||||
}
|
||||
$if windows {
|
||||
return is_device_path(path) || is_drive_rooted(path) || is_normal_path(path)
|
||||
}
|
||||
return path[0] == os.fslash
|
||||
}
|
||||
|
||||
// win_volume_len returns the length of the
|
||||
// Windows volume/drive from the given `path`.
|
||||
fn win_volume_len(path string) int {
|
||||
plen := path.len
|
||||
if plen < 2 {
|
||||
return 0
|
||||
}
|
||||
if has_drive_letter(path) {
|
||||
return 2
|
||||
}
|
||||
// its UNC path / DOS device path?
|
||||
if path.len >= 5 && starts_w_slash_slash(path) && !is_slash(path[2]) {
|
||||
for i := 3; i < plen; i++ {
|
||||
if is_slash(path[i]) {
|
||||
if i + 1 >= plen || is_slash(path[i + 1]) {
|
||||
break
|
||||
}
|
||||
i++
|
||||
for ; i < plen; i++ {
|
||||
if is_slash(path[i]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fn is_slash(b u8) bool {
|
||||
$if windows {
|
||||
return b == os.bslash || b == os.fslash
|
||||
}
|
||||
return b == os.fslash
|
||||
}
|
||||
|
||||
fn is_device_path(path string) bool {
|
||||
return win_volume_len(path) >= 5 && starts_w_slash_slash(path)
|
||||
}
|
||||
|
||||
fn has_drive_letter(path string) bool {
|
||||
return path.len >= 2 && path[0].is_letter() && path[1] == `:`
|
||||
}
|
||||
|
||||
fn starts_w_slash_slash(path string) bool {
|
||||
return path.len >= 2 && is_slash(path[0]) && is_slash(path[1])
|
||||
}
|
||||
|
||||
fn is_drive_rooted(path string) bool {
|
||||
return path.len >= 3 && has_drive_letter(path) && is_slash(path[2])
|
||||
}
|
||||
|
||||
// is_normal_path returns `true` if the given
|
||||
// `path` is NOT a network or Windows device path.
|
||||
fn is_normal_path(path string) bool {
|
||||
plen := path.len
|
||||
if plen == 0 {
|
||||
return false
|
||||
}
|
||||
return (plen == 1 && is_slash(path[0])) || (plen >= 2 && is_slash(path[0])
|
||||
&& !is_slash(path[1]))
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
module os
|
||||
|
||||
fn test_is_abs_path() {
|
||||
$if windows {
|
||||
assert is_abs_path('/')
|
||||
assert is_abs_path('\\')
|
||||
assert !is_abs_path('\\\\')
|
||||
assert is_abs_path(r'C:\path\to\files\file.v')
|
||||
assert is_abs_path(r'\\Host\share')
|
||||
assert is_abs_path(r'//Host\share\files\file.v')
|
||||
assert is_abs_path(r'\\.\BootPartition\Windows')
|
||||
assert !is_abs_path(r'\\.\')
|
||||
assert !is_abs_path(r'\\?\\')
|
||||
assert !is_abs_path(r'C:path\to\dir')
|
||||
assert !is_abs_path(r'dir')
|
||||
assert !is_abs_path(r'.\')
|
||||
assert !is_abs_path(r'.')
|
||||
assert !is_abs_path(r'\\Host')
|
||||
assert !is_abs_path(r'\\Host\')
|
||||
return
|
||||
}
|
||||
assert is_abs_path('/')
|
||||
assert is_abs_path('/path/to/files/file.v')
|
||||
assert !is_abs_path('\\')
|
||||
assert !is_abs_path('path/to/files/file.v')
|
||||
assert !is_abs_path('dir')
|
||||
assert !is_abs_path('./')
|
||||
assert !is_abs_path('.')
|
||||
}
|
12
vlib/os/os.v
12
vlib/os/os.v
|
@ -454,18 +454,6 @@ pub fn is_file(path string) bool {
|
|||
return exists(path) && !is_dir(path)
|
||||
}
|
||||
|
||||
// is_abs_path returns `true` if `path` is absolute.
|
||||
pub fn is_abs_path(path string) bool {
|
||||
if path.len == 0 {
|
||||
return false
|
||||
}
|
||||
$if windows {
|
||||
return path[0] == `/` || // incase we're in MingGW bash
|
||||
(path[0].is_letter() && path.len > 1 && path[1] == `:`)
|
||||
}
|
||||
return path[0] == `/`
|
||||
}
|
||||
|
||||
// join_path returns a path as string from input string parameter(s).
|
||||
[manualfree]
|
||||
pub fn join_path(base string, dirs ...string) string {
|
||||
|
|
|
@ -590,14 +590,6 @@ fn test_ext() {
|
|||
assert os.file_ext('file') == ''
|
||||
}
|
||||
|
||||
fn test_is_abs() {
|
||||
assert os.is_abs_path('/home/user')
|
||||
assert os.is_abs_path('v/vlib') == false
|
||||
$if windows {
|
||||
assert os.is_abs_path('C:\\Windows\\')
|
||||
}
|
||||
}
|
||||
|
||||
fn test_join() {
|
||||
$if windows {
|
||||
assert os.join_path('v', 'vlib', 'os') == 'v\\vlib\\os'
|
||||
|
|
|
@ -7,8 +7,17 @@ module sync
|
|||
// For that, please look at `channel_select_2_test.v` or `channel_select_3_test.v`
|
||||
// This test case uses the implementation in `sync/channels.v` directly
|
||||
// in order to test it independently from the support in the core language
|
||||
import os
|
||||
import time
|
||||
|
||||
fn test_should_run_flaky_test() {
|
||||
if os.getenv('VTEST_RUN_FLAKY') != '1' {
|
||||
eprintln('> skipping running flaky test, set VTEST_RUN_FLAKY to 1, to run it')
|
||||
exit(0)
|
||||
}
|
||||
assert true
|
||||
}
|
||||
|
||||
fn do_rec_i64(mut ch Channel) {
|
||||
mut sum := i64(0)
|
||||
for _ in 0 .. 300 {
|
||||
|
|
|
@ -184,3 +184,17 @@ fn test_parse_rfc3339() {
|
|||
assert expected == output
|
||||
}
|
||||
}
|
||||
|
||||
fn test_ad_second_to_parse_result_in_2001() ? {
|
||||
now_tm := time.parse('2001-01-01 04:00:00')?
|
||||
future_tm := now_tm.add_seconds(60)
|
||||
assert future_tm.str() == '2001-01-01 04:01:00'
|
||||
assert now_tm.unix < future_tm.unix
|
||||
}
|
||||
|
||||
fn test_ad_second_to_parse_result_pre_2001() ? {
|
||||
now_tm := time.parse('2000-01-01 04:00:00')?
|
||||
future_tm := now_tm.add_seconds(60)
|
||||
assert future_tm.str() == '2000-01-01 04:01:00'
|
||||
assert now_tm.unix < future_tm.unix
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@ pub const (
|
|||
seconds_per_hour = 60 * seconds_per_minute
|
||||
seconds_per_day = 24 * seconds_per_hour
|
||||
seconds_per_week = 7 * seconds_per_day
|
||||
days_per_400_years = 365 * 400 + 97
|
||||
days_per_100_years = 365 * 100 + 24
|
||||
days_per_4_years = 365 * 4 + 1
|
||||
days_per_400_years = days_in_year * 400 + 97
|
||||
days_per_100_years = days_in_year * 100 + 24
|
||||
days_per_4_years = days_in_year * 4 + 1
|
||||
days_in_year = 365
|
||||
days_before = [
|
||||
0,
|
||||
31,
|
||||
|
@ -179,13 +180,13 @@ pub fn (t Time) relative() string {
|
|||
}
|
||||
return '$prefix$d days$suffix'
|
||||
}
|
||||
if secs < time.seconds_per_hour * 24 * 365 {
|
||||
if secs < time.seconds_per_hour * 24 * time.days_in_year {
|
||||
if prefix == 'in ' {
|
||||
return 'on $t.md()'
|
||||
}
|
||||
return 'last $t.md()'
|
||||
}
|
||||
y := secs / time.seconds_per_hour / 24 / 365
|
||||
y := secs / time.seconds_per_hour / 24 / time.days_in_year
|
||||
if y == 1 {
|
||||
return '${prefix}1 year$suffix'
|
||||
}
|
||||
|
@ -234,14 +235,14 @@ pub fn (t Time) relative_short() string {
|
|||
}
|
||||
return '$prefix${h}h$suffix'
|
||||
}
|
||||
if secs < time.seconds_per_hour * 24 * 365 {
|
||||
if secs < time.seconds_per_hour * 24 * time.days_in_year {
|
||||
d := secs / time.seconds_per_hour / 24
|
||||
if d == 1 {
|
||||
return '${prefix}1d$suffix'
|
||||
}
|
||||
return '$prefix${d}d$suffix'
|
||||
}
|
||||
y := secs / time.seconds_per_hour / 24 / 365
|
||||
y := secs / time.seconds_per_hour / 24 / time.days_in_year
|
||||
if y == 1 {
|
||||
return '${prefix}1y$suffix'
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@ fn test_add_to_day_in_the_previous_century() ? {
|
|||
aa := a.add_days(180)
|
||||
dump(a.debug())
|
||||
dump(aa.debug())
|
||||
assert aa.ymmdd() == '1900-06-29'
|
||||
assert aa.ymmdd() == '1900-06-30'
|
||||
}
|
||||
|
||||
fn test_add_to_day_in_the_past() ? {
|
||||
a := time.parse_iso8601('1990-03-01')?
|
||||
aa := a.add_days(180)
|
||||
assert aa.ymmdd() == '1990-08-27'
|
||||
assert aa.ymmdd() == '1990-08-28'
|
||||
}
|
||||
|
||||
fn test_add_to_day_in_the_recent_past() ? {
|
||||
|
|
|
@ -267,3 +267,9 @@ fn test_recursive_local_call() {
|
|||
fn test_strftime() {
|
||||
assert '1980 July 11' == time_to_test.strftime('%Y %B %d')
|
||||
}
|
||||
|
||||
fn test_add_seconds_to_time() {
|
||||
now_tm := time.now()
|
||||
future_tm := now_tm.add_seconds(60)
|
||||
assert now_tm.unix < future_tm.unix
|
||||
}
|
||||
|
|
|
@ -48,67 +48,39 @@ pub fn unix2(abs i64, microsecond int) Time {
|
|||
|
||||
fn calculate_date_from_offset(day_offset_ i64) (int, int, int) {
|
||||
mut day_offset := day_offset_
|
||||
// Move offset to year 2001 as it's the start of a new 400-year cycle
|
||||
// Code below this rely on the fact that the day_offset is lined up with the 400-year cycle
|
||||
// 1970-2000 (inclusive) has 31 years (8 of which are leap years)
|
||||
mut year := 2001
|
||||
day_offset -= 31 * 365 + 8
|
||||
// Account for 400 year cycle
|
||||
year += int(day_offset / days_per_400_years) * 400
|
||||
day_offset %= days_per_400_years
|
||||
// Account for 100 year cycle
|
||||
if day_offset == days_per_100_years * 4 {
|
||||
year += 300
|
||||
day_offset -= days_per_100_years * 3
|
||||
|
||||
// source: http://howardhinnant.github.io/date_algorithms.html#civil_from_days
|
||||
|
||||
// shift from 1970-01-01 to 0000-03-01
|
||||
day_offset += 719468 // int(days_per_400_years * 1970 / 400 - (28+31))
|
||||
|
||||
mut era := 0
|
||||
if day_offset >= 0 {
|
||||
era = int(day_offset / days_per_400_years)
|
||||
} else {
|
||||
year += int(day_offset / days_per_100_years) * 100
|
||||
day_offset %= days_per_100_years
|
||||
era = int((day_offset - days_per_400_years - 1) / days_per_400_years)
|
||||
}
|
||||
// Account for 4 year cycle
|
||||
if day_offset == days_per_4_years * 25 {
|
||||
year += 96
|
||||
day_offset -= days_per_4_years * 24
|
||||
// doe(day of era) [0, 146096]
|
||||
doe := day_offset - era * days_per_400_years
|
||||
// yoe(year of era) [0, 399]
|
||||
yoe := (doe - doe / (days_per_4_years - 1) + doe / days_per_100_years - doe / (days_per_400_years - 1)) / days_in_year
|
||||
// year number
|
||||
mut y := int(yoe + era * 400)
|
||||
// doy (day of year), with year beginning Mar 1 [0, 365]
|
||||
doy := doe - (days_in_year * yoe + yoe / 4 - yoe / 100)
|
||||
|
||||
mp := (5 * doy + 2) / 153
|
||||
d := int(doy - (153 * mp + 2) / 5 + 1)
|
||||
mut m := int(mp)
|
||||
if mp < 10 {
|
||||
m += 3
|
||||
} else {
|
||||
year += int(day_offset / days_per_4_years) * 4
|
||||
day_offset %= days_per_4_years
|
||||
m -= 9
|
||||
}
|
||||
// Account for every year
|
||||
if day_offset == 365 * 4 {
|
||||
year += 3
|
||||
day_offset -= 365 * 3
|
||||
} else {
|
||||
year += int(day_offset / 365)
|
||||
day_offset %= 365
|
||||
if m <= 2 {
|
||||
y += 1
|
||||
}
|
||||
if day_offset < 0 {
|
||||
year--
|
||||
if is_leap_year(year) {
|
||||
day_offset += 366
|
||||
} else {
|
||||
day_offset += 365
|
||||
}
|
||||
}
|
||||
if is_leap_year(year) {
|
||||
if day_offset > 31 + 29 - 1 {
|
||||
// After leap day; pretend it wasn't there.
|
||||
day_offset--
|
||||
} else if day_offset == 31 + 29 - 1 {
|
||||
// Leap day.
|
||||
return year, 2, 29
|
||||
}
|
||||
}
|
||||
mut estimated_month := day_offset / 31
|
||||
for day_offset >= days_before[estimated_month + 1] {
|
||||
estimated_month++
|
||||
}
|
||||
for day_offset < days_before[estimated_month] {
|
||||
if estimated_month == 0 {
|
||||
break
|
||||
}
|
||||
estimated_month--
|
||||
}
|
||||
day_offset -= days_before[estimated_month]
|
||||
return year, int(estimated_month + 1), int(day_offset + 1)
|
||||
return y, m, d
|
||||
}
|
||||
|
||||
fn calculate_time_from_offset(second_offset_ i64) (int, int, int) {
|
||||
|
|
|
@ -256,13 +256,14 @@ pub:
|
|||
mut_pos token.Pos
|
||||
next_token token.Kind
|
||||
pub mut:
|
||||
expr Expr // expr.field_name
|
||||
expr_type Type // type of `Foo` in `Foo.bar`
|
||||
typ Type // type of the entire thing (`Foo.bar`)
|
||||
name_type Type // T in `T.name` or typeof in `typeof(expr).name`
|
||||
gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown
|
||||
scope &Scope
|
||||
from_embed_types []Type // holds the type of the embed that the method is called from
|
||||
expr Expr // expr.field_name
|
||||
expr_type Type // type of `Foo` in `Foo.bar`
|
||||
typ Type // type of the entire thing (`Foo.bar`)
|
||||
name_type Type // T in `T.name` or typeof in `typeof(expr).name`
|
||||
gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown
|
||||
scope &Scope
|
||||
from_embed_types []Type // holds the type of the embed that the method is called from
|
||||
has_hidden_receiver bool
|
||||
}
|
||||
|
||||
// root_ident returns the origin ident where the selector started.
|
||||
|
|
|
@ -1464,7 +1464,6 @@ pub fn (t &TypeSymbol) find_method_with_generic_parent(name string) ?Fn {
|
|||
param.typ = pt
|
||||
}
|
||||
}
|
||||
method.generic_names.clear()
|
||||
return method
|
||||
}
|
||||
else {}
|
||||
|
|
|
@ -927,9 +927,10 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if left_type.has_flag(.optional) && right_type.has_flag(.optional) {
|
||||
} else if left_type.has_flag(.optional) || right_type.has_flag(.optional) {
|
||||
opt_comp_pos := if left_type.has_flag(.optional) { left_pos } else { right_pos }
|
||||
c.error('unwrapped optional cannot be compared in an infix expression',
|
||||
left_right_pos)
|
||||
opt_comp_pos)
|
||||
}
|
||||
}
|
||||
.left_shift {
|
||||
|
@ -1143,8 +1144,11 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
|||
// TODO move this to symmetric_check? Right now it would break `return 0` for `fn()?int `
|
||||
left_is_optional := left_type.has_flag(.optional)
|
||||
right_is_optional := right_type.has_flag(.optional)
|
||||
if (left_is_optional && !right_is_optional) || (!left_is_optional && right_is_optional) {
|
||||
c.error('unwrapped optional cannot be used in an infix expression', left_right_pos)
|
||||
if left_is_optional && right_is_optional {
|
||||
c.error('unwrapped optionals cannot be used in an infix expression', left_right_pos)
|
||||
} else if left_is_optional || right_is_optional {
|
||||
opt_infix_pos := if left_is_optional { left_pos } else { right_pos }
|
||||
c.error('unwrapped optional cannot be used in an infix expression', opt_infix_pos)
|
||||
}
|
||||
// Dual sides check (compatibility check)
|
||||
if !(c.symmetric_check(left_type, right_type) && c.symmetric_check(right_type, left_type))
|
||||
|
@ -1846,6 +1850,37 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
|
|||
node.typ = field.typ
|
||||
return field.typ
|
||||
}
|
||||
if mut method := c.table.find_method(sym, field_name) {
|
||||
if c.expected_type != 0 && c.expected_type != ast.none_type {
|
||||
fn_type := ast.new_type(c.table.find_or_register_fn_type(c.mod, method, false,
|
||||
true))
|
||||
// if the expected type includes the receiver, don't hide it behind a closure
|
||||
if c.check_types(fn_type, c.expected_type) {
|
||||
return fn_type
|
||||
}
|
||||
}
|
||||
receiver := method.params[0].typ
|
||||
if receiver.nr_muls() > 0 {
|
||||
if !c.inside_unsafe {
|
||||
rec_sym := c.table.sym(receiver.set_nr_muls(0))
|
||||
if !rec_sym.is_heap() {
|
||||
suggestion := if rec_sym.kind == .struct_ {
|
||||
'declaring `$rec_sym.name` as `[heap]`'
|
||||
} else {
|
||||
'wrapping the `$rec_sym.name` object in a `struct` declared as `[heap]`'
|
||||
}
|
||||
c.error('method `${c.table.type_to_str(receiver.idx())}.$method.name` cannot be used as a variable outside `unsafe` blocks as its receiver might refer to an object stored on stack. Consider ${suggestion}.',
|
||||
node.expr.pos().extend(node.pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
method.params = method.params[1..]
|
||||
node.has_hidden_receiver = true
|
||||
method.name = ''
|
||||
fn_type := ast.new_type(c.table.find_or_register_fn_type(c.mod, method, false,
|
||||
true))
|
||||
return fn_type
|
||||
}
|
||||
if sym.kind !in [.struct_, .aggregate, .interface_, .sum_type] {
|
||||
if sym.kind != .placeholder {
|
||||
unwrapped_sym := c.table.sym(c.unwrap_generic(typ))
|
||||
|
@ -3371,7 +3406,21 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
|
|||
c.note('`[if $node.name]` is deprecated. Use `[if $node.name?]` instead',
|
||||
node.pos)
|
||||
} else {
|
||||
c.error('undefined ident: `$node.name`', node.pos)
|
||||
cname_mod := node.name.all_before('.')
|
||||
if cname_mod.len != node.name.len {
|
||||
mut const_names_in_mod := []string{}
|
||||
for _, so in c.table.global_scope.objects {
|
||||
if so is ast.ConstField {
|
||||
if so.mod == cname_mod {
|
||||
const_names_in_mod << so.name
|
||||
}
|
||||
}
|
||||
}
|
||||
c.error(util.new_suggestion(node.name, const_names_in_mod).say('undefined ident: `$node.name`'),
|
||||
node.pos)
|
||||
} else {
|
||||
c.error('undefined ident: `$node.name`', node.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.table.known_type(node.name) {
|
||||
|
@ -3533,6 +3582,7 @@ pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) ast.Type {
|
|||
}
|
||||
|
||||
pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) ast.Type {
|
||||
expected_type := c.expected_type
|
||||
if c.rlocked_names.len > 0 || c.locked_names.len > 0 {
|
||||
c.error('nested `lock`/`rlock` not allowed', node.pos)
|
||||
}
|
||||
|
@ -3556,16 +3606,17 @@ pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) ast.Type {
|
|||
}
|
||||
}
|
||||
c.stmts(node.stmts)
|
||||
c.rlocked_names = []
|
||||
c.locked_names = []
|
||||
// handle `x := rlock a { a.getval() }`
|
||||
mut ret_type := ast.void_type
|
||||
if node.stmts.len > 0 {
|
||||
last_stmt := node.stmts.last()
|
||||
if last_stmt is ast.ExprStmt {
|
||||
ret_type = last_stmt.typ
|
||||
c.expected_type = expected_type
|
||||
ret_type = c.expr(last_stmt.expr)
|
||||
}
|
||||
}
|
||||
c.rlocked_names = []
|
||||
c.locked_names = []
|
||||
if ret_type != ast.void_type {
|
||||
node.is_expr = true
|
||||
}
|
||||
|
|
|
@ -958,6 +958,9 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
|||
if param.typ == ast.voidptr_type_idx || arg_typ == ast.voidptr_type_idx {
|
||||
continue
|
||||
}
|
||||
if param.typ.is_any_kind_of_pointer() && arg_typ.is_any_kind_of_pointer() {
|
||||
continue
|
||||
}
|
||||
param_typ_sym_ := c.table.sym(c.table.unaliased_type(param.typ))
|
||||
arg_typ_sym_ := c.table.sym(c.table.unaliased_type(arg_typ))
|
||||
// Allow `[32]i8` as `&i8` etc
|
||||
|
@ -968,6 +971,17 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
|||
&& (typ_is_number || c.table.unaliased_type(arg_typ).is_any_kind_of_pointer())) {
|
||||
continue
|
||||
}
|
||||
// Allow `[N]anyptr` as `[N]anyptr`
|
||||
if arg_typ_sym_.kind == .array && param_typ_sym_.kind == .array {
|
||||
if (arg_typ_sym_.info as ast.Array).elem_type.is_any_kind_of_pointer()
|
||||
&& (param_typ_sym_.info as ast.Array).elem_type.is_any_kind_of_pointer() {
|
||||
continue
|
||||
}
|
||||
} else if arg_typ_sym_.kind == .array_fixed && param_typ_sym_.kind == .array_fixed {
|
||||
if (arg_typ_sym_.info as ast.ArrayFixed).elem_type.is_any_kind_of_pointer()&& (param_typ_sym_.info as ast.ArrayFixed).elem_type.is_any_kind_of_pointer() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Allow `int` as `&i8`
|
||||
if param.typ.is_any_kind_of_pointer() && typ_is_number {
|
||||
continue
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
vlib/v/checker/tests/infix_compare_optional_err.vv:6:5: error: unwrapped optional cannot be compared in an infix expression
|
||||
4 |
|
||||
4 |
|
||||
5 | fn main(){
|
||||
6 | if foo() > foo() {}
|
||||
| ~~~~~~~~~~~~~
|
||||
| ~~~~~
|
||||
7 | }
|
||||
|
|
|
@ -26,11 +26,11 @@ vlib/v/checker/tests/infix_err.vv:11:7: error: `+` cannot be used with `?int`
|
|||
| ^
|
||||
12 | _ = int(0) + g() // FIXME not detected
|
||||
13 | _ = g() + int(3)
|
||||
vlib/v/checker/tests/infix_err.vv:12:5: error: unwrapped optional cannot be used in an infix expression
|
||||
vlib/v/checker/tests/infix_err.vv:12:14: error: unwrapped optional cannot be used in an infix expression
|
||||
10 |
|
||||
11 | _ = 4 + g()
|
||||
12 | _ = int(0) + g() // FIXME not detected
|
||||
| ~~~~~~~~~~~~
|
||||
| ~~~
|
||||
13 | _ = g() + int(3)
|
||||
14 | _ = g() + 3
|
||||
vlib/v/checker/tests/infix_err.vv:13:9: error: `+` cannot be used with `?int`
|
||||
|
|
|
@ -5,6 +5,13 @@ vlib/v/checker/tests/lock_already_locked.vv:11:3: error: nested `lock`/`rlock` n
|
|||
| ~~~~~
|
||||
12 | a.x++
|
||||
13 | }
|
||||
vlib/v/checker/tests/lock_already_locked.vv:12:4: error: a has an `rlock` but needs a `lock`
|
||||
10 | lock a {
|
||||
11 | rlock a {
|
||||
12 | a.x++
|
||||
| ^
|
||||
13 | }
|
||||
14 | }
|
||||
vlib/v/checker/tests/lock_already_locked.vv:15:10: error: `a` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut argument to print
|
||||
13 | }
|
||||
14 | }
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/misspelled_mod_const_should_have_suggestion.vv:3:21: error: undefined ident: `time.secondz`.
|
||||
Did you mean `time.second`?
|
||||
1 | import time
|
||||
2 |
|
||||
3 | time.sleep(1 * time.secondz)
|
||||
| ~~~~~~~
|
|
@ -0,0 +1,3 @@
|
|||
import time
|
||||
|
||||
time.sleep(1 * time.secondz)
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/unsafe_method_as_field.vv:23:7: error: method `Foo.ref` cannot be used as a variable outside `unsafe` blocks as its receiver might refer to an object stored on stack. Consider declaring `Foo` as `[heap]`.
|
||||
21 | f := Foo{}
|
||||
22 | _ := f.no_ref // no error
|
||||
23 | _ := f.ref // error
|
||||
| ~~~~~
|
||||
24 |
|
||||
25 | b := Bar{}
|
|
@ -0,0 +1,27 @@
|
|||
struct Foo {
|
||||
}
|
||||
|
||||
fn (f Foo) no_ref() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
fn (f &Foo) ref() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
[heap]
|
||||
struct Bar {
|
||||
}
|
||||
|
||||
fn (f &Bar) ref() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
fn main() {
|
||||
f := Foo{}
|
||||
_ := f.no_ref // no error
|
||||
_ := f.ref // error
|
||||
|
||||
b := Bar{}
|
||||
_ := b.ref // no error
|
||||
}
|
|
@ -2,5 +2,4 @@ vlib/v/checker/tests/unwrapped_optional_infix.vv:5:9: error: unwrapped optional
|
|||
3 | }
|
||||
4 |
|
||||
5 | println(test() == "")
|
||||
| ~~~~~~~~~~~~
|
||||
|
||||
| ~~~~~~
|
||||
|
|
|
@ -1951,7 +1951,8 @@ pub fn (mut f Fmt) if_expr(node ast.IfExpr) {
|
|||
}
|
||||
|
||||
fn branch_is_single_line(b ast.IfBranch) bool {
|
||||
if b.stmts.len == 1 && b.comments.len == 0 && stmt_is_single_line(b.stmts[0]) {
|
||||
if b.stmts.len == 1 && b.comments.len == 0 && stmt_is_single_line(b.stmts[0])
|
||||
&& b.pos.line_nr == b.stmts[0].pos.line_nr {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
fn main() {
|
||||
a := fn () {
|
||||
if true {
|
||||
println('a')
|
||||
} else {
|
||||
println('a')
|
||||
}
|
||||
}
|
||||
a()
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
fn main() {
|
||||
a := fn () {
|
||||
if true {
|
||||
println('a')
|
||||
} else {
|
||||
println('a')
|
||||
}
|
||||
}
|
||||
a()
|
||||
}
|
|
@ -64,8 +64,7 @@ a: 5
|
|||
lock x { // wait for ongoing reads to finish, don't start new ones
|
||||
x.a = 17 // this value should never be read
|
||||
time.sleep(50* time.millisecond)
|
||||
x.a = if (i & 1) == 0 {
|
||||
7} else {5}
|
||||
x.a = if (i & 1) == 0 { 7 } else { 5 }
|
||||
} // now new reads are possible again
|
||||
time.sleep(20*time.millisecond)
|
||||
}
|
||||
|
|
|
@ -211,7 +211,11 @@ fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) {
|
|||
} else if g.inside_for_c_stmt {
|
||||
g.expr(val)
|
||||
} else {
|
||||
g.write('{$styp _ = ')
|
||||
if left_sym.kind == .function {
|
||||
g.write('{void* _ = ')
|
||||
} else {
|
||||
g.write('{$styp _ = ')
|
||||
}
|
||||
g.expr(val)
|
||||
g.writeln(';}')
|
||||
}
|
||||
|
@ -456,6 +460,8 @@ fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) {
|
|||
}
|
||||
if val is ast.ArrayInit {
|
||||
g.array_init(val, ident.name)
|
||||
} else if val_type.has_flag(.shared_f) {
|
||||
g.expr_with_cast(val, val_type, var_type)
|
||||
} else {
|
||||
g.expr(val)
|
||||
}
|
||||
|
|
|
@ -3339,6 +3339,68 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if m := g.table.find_method(sym, node.field_name) {
|
||||
mut has_embeds := false
|
||||
if sym.info in [ast.Struct, ast.Aggregate] {
|
||||
if node.from_embed_types.len > 0 {
|
||||
has_embeds = true
|
||||
}
|
||||
}
|
||||
if !has_embeds {
|
||||
if !node.has_hidden_receiver {
|
||||
g.write('${g.typ(node.expr_type.idx())}_$m.name')
|
||||
return
|
||||
}
|
||||
receiver := m.params[0]
|
||||
expr_styp := g.typ(node.expr_type.idx())
|
||||
data_styp := g.typ(receiver.typ.idx())
|
||||
mut sb := strings.new_builder(256)
|
||||
name := '_V_closure_${expr_styp}_${m.name}_$node.pos.pos'
|
||||
sb.write_string('${g.typ(m.return_type)} ${name}(')
|
||||
for i in 1 .. m.params.len {
|
||||
param := m.params[i]
|
||||
if i != 1 {
|
||||
sb.write_string(', ')
|
||||
}
|
||||
sb.write_string('${g.typ(param.typ)} a$i')
|
||||
}
|
||||
sb.writeln(') {')
|
||||
sb.writeln('\t$data_styp* a0 = *($data_styp**)(__RETURN_ADDRESS() - __CLOSURE_DATA_OFFSET);')
|
||||
if m.return_type != ast.void_type {
|
||||
sb.write_string('\treturn ')
|
||||
} else {
|
||||
sb.write_string('\t')
|
||||
}
|
||||
sb.write_string('${expr_styp}_${m.name}(')
|
||||
if !receiver.typ.is_ptr() {
|
||||
sb.write_string('*')
|
||||
}
|
||||
for i in 0 .. m.params.len {
|
||||
if i != 0 {
|
||||
sb.write_string(', ')
|
||||
}
|
||||
sb.write_string('a$i')
|
||||
}
|
||||
sb.writeln(');')
|
||||
sb.writeln('}')
|
||||
|
||||
g.anon_fn_definitions << sb.str()
|
||||
g.nr_closures++
|
||||
|
||||
g.write('__closure_create($name, ')
|
||||
if !receiver.typ.is_ptr() {
|
||||
g.write('memdup(')
|
||||
}
|
||||
if !node.expr_type.is_ptr() {
|
||||
g.write('&')
|
||||
}
|
||||
g.expr(node.expr)
|
||||
if !receiver.typ.is_ptr() {
|
||||
g.write(', sizeof($expr_styp))')
|
||||
}
|
||||
g.write(')')
|
||||
return
|
||||
}
|
||||
}
|
||||
n_ptr := node.expr_type.nr_muls() - 1
|
||||
if n_ptr > 0 {
|
||||
|
|
|
@ -1625,15 +1625,29 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
|
|||
arr_sym := g.table.sym(varg_type)
|
||||
mut arr_info := arr_sym.info as ast.Array
|
||||
if varg_type.has_flag(.generic) {
|
||||
if fn_def := g.table.find_fn(node.name) {
|
||||
mut muttable := unsafe { &ast.Table(g.table) }
|
||||
if utyp := muttable.resolve_generic_to_concrete(arr_info.elem_type, fn_def.generic_names,
|
||||
node.concrete_types)
|
||||
{
|
||||
arr_info.elem_type = utyp
|
||||
if node.is_method {
|
||||
left_sym := g.table.sym(node.left_type)
|
||||
if fn_def := left_sym.find_method_with_generic_parent(node.name) {
|
||||
mut muttable := unsafe { &ast.Table(g.table) }
|
||||
if utyp := muttable.resolve_generic_to_concrete(arr_info.elem_type,
|
||||
fn_def.generic_names, node.concrete_types)
|
||||
{
|
||||
arr_info.elem_type = utyp
|
||||
}
|
||||
} else {
|
||||
g.error('unable to find method $node.name', node.pos)
|
||||
}
|
||||
} else {
|
||||
g.error('unable to find function $node.name', node.pos)
|
||||
if fn_def := g.table.find_fn(node.name) {
|
||||
mut muttable := unsafe { &ast.Table(g.table) }
|
||||
if utyp := muttable.resolve_generic_to_concrete(arr_info.elem_type,
|
||||
fn_def.generic_names, node.concrete_types)
|
||||
{
|
||||
arr_info.elem_type = utyp
|
||||
}
|
||||
} else {
|
||||
g.error('unable to find function $node.name', node.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
elem_type := g.typ(arr_info.elem_type)
|
||||
|
|
|
@ -78,7 +78,8 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
|
|||
g.write(' ? ')
|
||||
}
|
||||
prev_expected_cast_type := g.expected_cast_type
|
||||
if node.is_expr && g.table.sym(node.typ).kind == .sum_type {
|
||||
if node.is_expr
|
||||
&& (g.table.sym(node.typ).kind == .sum_type || node.typ.has_flag(.shared_f)) {
|
||||
g.expected_cast_type = node.typ
|
||||
}
|
||||
g.stmts(branch.stmts)
|
||||
|
@ -204,7 +205,8 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
|
|||
}
|
||||
if needs_tmp_var {
|
||||
prev_expected_cast_type := g.expected_cast_type
|
||||
if node.is_expr && g.table.sym(node.typ).kind == .sum_type {
|
||||
if node.is_expr
|
||||
&& (g.table.sym(node.typ).kind == .sum_type || node.typ.has_flag(.shared_f)) {
|
||||
g.expected_cast_type = node.typ
|
||||
}
|
||||
g.stmts_with_tmp_var(branch.stmts, tmp)
|
||||
|
|
|
@ -172,8 +172,7 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
|
|||
}
|
||||
// `vals[i].field = x` is an exception and requires `array_get`:
|
||||
// `(*(Val*)array_get(vals, i)).field = x;`
|
||||
is_selector := node.left is ast.SelectorExpr
|
||||
if g.is_assign_lhs && !is_selector && node.is_setter {
|
||||
if g.is_assign_lhs && node.is_setter {
|
||||
is_direct_array_access := (g.fn_decl != 0 && g.fn_decl.is_direct_arr) || node.is_direct
|
||||
is_op_assign := g.assign_op != .assign && info.elem_type != ast.string_type
|
||||
if is_direct_array_access {
|
||||
|
|
|
@ -201,21 +201,25 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
|
|||
g.write(')')
|
||||
}
|
||||
.struct_ {
|
||||
ptr_typ := g.equality_fn(left.unaliased)
|
||||
if node.op == .ne {
|
||||
g.write('!')
|
||||
if g.pref.translated {
|
||||
g.gen_plain_infix_expr(node)
|
||||
} else {
|
||||
ptr_typ := g.equality_fn(left.unaliased)
|
||||
if node.op == .ne {
|
||||
g.write('!')
|
||||
}
|
||||
g.write('${ptr_typ}_struct_eq(')
|
||||
if left.typ.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
g.write(', ')
|
||||
if right.typ.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.right)
|
||||
g.write(')')
|
||||
}
|
||||
g.write('${ptr_typ}_struct_eq(')
|
||||
if left.typ.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
g.write(', ')
|
||||
if right.typ.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.right)
|
||||
g.write(')')
|
||||
}
|
||||
.sum_type {
|
||||
ptr_typ := g.equality_fn(left.unaliased)
|
||||
|
|
|
@ -46,18 +46,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
|
|||
g.write('($styp){')
|
||||
}
|
||||
}
|
||||
// mut fields := []string{}
|
||||
mut inited_fields := map[string]int{} // TODO this is done in checker, move to ast node
|
||||
/*
|
||||
if node.fields.len == 0 && node.exprs.len > 0 {
|
||||
// Get fields for {a,b} short syntax. Fields array wasn't set in the parser.
|
||||
for f in info.fields {
|
||||
fields << f.name
|
||||
}
|
||||
} else {
|
||||
fields = node.fields
|
||||
}
|
||||
*/
|
||||
mut inited_fields := map[string]int{}
|
||||
if is_multiline {
|
||||
g.indent++
|
||||
}
|
||||
|
@ -149,8 +138,6 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
|
|||
}
|
||||
g.is_shared = old_is_shared2
|
||||
}
|
||||
// g.zero_struct_fields(info, inited_fields)
|
||||
// nr_fields = info.fields.len
|
||||
for mut field in info.fields {
|
||||
if !field.typ.has_flag(.shared_f) {
|
||||
g.is_shared = false
|
||||
|
@ -183,6 +170,9 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
|
|||
}
|
||||
}
|
||||
if !cloned {
|
||||
inside_cast_in_heap := g.inside_cast_in_heap
|
||||
g.inside_cast_in_heap = 0 // prevent use of pointers in child structs
|
||||
|
||||
if field_type_sym.kind == .array_fixed && sfield.expr is ast.Ident {
|
||||
fixed_array_info := field_type_sym.info as ast.ArrayFixed
|
||||
g.write('{')
|
||||
|
@ -202,6 +192,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
|
|||
}
|
||||
g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type)
|
||||
}
|
||||
g.inside_cast_in_heap = inside_cast_in_heap // restore value for further struct inits
|
||||
}
|
||||
if is_multiline {
|
||||
g.writeln(',')
|
||||
|
|
|
@ -378,6 +378,11 @@ fn (mut w Walker) expr(node_ ast.Expr) {
|
|||
}
|
||||
ast.SelectorExpr {
|
||||
w.expr(node.expr)
|
||||
if node.expr_type != 0 {
|
||||
if method := w.table.find_method(w.table.sym(node.expr_type), node.field_name) {
|
||||
w.fn_by_name(method.fkey())
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.SqlExpr {
|
||||
w.expr(node.db_expr)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
vlib/v/parser/tests/dollar_err.vv:1:1: error: unexpected eof, expecting `if`
|
||||
vlib/v/parser/tests/dollar_err.vv:1:1: error: unexpected eof
|
||||
1 | $
|
||||
| ^
|
||||
| ^
|
||||
|
|
|
@ -287,13 +287,13 @@ fn vweb_tmpl_${fn_name}() string {
|
|||
}
|
||||
}
|
||||
.js {
|
||||
if line.contains('//V_TEMPLATE') {
|
||||
source.writeln(insert_template_code(fn_name, tmpl_str_start, line))
|
||||
} else {
|
||||
// replace `$` to `\$` at first to escape JavaScript template literal syntax
|
||||
source.writeln(line.replace(r'$', r'\$').replace(r'$$', r'@').replace(r'.$',
|
||||
r'.@').replace(r"'", r"\'"))
|
||||
}
|
||||
// if line.contains('//V_TEMPLATE') {
|
||||
source.writeln(insert_template_code(fn_name, tmpl_str_start, line))
|
||||
//} else {
|
||||
// replace `$` to `\$` at first to escape JavaScript template literal syntax
|
||||
// source.writeln(line.replace(r'$', r'\$').replace(r'$$', r'@').replace(r'.$',
|
||||
// r'.@').replace(r"'", r"\'"))
|
||||
//}
|
||||
continue
|
||||
}
|
||||
.css {
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
struct Vec {
|
||||
mut:
|
||||
x f64
|
||||
y f64
|
||||
z f64
|
||||
}
|
||||
|
||||
struct Plane {
|
||||
position Vec
|
||||
normal Vec
|
||||
}
|
||||
|
||||
struct Sphere {
|
||||
position Vec
|
||||
radius f64
|
||||
}
|
||||
|
||||
interface Object {
|
||||
position Vec
|
||||
}
|
||||
|
||||
fn test_append_struct_to_interface_array() {
|
||||
mut scene := []Object{}
|
||||
|
||||
scene << Plane{
|
||||
position: Vec{0, -10, 0}
|
||||
normal: Vec{0, -1, 0}
|
||||
}
|
||||
scene << Sphere{
|
||||
position: Vec{0, 0, -20}
|
||||
radius: 7
|
||||
}
|
||||
|
||||
println(scene)
|
||||
|
||||
assert scene.len == 2
|
||||
assert scene[0].position.x == 0
|
||||
assert scene[0].position.y == -10
|
||||
assert scene[0].position.z == 0
|
||||
assert scene[1].position.x == 0
|
||||
assert scene[1].position.y == 0
|
||||
assert scene[1].position.z == -20
|
||||
}
|
|
@ -60,7 +60,11 @@ fn main() {
|
|||
(aa % gcd) == big.zero_int,
|
||||
(bb % gcd) == big.zero_int,
|
||||
].all(it == true)
|
||||
{ true } else { false }
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
cfgs := [
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
struct Foo<T> {
|
||||
mut:
|
||||
arr []T
|
||||
}
|
||||
|
||||
fn (mut foo Foo<T>) push<T>(items ...T) {
|
||||
for item in items {
|
||||
foo.arr << item
|
||||
}
|
||||
}
|
||||
|
||||
fn test_generic_method_with_variadic_args() {
|
||||
mut f := Foo<int>{}
|
||||
f.push(1, 2, 3, 5, 8, 13, 21, 34, 55)
|
||||
println(f.arr)
|
||||
assert f.arr == [1, 2, 3, 5, 8, 13, 21, 34, 55]
|
||||
}
|
|
@ -9,6 +9,11 @@ import v.util.vtest
|
|||
|
||||
const turn_off_vcolors = os.setenv('VCOLORS', 'never', true)
|
||||
|
||||
const skip_files = [
|
||||
'do_not_remove_this',
|
||||
'tmpl_parse_html.vv', // skipped, due to a V template compilation problem after b42c824
|
||||
]
|
||||
|
||||
fn test_all() {
|
||||
mut total_errors := 0
|
||||
vexe := os.getenv('VEXE')
|
||||
|
@ -25,6 +30,11 @@ fn test_all() {
|
|||
paths := vtest.filter_vtest_only(tests, basepath: dir)
|
||||
for path in paths {
|
||||
print(path + ' ')
|
||||
fname := os.file_name(path)
|
||||
if fname in skip_files {
|
||||
println(term.bright_yellow('SKIP'))
|
||||
continue
|
||||
}
|
||||
program := path
|
||||
tname := rand.ulid()
|
||||
compilation := os.execute('${os.quoted_path(vexe)} -o $tname -cflags "-w" -cg ${os.quoted_path(program)}')
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
struct Foo {
|
||||
s string
|
||||
mut:
|
||||
i int
|
||||
}
|
||||
|
||||
fn (f Foo) get_s() string {
|
||||
return f.s
|
||||
}
|
||||
|
||||
fn (f &Foo) get_s_ref() string {
|
||||
return f.s
|
||||
}
|
||||
|
||||
fn (f Foo) add(a int) int {
|
||||
return a + f.i
|
||||
}
|
||||
|
||||
fn (f &Foo) add_ref(a int) int {
|
||||
return a + f.i
|
||||
}
|
||||
|
||||
fn (mut f Foo) set(a int) {
|
||||
f.i = a
|
||||
}
|
||||
|
||||
fn (f_ Foo) set_val(a int) int {
|
||||
mut f := unsafe { &f_ }
|
||||
old := f.i
|
||||
f.i = a
|
||||
return old
|
||||
}
|
||||
|
||||
fn test_methods_as_fields() {
|
||||
mut f := Foo{
|
||||
s: 'hello'
|
||||
i: 1
|
||||
}
|
||||
|
||||
get_s := f.get_s
|
||||
get_s_ref := unsafe { f.get_s_ref }
|
||||
add := f.add
|
||||
add_ref := unsafe { f.add_ref }
|
||||
set := unsafe { f.set }
|
||||
set_val := f.set_val
|
||||
|
||||
assert typeof(get_s).str() == 'fn () string'
|
||||
assert typeof(get_s_ref).str() == 'fn () string'
|
||||
assert typeof(add).str() == 'fn (int) int'
|
||||
assert typeof(add_ref).str() == 'fn (int) int'
|
||||
|
||||
assert get_s() == 'hello'
|
||||
assert get_s_ref() == 'hello'
|
||||
assert add(2) == 3
|
||||
assert add_ref(2) == 3
|
||||
|
||||
assert f.i == 1
|
||||
set(2)
|
||||
assert f.i == 2
|
||||
old := set_val(3)
|
||||
assert f.i == 2
|
||||
new := set_val(5)
|
||||
assert old == new && old == 1
|
||||
}
|
||||
|
||||
// the difference between these two tests is that here `f` is &Foo
|
||||
fn test_methods_as_fields_ref() {
|
||||
mut f := &Foo{
|
||||
s: 'hello'
|
||||
i: 1
|
||||
}
|
||||
|
||||
get_s := f.get_s
|
||||
get_s_ref := unsafe { f.get_s_ref }
|
||||
add := f.add
|
||||
add_ref := unsafe { f.add_ref }
|
||||
set := unsafe { f.set }
|
||||
set_val := f.set_val
|
||||
|
||||
assert typeof(get_s).str() == 'fn () string'
|
||||
assert typeof(get_s_ref).str() == 'fn () string'
|
||||
assert typeof(add).str() == 'fn (int) int'
|
||||
assert typeof(add_ref).str() == 'fn (int) int'
|
||||
|
||||
assert get_s() == 'hello'
|
||||
assert get_s_ref() == 'hello'
|
||||
assert add(2) == 3
|
||||
assert add_ref(2) == 3
|
||||
|
||||
assert f.i == 1
|
||||
set(2)
|
||||
assert f.i == 2
|
||||
old := set_val(3)
|
||||
assert f.i == 2
|
||||
new := set_val(5)
|
||||
assert old == new && old == 1
|
||||
}
|
||||
|
||||
struct GG_Ctx {
|
||||
frame_fn fn (voidptr) int
|
||||
}
|
||||
|
||||
[heap]
|
||||
struct App {
|
||||
msg string = 'hello'
|
||||
}
|
||||
|
||||
fn (app &App) frame() int {
|
||||
return app.msg.len
|
||||
}
|
||||
|
||||
fn test_ctx_arg_expected() {
|
||||
mut app := &App{}
|
||||
mut ctx := &GG_Ctx{
|
||||
frame_fn: app.frame
|
||||
}
|
||||
assert typeof(ctx.frame_fn).str() == 'fn (voidptr) int'
|
||||
assert ctx.frame_fn(app) == 5
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
type AA = bool | int
|
||||
|
||||
fn test_shared_if_expr() {
|
||||
shared a := [1, 2, 3]
|
||||
b := [4, 5, 6]
|
||||
c := lock a {
|
||||
if a == b { a } else { b }
|
||||
}
|
||||
assert c == [4, 5, 6]
|
||||
d := lock a {
|
||||
if a != b {
|
||||
a << 5
|
||||
a
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
assert d == [1, 2, 3, 5]
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
struct Foo {
|
||||
x int
|
||||
}
|
||||
|
||||
fn (f Foo) hi() int {
|
||||
return f.x
|
||||
}
|
||||
|
||||
fn main() {
|
||||
f := Foo{123}
|
||||
_ = f.hi
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
struct App {
|
||||
mut:
|
||||
buffer []string
|
||||
}
|
||||
|
||||
fn test_struct_field_array_index() {
|
||||
mut app := &App{
|
||||
buffer: []string{len: 2}
|
||||
}
|
||||
|
||||
app.buffer[0] += 'hello'
|
||||
app.buffer[1] += 'world'
|
||||
|
||||
println(app)
|
||||
|
||||
assert app.buffer == ['hello', 'world']
|
||||
}
|
|
@ -20,8 +20,8 @@ fn test_template_interpolation_can_be_selectively_turned_on_in_script_tags() {
|
|||
text := $tmpl('tmpl/selective_interpolation_in_script_tag.html')
|
||||
dump(text)
|
||||
assert text.contains('Username: abcd')
|
||||
assert text.contains('var non_interpolated_labels = @benchmark_plot_data.dates;')
|
||||
assert text.contains('var non_interpolated_values = @benchmark_plot_data.numerical_result;')
|
||||
assert text.contains("var non_interpolated_labels = ['2012-11-30', '2022-12-29'];")
|
||||
assert text.contains('var non_interpolated_values = [5, 6, 7, 1];')
|
||||
assert text.contains("var real_labels = ['2012-11-30', '2022-12-29']; //V_TEMPLATE")
|
||||
assert text.contains('var real_values = [5, 6, 7, 1]; //V_TEMPLATE')
|
||||
assert text.contains('Year: 2022')
|
||||
|
|
Loading…
Reference in New Issue