From 2b1e4d8cf9946dc682f4d78070708ddee07e8345 Mon Sep 17 00:00:00 2001 From: Matteo Pologruto Date: Wed, 26 Oct 2022 15:47:23 +0200 Subject: [PATCH 1/2] Run integration tests concurrently This enhancement reduces the amount of time needed to execute integration tests, since each test package no longer has to wait for the previous ones to be completed. In order to do this, a regex is used to match each test file. It is also specified the path to the directory that contains the file. --- .github/workflows/test-go-task.yml | 52 ++++++++++++++++++++++++++++-- Taskfile.yml | 24 ++++++++++++-- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-go-task.yml b/.github/workflows/test-go-task.yml index f841f9d376c..84f839a0130 100644 --- a/.github/workflows/test-go-task.yml +++ b/.github/workflows/test-go-task.yml @@ -58,13 +58,61 @@ jobs: echo "result=$RESULT" >> $GITHUB_OUTPUT - test: + tests-collector: + runs-on: ubuntu-latest needs: run-determination if: needs.run-determination.outputs.result == 'true' + outputs: + tests-data: ${{ steps.collection.outputs.tests-data }} + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Collect tests + id: collection + run: | + echo "tests-data=$(go list ./internal/integrationtest/... | grep integrationtest/ | tr "/" " " | cut -d " " -f 6 | jq -cR '[inputs]')" >> $GITHUB_OUTPUT + test-integration: + needs: tests-collector strategy: - fail-fast: false + matrix: + operating-system: + - ubuntu-latest + - windows-latest + - macos-latest + tests: ${{ fromJSON(needs.tests-collector.outputs.tests-data) }} + + runs-on: ${{ matrix.operating-system }} + + steps: + # By default, actions/checkout converts the repo's LF line endings to CRLF on the Windows runner. + - name: Disable EOL conversions + run: git config --global core.autocrlf false + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Install Task + uses: arduino/setup-task@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + version: 3.x + + - name: Run tests + shell: bash + run: | + export GO_TEST_PACKAGE="github.com/arduino/arduino-cli/internal/integrationtest/${{ matrix.tests }}" + task go:integration-test + + test: + needs: run-determination + strategy: matrix: operating-system: - ubuntu-latest diff --git a/Taskfile.yml b/Taskfile.yml index 86ca98cd289..a793036b136 100755 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -105,6 +105,23 @@ tasks: {{default .DEFAULT_GO_PACKAGES .GO_PACKAGES}} \ {{.TEST_LDFLAGS}} + go:integration-test: + desc: Run the Go-based integration tests + deps: + - task: go:build + dir: '{{default "./" .GO_MODULE_PATH}}' + cmds: + - | + go test \ + -v \ + -short \ + {{ .GO_TEST_PACKAGE }} \ + -run '{{default ".*" .GO_TEST_REGEX}}' \ + {{default "-timeout 20m -coverpkg=./... -covermode=atomic" .GO_TEST_FLAGS}} \ + -coverprofile=coverage_unit.txt \ + {{default .DEFAULT_INTEGRATIONTEST_GO_PACKAGES .GO_PACKAGES}} \ + {{.TEST_LDFLAGS}} + # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/test-go-integration-task/Taskfile.yml go:test-integration: desc: Run integration tests @@ -342,10 +359,13 @@ tasks: vars: PROJECT_NAME: "arduino-cli" DIST_DIR: "dist" - # all modules of this project except for "legacy/..." module + # all modules of this project except for "legacy/..." module and integration test DEFAULT_GO_PACKAGES: sh: | - echo $(cd {{default "./" .GO_MODULE_PATH}} && go list ./... | grep -v legacy | tr '\n' ' ' || echo '"ERROR: Unable to discover Go packages"') + echo $(cd {{default "./" .GO_MODULE_PATH}} && go list ./... | grep -v internal/integrationtest | grep -v legacy | tr '\n' ' ' || echo '"ERROR: Unable to discover Go packages"') + DEFAULT_INTEGRATIONTEST_GO_PACKAGES: + sh: | + echo $(cd {{default "./" .GO_MODULE_PATH}} && go list ./... | grep internal/integrationtest | tr '\n' ' ' || echo '"ERROR: Unable to discover Go packages"') # build vars COMMIT: sh: echo "$(git log --no-show-signature -n 1 --format=%h)" From 1cb4633db13898283c5902eb828567cd258a830a Mon Sep 17 00:00:00 2001 From: Matteo Pologruto Date: Thu, 27 Oct 2022 14:02:18 +0200 Subject: [PATCH 2/2] Merge all test files that share a package into one Having different test files that shared the same package was inefficient, because the whole package test was run one time for each file. This enhancement avoids repeating the same tests more than once. --- .../integrationtest/board/board_list_test.go | 63 ---- internal/integrationtest/board/board_test.go | 39 ++ .../compile/compile_part_2_test.go | 162 --------- .../compile/compile_part_3_test.go | 133 ------- ...compile_part_1_test.go => compile_test.go} | 246 +++++++++++++ .../integrationtest/core/core_list_test.go | 40 -- internal/integrationtest/core/core_test.go | 15 + .../daemon/daemon_board_watch_test.go | 82 ----- .../daemon/daemon_compile_test.go | 127 ------- .../daemon/daemon_core_test.go | 78 ---- .../daemon/daemon_lib_install_test.go | 156 -------- .../integrationtest/daemon/daemon_test.go | 343 ++++++++++++++++++ 12 files changed, 643 insertions(+), 841 deletions(-) delete mode 100644 internal/integrationtest/board/board_list_test.go delete mode 100644 internal/integrationtest/compile/compile_part_2_test.go delete mode 100644 internal/integrationtest/compile/compile_part_3_test.go rename internal/integrationtest/compile/{compile_part_1_test.go => compile_test.go} (75%) delete mode 100644 internal/integrationtest/core/core_list_test.go delete mode 100644 internal/integrationtest/daemon/daemon_board_watch_test.go delete mode 100644 internal/integrationtest/daemon/daemon_compile_test.go delete mode 100644 internal/integrationtest/daemon/daemon_core_test.go delete mode 100644 internal/integrationtest/daemon/daemon_lib_install_test.go diff --git a/internal/integrationtest/board/board_list_test.go b/internal/integrationtest/board/board_list_test.go deleted file mode 100644 index 12b60497171..00000000000 --- a/internal/integrationtest/board/board_list_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2022 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to -// modify or otherwise use the software for commercial activities involving the -// Arduino software without disclosing the source code of your own applications. -// To purchase a commercial license, send an email to license@arduino.cc. - -package board_test - -import ( - "testing" - - "github.com/arduino/arduino-cli/internal/integrationtest" - "github.com/stretchr/testify/require" - "go.bug.st/testifyjson/requirejson" -) - -func TestCorrectBoardListOrdering(t *testing.T) { - env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) - defer env.CleanUp() - - _, _, err := cli.Run("core", "install", "arduino:avr") - require.NoError(t, err) - jsonOut, _, err := cli.Run("board", "listall", "--format", "json") - require.NoError(t, err) - requirejson.Query(t, jsonOut, "[.boards[] | .fqbn]", `[ - "arduino:avr:yun", - "arduino:avr:uno", - "arduino:avr:unomini", - "arduino:avr:diecimila", - "arduino:avr:nano", - "arduino:avr:mega", - "arduino:avr:megaADK", - "arduino:avr:leonardo", - "arduino:avr:leonardoeth", - "arduino:avr:micro", - "arduino:avr:esplora", - "arduino:avr:mini", - "arduino:avr:ethernet", - "arduino:avr:fio", - "arduino:avr:bt", - "arduino:avr:LilyPadUSB", - "arduino:avr:lilypad", - "arduino:avr:pro", - "arduino:avr:atmegang", - "arduino:avr:robotControl", - "arduino:avr:robotMotor", - "arduino:avr:gemma", - "arduino:avr:circuitplay32u4cat", - "arduino:avr:yunmini", - "arduino:avr:chiwawa", - "arduino:avr:one", - "arduino:avr:unowifi" - ]`) -} diff --git a/internal/integrationtest/board/board_test.go b/internal/integrationtest/board/board_test.go index 41777710a13..c243abd7843 100644 --- a/internal/integrationtest/board/board_test.go +++ b/internal/integrationtest/board/board_test.go @@ -29,6 +29,45 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing" ) +func TestCorrectBoardListOrdering(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + _, _, err := cli.Run("core", "install", "arduino:avr") + require.NoError(t, err) + jsonOut, _, err := cli.Run("board", "listall", "--format", "json") + require.NoError(t, err) + requirejson.Query(t, jsonOut, "[.boards[] | .fqbn]", `[ + "arduino:avr:yun", + "arduino:avr:uno", + "arduino:avr:unomini", + "arduino:avr:diecimila", + "arduino:avr:nano", + "arduino:avr:mega", + "arduino:avr:megaADK", + "arduino:avr:leonardo", + "arduino:avr:leonardoeth", + "arduino:avr:micro", + "arduino:avr:esplora", + "arduino:avr:mini", + "arduino:avr:ethernet", + "arduino:avr:fio", + "arduino:avr:bt", + "arduino:avr:LilyPadUSB", + "arduino:avr:lilypad", + "arduino:avr:pro", + "arduino:avr:atmegang", + "arduino:avr:robotControl", + "arduino:avr:robotMotor", + "arduino:avr:gemma", + "arduino:avr:circuitplay32u4cat", + "arduino:avr:yunmini", + "arduino:avr:chiwawa", + "arduino:avr:one", + "arduino:avr:unowifi" + ]`) +} + func TestBoardList(t *testing.T) { if os.Getenv("CI") != "" { t.Skip("VMs have no serial ports") diff --git a/internal/integrationtest/compile/compile_part_2_test.go b/internal/integrationtest/compile/compile_part_2_test.go deleted file mode 100644 index f3a057097c5..00000000000 --- a/internal/integrationtest/compile/compile_part_2_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2022 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to -// modify or otherwise use the software for commercial activities involving the -// Arduino software without disclosing the source code of your own applications. -// To purchase a commercial license, send an email to license@arduino.cc. - -package compile_test - -import ( - "encoding/json" - "testing" - - "github.com/arduino/arduino-cli/internal/integrationtest" - "github.com/arduino/go-paths-helper" - "github.com/stretchr/testify/require" -) - -func TestCompileWithoutPrecompiledLibraries(t *testing.T) { - env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) - defer env.CleanUp() - - // Init the environment explicitly - url := "https://adafruit.github.io/arduino-board-index/package_adafruit_index.json" - _, _, err := cli.Run("core", "update-index", "--additional-urls="+url) - require.NoError(t, err) - _, _, err = cli.Run("core", "install", "arduino:mbed@1.3.1", "--additional-urls="+url) - require.NoError(t, err) - - // // Precompiled version of Arduino_TensorflowLite - // _, _, err = cli.Run("lib", "install", "Arduino_LSM9DS1") - // require.NoError(t, err) - // _, _, err = cli.Run("lib", "install", "Arduino_TensorflowLite@2.1.1-ALPHA-precompiled") - // require.NoError(t, err) - - // sketchPath := cli.SketchbookDir().Join("libraries", "Arduino_TensorFlowLite", "examples", "hello_world") - // _, _, err = cli.Run("compile", "-b", "arduino:mbed:nano33ble", sketchPath.String()) - // require.NoError(t, err) - - _, _, err = cli.Run("core", "install", "arduino:samd@1.8.7", "--additional-urls="+url) - require.NoError(t, err) - // _, _, err = cli.Run("core", "install", "adafruit:samd@1.6.4", "--additional-urls="+url) - // require.NoError(t, err) - // // should work on adafruit too after https://github.com/arduino/arduino-cli/pull/1134 - // _, _, err = cli.Run("compile", "-b", "adafruit:samd:adafruit_feather_m4", sketchPath.String()) - // require.NoError(t, err) - - // // Non-precompiled version of Arduino_TensorflowLite - // _, _, err = cli.Run("lib", "install", "Arduino_TensorflowLite@2.1.0-ALPHA") - // require.NoError(t, err) - // _, _, err = cli.Run("compile", "-b", "arduino:mbed:nano33ble", sketchPath.String()) - // require.NoError(t, err) - // _, _, err = cli.Run("compile", "-b", "adafruit:samd:adafruit_feather_m4", sketchPath.String()) - // require.NoError(t, err) - - // Bosch sensor library - _, _, err = cli.Run("lib", "install", "BSEC Software Library@1.5.1474") - require.NoError(t, err) - sketchPath := cli.SketchbookDir().Join("libraries", "BSEC_Software_Library", "examples", "basic") - _, _, err = cli.Run("compile", "-b", "arduino:samd:mkr1000", sketchPath.String()) - require.NoError(t, err) - _, _, err = cli.Run("compile", "-b", "arduino:mbed:nano33ble", sketchPath.String()) - require.NoError(t, err) - - // USBBlaster library - _, _, err = cli.Run("lib", "install", "USBBlaster@1.0.0") - require.NoError(t, err) - sketchPath = cli.SketchbookDir().Join("libraries", "USBBlaster", "examples", "USB_Blaster") - _, _, err = cli.Run("compile", "-b", "arduino:samd:mkrvidor4000", sketchPath.String()) - require.NoError(t, err) -} - -func TestCompileWithCustomLibraries(t *testing.T) { - env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) - defer env.CleanUp() - - // Creates config with additional URL to install necessary core - url := "http://arduino.esp8266.com/stable/package_esp8266com_index.json" - _, _, err := cli.Run("config", "init", "--dest-dir", ".", "--additional-urls", url) - require.NoError(t, err) - - // Init the environment explicitly - _, _, err = cli.Run("update") - require.NoError(t, err) - - _, _, err = cli.Run("core", "install", "esp8266:esp8266") - require.NoError(t, err) - - sketchName := "sketch_with_multiple_custom_libraries" - sketchPath := cli.CopySketch(sketchName) - fqbn := "esp8266:esp8266:nodemcu:xtal=80,vt=heap,eesz=4M1M,wipe=none,baud=115200" - - firstLib := sketchPath.Join("libraries1") - secondLib := sketchPath.Join("libraries2") - _, _, err = cli.Run("compile", "--libraries", firstLib.String(), "--libraries", secondLib.String(), "-b", fqbn, sketchPath.String()) - require.NoError(t, err) -} - -func TestCompileWithArchivesAndLongPaths(t *testing.T) { - env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) - defer env.CleanUp() - - // Creates config with additional URL to install necessary core - url := "http://arduino.esp8266.com/stable/package_esp8266com_index.json" - _, _, err := cli.Run("config", "init", "--dest-dir", ".", "--additional-urls", url) - require.NoError(t, err) - - // Init the environment explicitly - _, _, err = cli.Run("update") - require.NoError(t, err) - - // Install core to compile - _, _, err = cli.Run("core", "install", "esp8266:esp8266@2.7.4") - require.NoError(t, err) - - // Install test library - _, _, err = cli.Run("lib", "install", "ArduinoIoTCloud") - require.NoError(t, err) - - stdout, _, err := cli.Run("lib", "examples", "ArduinoIoTCloud", "--format", "json") - require.NoError(t, err) - var libOutput []map[string]interface{} - err = json.Unmarshal(stdout, &libOutput) - require.NoError(t, err) - sketchPath := paths.New(libOutput[0]["library"].(map[string]interface{})["install_dir"].(string)) - sketchPath = sketchPath.Join("examples", "ArduinoIoTCloud-Advanced") - - _, _, err = cli.Run("compile", "-b", "esp8266:esp8266:huzzah", sketchPath.String()) - require.NoError(t, err) -} - -func TestCompileWithPrecompileLibrary(t *testing.T) { - env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) - defer env.CleanUp() - - _, _, err := cli.Run("update") - require.NoError(t, err) - - _, _, err = cli.Run("core", "install", "arduino:samd@1.8.11") - require.NoError(t, err) - fqbn := "arduino:samd:mkrzero" - - // Install precompiled library - // For more information see: - // https://arduino.github.io/arduino-cli/latest/library-specification/#precompiled-binaries - _, _, err = cli.Run("lib", "install", "BSEC Software Library@1.5.1474") - require.NoError(t, err) - sketchFolder := cli.SketchbookDir().Join("libraries", "BSEC_Software_Library", "examples", "basic") - - // Compile and verify dependencies detection for fully precompiled library is not skipped - stdout, _, err := cli.Run("compile", "-b", fqbn, sketchFolder.String(), "-v") - require.NoError(t, err) - require.NotContains(t, string(stdout), "Skipping dependencies detection for precompiled library BSEC Software Library") -} diff --git a/internal/integrationtest/compile/compile_part_3_test.go b/internal/integrationtest/compile/compile_part_3_test.go deleted file mode 100644 index c95d645cfc6..00000000000 --- a/internal/integrationtest/compile/compile_part_3_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2022 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to -// modify or otherwise use the software for commercial activities involving the -// Arduino software without disclosing the source code of your own applications. -// To purchase a commercial license, send an email to license@arduino.cc. - -package compile_test - -import ( - "testing" - - "github.com/arduino/arduino-cli/internal/integrationtest" - "github.com/arduino/go-paths-helper" - "github.com/stretchr/testify/require" - "gopkg.in/src-d/go-git.v4" - "gopkg.in/src-d/go-git.v4/plumbing" -) - -func TestCompileWithFullyPrecompiledLibrary(t *testing.T) { - env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) - defer env.CleanUp() - - _, _, err := cli.Run("update") - require.NoError(t, err) - - _, _, err = cli.Run("core", "install", "arduino:mbed@1.3.1") - require.NoError(t, err) - fqbn := "arduino:mbed:nano33ble" - - // Create settings with library unsafe install set to true - envVar := cli.GetDefaultEnv() - envVar["ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL"] = "true" - _, _, err = cli.RunWithCustomEnv(envVar, "config", "init", "--dest-dir", ".") - require.NoError(t, err) - - // Install fully precompiled library - // For more information see: - // https://arduino.github.io/arduino-cli/latest/library-specification/#precompiled-binaries - wd, err := paths.Getwd() - require.NoError(t, err) - _, _, err = cli.Run("lib", "install", "--zip-path", wd.Parent().Join("testdata", "Arduino_TensorFlowLite-2.1.0-ALPHA-precompiled.zip").String()) - require.NoError(t, err) - sketchFolder := cli.SketchbookDir().Join("libraries", "Arduino_TensorFlowLite-2.1.0-ALPHA-precompiled", "examples", "hello_world") - - // Install example dependency - _, _, err = cli.Run("lib", "install", "Arduino_LSM9DS1") - require.NoError(t, err) - - // Compile and verify dependencies detection for fully precompiled library is skipped - stdout, _, err := cli.Run("compile", "-b", fqbn, sketchFolder.String(), "-v") - require.NoError(t, err) - require.Contains(t, string(stdout), "Skipping dependencies detection for precompiled library Arduino_TensorFlowLite") -} - -func TestCompileManuallyInstalledPlatform(t *testing.T) { - env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) - defer env.CleanUp() - - _, _, err := cli.Run("update") - require.NoError(t, err) - - sketchName := "CompileSketchManuallyInstalledPlatformUsingPlatformLocalTxt" - sketchPath := cli.SketchbookDir().Join(sketchName) - fqbn := "arduino-beta-development:avr:uno" - _, _, err = cli.Run("sketch", "new", sketchPath.String()) - require.NoError(t, err) - - // Manually installs a core in sketchbooks hardware folder - gitUrl := "https://github.com/arduino/ArduinoCore-avr.git" - repoDir := cli.SketchbookDir().Join("hardware", "arduino-beta-development", "avr") - _, err = git.PlainClone(repoDir.String(), false, &git.CloneOptions{ - URL: gitUrl, - ReferenceName: plumbing.NewTagReferenceName("1.8.3"), - }) - require.NoError(t, err) - - // Installs also the same core via CLI so all the necessary tools are installed - _, _, err = cli.Run("core", "install", "arduino:avr@1.8.3") - require.NoError(t, err) - - // Verifies compilation works without issues - _, _, err = cli.Run("compile", "--clean", "-b", fqbn, sketchPath.String()) - require.NoError(t, err) -} - -func TestCompileManuallyInstalledPlatformUsingPlatformLocalTxt(t *testing.T) { - env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) - defer env.CleanUp() - - _, _, err := cli.Run("update") - require.NoError(t, err) - - sketchName := "CompileSketchManuallyInstalledPlatformUsingPlatformLocalTxt" - sketchPath := cli.SketchbookDir().Join(sketchName) - fqbn := "arduino-beta-development:avr:uno" - _, _, err = cli.Run("sketch", "new", sketchPath.String()) - require.NoError(t, err) - - // Manually installs a core in sketchbooks hardware folder - gitUrl := "https://github.com/arduino/ArduinoCore-avr.git" - repoDir := cli.SketchbookDir().Join("hardware", "arduino-beta-development", "avr") - _, err = git.PlainClone(repoDir.String(), false, &git.CloneOptions{ - URL: gitUrl, - ReferenceName: plumbing.NewTagReferenceName("1.8.3"), - }) - require.NoError(t, err) - - // Installs also the same core via CLI so all the necessary tools are installed - _, _, err = cli.Run("core", "install", "arduino:avr@1.8.3") - require.NoError(t, err) - - // Verifies compilation works without issues - _, _, err = cli.Run("compile", "--clean", "-b", fqbn, sketchPath.String()) - require.NoError(t, err) - - // Overrides default platform compiler with an unexisting one - platformLocalTxt := repoDir.Join("platform.local.txt") - platformLocalTxt.WriteFile([]byte("compiler.c.cmd=my-compiler-that-does-not-exist")) - - // Verifies compilation now fails because compiler is not found - _, stderr, err := cli.Run("compile", "--clean", "-b", fqbn, sketchPath.String()) - require.Error(t, err) - require.Contains(t, string(stderr), "my-compiler-that-does-not-exist") -} diff --git a/internal/integrationtest/compile/compile_part_1_test.go b/internal/integrationtest/compile/compile_test.go similarity index 75% rename from internal/integrationtest/compile/compile_part_1_test.go rename to internal/integrationtest/compile/compile_test.go index ad2d8a8adb4..a349ec6b78c 100644 --- a/internal/integrationtest/compile/compile_part_1_test.go +++ b/internal/integrationtest/compile/compile_test.go @@ -27,6 +27,8 @@ import ( "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" "go.bug.st/testifyjson/requirejson" + "gopkg.in/src-d/go-git.v4" + "gopkg.in/src-d/go-git.v4/plumbing" ) func TestCompile(t *testing.T) { @@ -711,3 +713,247 @@ func compileUsingBoardsLocalTxt(t *testing.T, env *integrationtest.Environment, _, _, err = cli.Run("compile", "--clean", "-b", fqbn, sketchPath.String()) require.NoError(t, err) } + +func TestCompileWithoutPrecompiledLibraries(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + // Init the environment explicitly + url := "https://adafruit.github.io/arduino-board-index/package_adafruit_index.json" + _, _, err := cli.Run("core", "update-index", "--additional-urls="+url) + require.NoError(t, err) + _, _, err = cli.Run("core", "install", "arduino:mbed@1.3.1", "--additional-urls="+url) + require.NoError(t, err) + + // // Precompiled version of Arduino_TensorflowLite + // _, _, err = cli.Run("lib", "install", "Arduino_LSM9DS1") + // require.NoError(t, err) + // _, _, err = cli.Run("lib", "install", "Arduino_TensorflowLite@2.1.1-ALPHA-precompiled") + // require.NoError(t, err) + + // sketchPath := cli.SketchbookDir().Join("libraries", "Arduino_TensorFlowLite", "examples", "hello_world") + // _, _, err = cli.Run("compile", "-b", "arduino:mbed:nano33ble", sketchPath.String()) + // require.NoError(t, err) + + _, _, err = cli.Run("core", "install", "arduino:samd@1.8.7", "--additional-urls="+url) + require.NoError(t, err) + // _, _, err = cli.Run("core", "install", "adafruit:samd@1.6.4", "--additional-urls="+url) + // require.NoError(t, err) + // // should work on adafruit too after https://github.com/arduino/arduino-cli/pull/1134 + // _, _, err = cli.Run("compile", "-b", "adafruit:samd:adafruit_feather_m4", sketchPath.String()) + // require.NoError(t, err) + + // // Non-precompiled version of Arduino_TensorflowLite + // _, _, err = cli.Run("lib", "install", "Arduino_TensorflowLite@2.1.0-ALPHA") + // require.NoError(t, err) + // _, _, err = cli.Run("compile", "-b", "arduino:mbed:nano33ble", sketchPath.String()) + // require.NoError(t, err) + // _, _, err = cli.Run("compile", "-b", "adafruit:samd:adafruit_feather_m4", sketchPath.String()) + // require.NoError(t, err) + + // Bosch sensor library + _, _, err = cli.Run("lib", "install", "BSEC Software Library@1.5.1474") + require.NoError(t, err) + sketchPath := cli.SketchbookDir().Join("libraries", "BSEC_Software_Library", "examples", "basic") + _, _, err = cli.Run("compile", "-b", "arduino:samd:mkr1000", sketchPath.String()) + require.NoError(t, err) + _, _, err = cli.Run("compile", "-b", "arduino:mbed:nano33ble", sketchPath.String()) + require.NoError(t, err) + + // USBBlaster library + _, _, err = cli.Run("lib", "install", "USBBlaster@1.0.0") + require.NoError(t, err) + sketchPath = cli.SketchbookDir().Join("libraries", "USBBlaster", "examples", "USB_Blaster") + _, _, err = cli.Run("compile", "-b", "arduino:samd:mkrvidor4000", sketchPath.String()) + require.NoError(t, err) +} + +func TestCompileWithCustomLibraries(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + // Creates config with additional URL to install necessary core + url := "http://arduino.esp8266.com/stable/package_esp8266com_index.json" + _, _, err := cli.Run("config", "init", "--dest-dir", ".", "--additional-urls", url) + require.NoError(t, err) + + // Init the environment explicitly + _, _, err = cli.Run("update") + require.NoError(t, err) + + _, _, err = cli.Run("core", "install", "esp8266:esp8266") + require.NoError(t, err) + + sketchName := "sketch_with_multiple_custom_libraries" + sketchPath := cli.CopySketch(sketchName) + fqbn := "esp8266:esp8266:nodemcu:xtal=80,vt=heap,eesz=4M1M,wipe=none,baud=115200" + + firstLib := sketchPath.Join("libraries1") + secondLib := sketchPath.Join("libraries2") + _, _, err = cli.Run("compile", "--libraries", firstLib.String(), "--libraries", secondLib.String(), "-b", fqbn, sketchPath.String()) + require.NoError(t, err) +} + +func TestCompileWithArchivesAndLongPaths(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + // Creates config with additional URL to install necessary core + url := "http://arduino.esp8266.com/stable/package_esp8266com_index.json" + _, _, err := cli.Run("config", "init", "--dest-dir", ".", "--additional-urls", url) + require.NoError(t, err) + + // Init the environment explicitly + _, _, err = cli.Run("update") + require.NoError(t, err) + + // Install core to compile + _, _, err = cli.Run("core", "install", "esp8266:esp8266@2.7.4") + require.NoError(t, err) + + // Install test library + _, _, err = cli.Run("lib", "install", "ArduinoIoTCloud") + require.NoError(t, err) + + stdout, _, err := cli.Run("lib", "examples", "ArduinoIoTCloud", "--format", "json") + require.NoError(t, err) + var libOutput []map[string]interface{} + err = json.Unmarshal(stdout, &libOutput) + require.NoError(t, err) + sketchPath := paths.New(libOutput[0]["library"].(map[string]interface{})["install_dir"].(string)) + sketchPath = sketchPath.Join("examples", "ArduinoIoTCloud-Advanced") + + _, _, err = cli.Run("compile", "-b", "esp8266:esp8266:huzzah", sketchPath.String()) + require.NoError(t, err) +} + +func TestCompileWithPrecompileLibrary(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + _, _, err := cli.Run("update") + require.NoError(t, err) + + _, _, err = cli.Run("core", "install", "arduino:samd@1.8.11") + require.NoError(t, err) + fqbn := "arduino:samd:mkrzero" + + // Install precompiled library + // For more information see: + // https://arduino.github.io/arduino-cli/latest/library-specification/#precompiled-binaries + _, _, err = cli.Run("lib", "install", "BSEC Software Library@1.5.1474") + require.NoError(t, err) + sketchFolder := cli.SketchbookDir().Join("libraries", "BSEC_Software_Library", "examples", "basic") + + // Compile and verify dependencies detection for fully precompiled library is not skipped + stdout, _, err := cli.Run("compile", "-b", fqbn, sketchFolder.String(), "-v") + require.NoError(t, err) + require.NotContains(t, string(stdout), "Skipping dependencies detection for precompiled library BSEC Software Library") +} + +func TestCompileWithFullyPrecompiledLibrary(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + _, _, err := cli.Run("update") + require.NoError(t, err) + + _, _, err = cli.Run("core", "install", "arduino:mbed@1.3.1") + require.NoError(t, err) + fqbn := "arduino:mbed:nano33ble" + + // Create settings with library unsafe install set to true + envVar := cli.GetDefaultEnv() + envVar["ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL"] = "true" + _, _, err = cli.RunWithCustomEnv(envVar, "config", "init", "--dest-dir", ".") + require.NoError(t, err) + + // Install fully precompiled library + // For more information see: + // https://arduino.github.io/arduino-cli/latest/library-specification/#precompiled-binaries + wd, err := paths.Getwd() + require.NoError(t, err) + _, _, err = cli.Run("lib", "install", "--zip-path", wd.Parent().Join("testdata", "Arduino_TensorFlowLite-2.1.0-ALPHA-precompiled.zip").String()) + require.NoError(t, err) + sketchFolder := cli.SketchbookDir().Join("libraries", "Arduino_TensorFlowLite-2.1.0-ALPHA-precompiled", "examples", "hello_world") + + // Install example dependency + _, _, err = cli.Run("lib", "install", "Arduino_LSM9DS1") + require.NoError(t, err) + + // Compile and verify dependencies detection for fully precompiled library is skipped + stdout, _, err := cli.Run("compile", "-b", fqbn, sketchFolder.String(), "-v") + require.NoError(t, err) + require.Contains(t, string(stdout), "Skipping dependencies detection for precompiled library Arduino_TensorFlowLite") +} + +func TestCompileManuallyInstalledPlatform(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + _, _, err := cli.Run("update") + require.NoError(t, err) + + sketchName := "CompileSketchManuallyInstalledPlatformUsingPlatformLocalTxt" + sketchPath := cli.SketchbookDir().Join(sketchName) + fqbn := "arduino-beta-development:avr:uno" + _, _, err = cli.Run("sketch", "new", sketchPath.String()) + require.NoError(t, err) + + // Manually installs a core in sketchbooks hardware folder + gitUrl := "https://github.com/arduino/ArduinoCore-avr.git" + repoDir := cli.SketchbookDir().Join("hardware", "arduino-beta-development", "avr") + _, err = git.PlainClone(repoDir.String(), false, &git.CloneOptions{ + URL: gitUrl, + ReferenceName: plumbing.NewTagReferenceName("1.8.3"), + }) + require.NoError(t, err) + + // Installs also the same core via CLI so all the necessary tools are installed + _, _, err = cli.Run("core", "install", "arduino:avr@1.8.3") + require.NoError(t, err) + + // Verifies compilation works without issues + _, _, err = cli.Run("compile", "--clean", "-b", fqbn, sketchPath.String()) + require.NoError(t, err) +} + +func TestCompileManuallyInstalledPlatformUsingPlatformLocalTxt(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + _, _, err := cli.Run("update") + require.NoError(t, err) + + sketchName := "CompileSketchManuallyInstalledPlatformUsingPlatformLocalTxt" + sketchPath := cli.SketchbookDir().Join(sketchName) + fqbn := "arduino-beta-development:avr:uno" + _, _, err = cli.Run("sketch", "new", sketchPath.String()) + require.NoError(t, err) + + // Manually installs a core in sketchbooks hardware folder + gitUrl := "https://github.com/arduino/ArduinoCore-avr.git" + repoDir := cli.SketchbookDir().Join("hardware", "arduino-beta-development", "avr") + _, err = git.PlainClone(repoDir.String(), false, &git.CloneOptions{ + URL: gitUrl, + ReferenceName: plumbing.NewTagReferenceName("1.8.3"), + }) + require.NoError(t, err) + + // Installs also the same core via CLI so all the necessary tools are installed + _, _, err = cli.Run("core", "install", "arduino:avr@1.8.3") + require.NoError(t, err) + + // Verifies compilation works without issues + _, _, err = cli.Run("compile", "--clean", "-b", fqbn, sketchPath.String()) + require.NoError(t, err) + + // Overrides default platform compiler with an unexisting one + platformLocalTxt := repoDir.Join("platform.local.txt") + platformLocalTxt.WriteFile([]byte("compiler.c.cmd=my-compiler-that-does-not-exist")) + + // Verifies compilation now fails because compiler is not found + _, stderr, err := cli.Run("compile", "--clean", "-b", fqbn, sketchPath.String()) + require.Error(t, err) + require.Contains(t, string(stderr), "my-compiler-that-does-not-exist") +} diff --git a/internal/integrationtest/core/core_list_test.go b/internal/integrationtest/core/core_list_test.go deleted file mode 100644 index 516d9d2133b..00000000000 --- a/internal/integrationtest/core/core_list_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2022 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to -// modify or otherwise use the software for commercial activities involving the -// Arduino software without disclosing the source code of your own applications. -// To purchase a commercial license, send an email to license@arduino.cc. - -package core_test - -import ( - "testing" - - "github.com/arduino/arduino-cli/internal/integrationtest" - "github.com/arduino/go-paths-helper" - "github.com/stretchr/testify/require" - "go.bug.st/testifyjson/requirejson" -) - -func TestCorrectHandlingOfPlatformVersionProperty(t *testing.T) { - // See: https://github.com/arduino/arduino-cli/issues/1823 - env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) - defer env.CleanUp() - - // Copy test platform - testPlatform := paths.New("testdata", "issue_1823", "DxCore-dev") - require.NoError(t, testPlatform.CopyDirTo(cli.SketchbookDir().Join("hardware", "DxCore-dev"))) - - // Trigger problematic call - out, _, err := cli.Run("core", "list", "--format", "json") - require.NoError(t, err) - requirejson.Contains(t, out, `[{"id":"DxCore-dev:megaavr","installed":"1.4.10","name":"DxCore"}]`) -} diff --git a/internal/integrationtest/core/core_test.go b/internal/integrationtest/core/core_test.go index c110a125760..81ecdb9f5f2 100644 --- a/internal/integrationtest/core/core_test.go +++ b/internal/integrationtest/core/core_test.go @@ -26,6 +26,21 @@ import ( "go.bug.st/testifyjson/requirejson" ) +func TestCorrectHandlingOfPlatformVersionProperty(t *testing.T) { + // See: https://github.com/arduino/arduino-cli/issues/1823 + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + // Copy test platform + testPlatform := paths.New("testdata", "issue_1823", "DxCore-dev") + require.NoError(t, testPlatform.CopyDirTo(cli.SketchbookDir().Join("hardware", "DxCore-dev"))) + + // Trigger problematic call + out, _, err := cli.Run("core", "list", "--format", "json") + require.NoError(t, err) + requirejson.Contains(t, out, `[{"id":"DxCore-dev:megaavr","installed":"1.4.10","name":"DxCore"}]`) +} + func TestCoreSearch(t *testing.T) { env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) defer env.CleanUp() diff --git a/internal/integrationtest/daemon/daemon_board_watch_test.go b/internal/integrationtest/daemon/daemon_board_watch_test.go deleted file mode 100644 index a526427b45d..00000000000 --- a/internal/integrationtest/daemon/daemon_board_watch_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2022 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to -// modify or otherwise use the software for commercial activities involving the -// Arduino software without disclosing the source code of your own applications. -// To purchase a commercial license, send an email to license@arduino.cc. - -package daemon_test - -import ( - "context" - "fmt" - "io" - "testing" - "time" - - "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "github.com/stretchr/testify/require" -) - -func TestArduinoCliDaemon(t *testing.T) { - // See: https://github.com/arduino/arduino-cli/pull/1804 - - t.SkipNow() // TO BE Removed once the bug is fixed - - env, cli := createEnvForDaemon(t) - defer env.CleanUp() - - grpcInst := cli.Create() - require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { - fmt.Printf("INIT> %v\n", ir.GetMessage()) - })) - - // Run a one-shot board list - boardListResp, err := grpcInst.BoardList(time.Second) - require.NoError(t, err) - fmt.Printf("Got boardlist response with %d ports\n", len(boardListResp.GetPorts())) - - // Run a one-shot board list again (should not fail) - boardListResp, err = grpcInst.BoardList(time.Second) - require.NoError(t, err) - fmt.Printf("Got boardlist response with %d ports\n", len(boardListResp.GetPorts())) - - testWatcher := func() { - // Run watcher - watcher, err := grpcInst.BoardListWatch() - require.NoError(t, err) - ctx, cancel := context.WithCancel(context.Background()) - go func() { - defer cancel() - for { - msg, err := watcher.Recv() - if err == io.EOF { - fmt.Println("Watcher EOF") - return - } - require.Empty(t, msg.Error, "Board list watcher returned an error") - require.NoError(t, err, "BoardListWatch grpc call returned an error") - fmt.Printf("WATCH> %v\n", msg) - } - }() - time.Sleep(time.Second) - require.NoError(t, watcher.CloseSend()) - select { - case <-ctx.Done(): - // all right! - case <-time.After(time.Second): - require.Fail(t, "BoardListWatch didn't close") - } - } - - testWatcher() - testWatcher() -} diff --git a/internal/integrationtest/daemon/daemon_compile_test.go b/internal/integrationtest/daemon/daemon_compile_test.go deleted file mode 100644 index a26f4d14e1d..00000000000 --- a/internal/integrationtest/daemon/daemon_compile_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2022 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to -// modify or otherwise use the software for commercial activities involving the -// Arduino software without disclosing the source code of your own applications. -// To purchase a commercial license, send an email to license@arduino.cc. - -package daemon_test - -import ( - "context" - "fmt" - "io" - "testing" - - "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "github.com/arduino/go-paths-helper" - "github.com/stretchr/testify/require" -) - -func TestDaemonCompileOptions(t *testing.T) { - // See: https://github.com/arduino/arduino-cli/issues/1614 - // See: https://github.com/arduino/arduino-cli/pull/1820 - - env, cli := createEnvForDaemon(t) - defer env.CleanUp() - - grpcInst := cli.Create() - require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { - fmt.Printf("INIT> %v\n", ir.GetMessage()) - })) - - plInst, err := grpcInst.PlatformInstall(context.Background(), "arduino", "avr", "1.8.5", true) - require.NoError(t, err) - for { - msg, err := plInst.Recv() - if err == io.EOF { - break - } - require.NoError(t, err) - fmt.Printf("INSTALL> %v\n", msg) - } - - // Install boards.local.txt to trigger bug - platformLocalTxt := paths.New("testdata", "boards.local.txt-issue1614") - err = platformLocalTxt.CopyTo(cli.DataDir(). - Join("packages", "arduino", "hardware", "avr", "1.8.5", "boards.local.txt")) - require.NoError(t, err) - - // Re-init instance to update changes - require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { - fmt.Printf("INIT> %v\n", ir.GetMessage()) - })) - - // Build sketch (with errors) - sk := paths.New("testdata", "bare_minimum") - compile, err := grpcInst.Compile(context.Background(), "arduino:avr:uno:some_menu=bad", sk.String()) - require.NoError(t, err) - for { - msg, err := compile.Recv() - if err == io.EOF { - require.FailNow(t, "Expected compilation failure", "compilation succeeded") - break - } - if err != nil { - fmt.Println("COMPILE ERROR>", err) - break - } - if msg.ErrStream != nil { - fmt.Printf("COMPILE> %v\n", string(msg.GetErrStream())) - } - } - - // Build sketch (without errors) - compile, err = grpcInst.Compile(context.Background(), "arduino:avr:uno:some_menu=good", sk.String()) - require.NoError(t, err) - for { - msg, err := compile.Recv() - if err == io.EOF { - break - } - require.NoError(t, err) - if msg.ErrStream != nil { - fmt.Printf("COMPILE> %v\n", string(msg.GetErrStream())) - } - } -} - -func TestDaemonCompileAfterFailedLibInstall(t *testing.T) { - // See: https://github.com/arduino/arduino-cli/issues/1812 - - env, cli := createEnvForDaemon(t) - defer env.CleanUp() - - grpcInst := cli.Create() - require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { - fmt.Printf("INIT> %v\n", ir.GetMessage()) - })) - - // Build sketch (with errors) - sk := paths.New("testdata", "bare_minimum") - compile, err := grpcInst.Compile(context.Background(), "", sk.String()) - require.NoError(t, err) - for { - msg, err := compile.Recv() - if err == io.EOF { - require.FailNow(t, "Expected compilation failure", "compilation succeeded") - break - } - if err != nil { - fmt.Println("COMPILE ERROR>", err) - require.Contains(t, err.Error(), "Missing FQBN") - break - } - if msg.ErrStream != nil { - fmt.Printf("COMPILE> %v\n", string(msg.GetErrStream())) - } - } -} diff --git a/internal/integrationtest/daemon/daemon_core_test.go b/internal/integrationtest/daemon/daemon_core_test.go deleted file mode 100644 index b0ddb3435ac..00000000000 --- a/internal/integrationtest/daemon/daemon_core_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2022 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to -// modify or otherwise use the software for commercial activities involving the -// Arduino software without disclosing the source code of your own applications. -// To purchase a commercial license, send an email to license@arduino.cc. - -package daemon_test - -import ( - "context" - "fmt" - "io" - "testing" - - "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "github.com/stretchr/testify/require" -) - -func TestDaemonCoreUpdateIndex(t *testing.T) { - env, cli := createEnvForDaemon(t) - defer env.CleanUp() - - grpcInst := cli.Create() - require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { - fmt.Printf("INIT> %v\n", ir.GetMessage()) - })) - - // Set extra indexes - err := cli.SetValue( - "board_manager.additional_urls", ""+ - `["http://arduino.esp8266.com/stable/package_esp8266com_index.json",`+ - ` "http://downloads.arduino.cc/package_inexistent_index.json"]`) - require.NoError(t, err) - - analyzeUpdateIndexClient := func(cl commands.ArduinoCoreService_UpdateIndexClient) (error, map[string]*commands.DownloadProgressEnd) { - analyzer := NewDownloadProgressAnalyzer(t) - for { - msg, err := cl.Recv() - // fmt.Println("DOWNLOAD>", msg) - if err == io.EOF { - return nil, analyzer.Results - } - if err != nil { - return err, analyzer.Results - } - require.NoError(t, err) - analyzer.Process(msg.GetDownloadProgress()) - } - } - - { - cl, err := grpcInst.UpdateIndex(context.Background(), true) - require.NoError(t, err) - err, res := analyzeUpdateIndexClient(cl) - require.NoError(t, err) - require.Len(t, res, 1) - require.True(t, res["https://downloads.arduino.cc/packages/package_index.tar.bz2"].Success) - } - { - cl, err := grpcInst.UpdateIndex(context.Background(), false) - require.NoError(t, err) - err, res := analyzeUpdateIndexClient(cl) - require.Error(t, err) - require.Len(t, res, 3) - require.True(t, res["https://downloads.arduino.cc/packages/package_index.tar.bz2"].Success) - require.True(t, res["http://arduino.esp8266.com/stable/package_esp8266com_index.json"].Success) - require.False(t, res["http://downloads.arduino.cc/package_inexistent_index.json"].Success) - } -} diff --git a/internal/integrationtest/daemon/daemon_lib_install_test.go b/internal/integrationtest/daemon/daemon_lib_install_test.go deleted file mode 100644 index b4ff76f2218..00000000000 --- a/internal/integrationtest/daemon/daemon_lib_install_test.go +++ /dev/null @@ -1,156 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2022 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to -// modify or otherwise use the software for commercial activities involving the -// Arduino software without disclosing the source code of your own applications. -// To purchase a commercial license, send an email to license@arduino.cc. - -package daemon_test - -import ( - "context" - "fmt" - "io" - "testing" - - "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "github.com/stretchr/testify/require" -) - -func TestDaemonBundleLibInstall(t *testing.T) { - env, cli := createEnvForDaemon(t) - defer env.CleanUp() - - grpcInst := cli.Create() - require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { - fmt.Printf("INIT> %v\n", ir.GetMessage()) - })) - - // Install libraries in bundled dir - { - instCl, err := grpcInst.LibraryInstall(context.Background(), "Arduino_BuiltIn", "", false, false, true) - require.NoError(t, err) - for { - msg, err := instCl.Recv() - if err == io.EOF { - break - } - require.NoError(t, err) - fmt.Printf("LIB INSTALL> %+v\n", msg) - } - } - - // Check if libraries are installed as expected - { - resp, err := grpcInst.LibraryList(context.Background(), "", "", true, false) - require.NoError(t, err) - libsAndLocation := map[string]commands.LibraryLocation{} - for _, lib := range resp.GetInstalledLibraries() { - libsAndLocation[lib.Library.Name] = lib.Library.Location - } - require.Contains(t, libsAndLocation, "Ethernet") - require.Contains(t, libsAndLocation, "SD") - require.Contains(t, libsAndLocation, "Firmata") - require.Equal(t, libsAndLocation["Ethernet"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) - require.Equal(t, libsAndLocation["SD"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) - require.Equal(t, libsAndLocation["Firmata"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) - } - - // Install a library in sketchbook to override bundled - { - instCl, err := grpcInst.LibraryInstall(context.Background(), "Ethernet", "", false, false, false) - require.NoError(t, err) - for { - msg, err := instCl.Recv() - if err == io.EOF { - break - } - require.NoError(t, err) - fmt.Printf("LIB INSTALL> %+v\n", msg) - } - } - - // Check if libraries are installed as expected - installedEthernetVersion := "" - { - resp, err := grpcInst.LibraryList(context.Background(), "", "", true, false) - require.NoError(t, err) - libsAndLocation := map[string]commands.LibraryLocation{} - for _, lib := range resp.GetInstalledLibraries() { - libsAndLocation[lib.Library.Name] = lib.Library.Location - if lib.Library.Name == "Ethernet" { - installedEthernetVersion = lib.Library.Version - } - } - require.Contains(t, libsAndLocation, "Ethernet") - require.Contains(t, libsAndLocation, "SD") - require.Contains(t, libsAndLocation, "Firmata") - require.Equal(t, libsAndLocation["Ethernet"], commands.LibraryLocation_LIBRARY_LOCATION_USER) - require.Equal(t, libsAndLocation["SD"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) - require.Equal(t, libsAndLocation["Firmata"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) - } - - // Remove library from sketchbook - { - uninstCl, err := grpcInst.LibraryUninstall(context.Background(), "Ethernet", installedEthernetVersion) - require.NoError(t, err) - for { - msg, err := uninstCl.Recv() - if err == io.EOF { - break - } - require.NoError(t, err) - fmt.Printf("LIB INSTALL> %+v\n", msg) - } - } - - // Check if libraries are installed as expected - { - resp, err := grpcInst.LibraryList(context.Background(), "", "", true, false) - require.NoError(t, err) - libsAndLocation := map[string]commands.LibraryLocation{} - for _, lib := range resp.GetInstalledLibraries() { - libsAndLocation[lib.Library.Name] = lib.Library.Location - } - require.Contains(t, libsAndLocation, "Ethernet") - require.Contains(t, libsAndLocation, "SD") - require.Contains(t, libsAndLocation, "Firmata") - require.Equal(t, libsAndLocation["Ethernet"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) - require.Equal(t, libsAndLocation["SD"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) - require.Equal(t, libsAndLocation["Firmata"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) - } - - // Un-Set builtin libraries dir - err := cli.SetValue("directories.builtin.libraries", `""`) - require.NoError(t, err) - - // Re-init - require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { - fmt.Printf("INIT> %v\n", ir.GetMessage()) - })) - - // Install libraries in bundled dir (should now fail) - { - instCl, err := grpcInst.LibraryInstall(context.Background(), "Arduino_BuiltIn", "", false, false, true) - require.NoError(t, err) - for { - msg, err := instCl.Recv() - if err == io.EOF { - require.FailNow(t, "LibraryInstall is supposed to fail because builtin libraries directory is not set") - } - if err != nil { - fmt.Println("LIB INSTALL ERROR:", err) - break - } - fmt.Printf("LIB INSTALL> %+v\n", msg) - } - } -} diff --git a/internal/integrationtest/daemon/daemon_test.go b/internal/integrationtest/daemon/daemon_test.go index b868483155f..8685a7c0750 100644 --- a/internal/integrationtest/daemon/daemon_test.go +++ b/internal/integrationtest/daemon/daemon_test.go @@ -16,12 +16,73 @@ package daemon_test import ( + "context" + "fmt" + "io" "testing" + "time" "github.com/arduino/arduino-cli/internal/integrationtest" + "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" ) +func TestArduinoCliDaemon(t *testing.T) { + // See: https://github.com/arduino/arduino-cli/pull/1804 + + t.SkipNow() // TO BE Removed once the bug is fixed + + env, cli := createEnvForDaemon(t) + defer env.CleanUp() + + grpcInst := cli.Create() + require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { + fmt.Printf("INIT> %v\n", ir.GetMessage()) + })) + + // Run a one-shot board list + boardListResp, err := grpcInst.BoardList(time.Second) + require.NoError(t, err) + fmt.Printf("Got boardlist response with %d ports\n", len(boardListResp.GetPorts())) + + // Run a one-shot board list again (should not fail) + boardListResp, err = grpcInst.BoardList(time.Second) + require.NoError(t, err) + fmt.Printf("Got boardlist response with %d ports\n", len(boardListResp.GetPorts())) + + testWatcher := func() { + // Run watcher + watcher, err := grpcInst.BoardListWatch() + require.NoError(t, err) + ctx, cancel := context.WithCancel(context.Background()) + go func() { + defer cancel() + for { + msg, err := watcher.Recv() + if err == io.EOF { + fmt.Println("Watcher EOF") + return + } + require.Empty(t, msg.Error, "Board list watcher returned an error") + require.NoError(t, err, "BoardListWatch grpc call returned an error") + fmt.Printf("WATCH> %v\n", msg) + } + }() + time.Sleep(time.Second) + require.NoError(t, watcher.CloseSend()) + select { + case <-ctx.Done(): + // all right! + case <-time.After(time.Second): + require.Fail(t, "BoardListWatch didn't close") + } + } + + testWatcher() + testWatcher() +} + // createEnvForDaemon performs the minimum required operations to start the arduino-cli daemon. // It returns a testsuite.Environment and an ArduinoCLI client to perform the integration tests. // The Environment must be disposed by calling the CleanUp method via defer. @@ -39,3 +100,285 @@ func createEnvForDaemon(t *testing.T) (*integrationtest.Environment, *integratio _ = cli.StartDaemon(false) return env, cli } + +func TestDaemonCompileOptions(t *testing.T) { + // See: https://github.com/arduino/arduino-cli/issues/1614 + // See: https://github.com/arduino/arduino-cli/pull/1820 + + env, cli := createEnvForDaemon(t) + defer env.CleanUp() + + grpcInst := cli.Create() + require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { + fmt.Printf("INIT> %v\n", ir.GetMessage()) + })) + + plInst, err := grpcInst.PlatformInstall(context.Background(), "arduino", "avr", "1.8.5", true) + require.NoError(t, err) + for { + msg, err := plInst.Recv() + if err == io.EOF { + break + } + require.NoError(t, err) + fmt.Printf("INSTALL> %v\n", msg) + } + + // Install boards.local.txt to trigger bug + platformLocalTxt := paths.New("testdata", "boards.local.txt-issue1614") + err = platformLocalTxt.CopyTo(cli.DataDir(). + Join("packages", "arduino", "hardware", "avr", "1.8.5", "boards.local.txt")) + require.NoError(t, err) + + // Re-init instance to update changes + require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { + fmt.Printf("INIT> %v\n", ir.GetMessage()) + })) + + // Build sketch (with errors) + sk := paths.New("testdata", "bare_minimum") + compile, err := grpcInst.Compile(context.Background(), "arduino:avr:uno:some_menu=bad", sk.String()) + require.NoError(t, err) + for { + msg, err := compile.Recv() + if err == io.EOF { + require.FailNow(t, "Expected compilation failure", "compilation succeeded") + break + } + if err != nil { + fmt.Println("COMPILE ERROR>", err) + break + } + if msg.ErrStream != nil { + fmt.Printf("COMPILE> %v\n", string(msg.GetErrStream())) + } + } + + // Build sketch (without errors) + compile, err = grpcInst.Compile(context.Background(), "arduino:avr:uno:some_menu=good", sk.String()) + require.NoError(t, err) + for { + msg, err := compile.Recv() + if err == io.EOF { + break + } + require.NoError(t, err) + if msg.ErrStream != nil { + fmt.Printf("COMPILE> %v\n", string(msg.GetErrStream())) + } + } +} + +func TestDaemonCompileAfterFailedLibInstall(t *testing.T) { + // See: https://github.com/arduino/arduino-cli/issues/1812 + + env, cli := createEnvForDaemon(t) + defer env.CleanUp() + + grpcInst := cli.Create() + require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { + fmt.Printf("INIT> %v\n", ir.GetMessage()) + })) + + // Build sketch (with errors) + sk := paths.New("testdata", "bare_minimum") + compile, err := grpcInst.Compile(context.Background(), "", sk.String()) + require.NoError(t, err) + for { + msg, err := compile.Recv() + if err == io.EOF { + require.FailNow(t, "Expected compilation failure", "compilation succeeded") + break + } + if err != nil { + fmt.Println("COMPILE ERROR>", err) + require.Contains(t, err.Error(), "Missing FQBN") + break + } + if msg.ErrStream != nil { + fmt.Printf("COMPILE> %v\n", string(msg.GetErrStream())) + } + } +} + +func TestDaemonCoreUpdateIndex(t *testing.T) { + env, cli := createEnvForDaemon(t) + defer env.CleanUp() + + grpcInst := cli.Create() + require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { + fmt.Printf("INIT> %v\n", ir.GetMessage()) + })) + + // Set extra indexes + err := cli.SetValue( + "board_manager.additional_urls", ""+ + `["http://arduino.esp8266.com/stable/package_esp8266com_index.json",`+ + ` "http://downloads.arduino.cc/package_inexistent_index.json"]`) + require.NoError(t, err) + + analyzeUpdateIndexClient := func(cl commands.ArduinoCoreService_UpdateIndexClient) (map[string]*commands.DownloadProgressEnd, error) { + analyzer := NewDownloadProgressAnalyzer(t) + for { + msg, err := cl.Recv() + // fmt.Println("DOWNLOAD>", msg) + if err == io.EOF { + return analyzer.Results, nil + } + if err != nil { + return analyzer.Results, err + } + require.NoError(t, err) + analyzer.Process(msg.GetDownloadProgress()) + } + } + + { + cl, err := grpcInst.UpdateIndex(context.Background(), true) + require.NoError(t, err) + res, err := analyzeUpdateIndexClient(cl) + require.NoError(t, err) + require.Len(t, res, 1) + require.True(t, res["https://downloads.arduino.cc/packages/package_index.tar.bz2"].Success) + } + { + cl, err := grpcInst.UpdateIndex(context.Background(), false) + require.NoError(t, err) + res, err := analyzeUpdateIndexClient(cl) + require.Error(t, err) + require.Len(t, res, 3) + require.True(t, res["https://downloads.arduino.cc/packages/package_index.tar.bz2"].Success) + require.True(t, res["http://arduino.esp8266.com/stable/package_esp8266com_index.json"].Success) + require.False(t, res["http://downloads.arduino.cc/package_inexistent_index.json"].Success) + } +} + +func TestDaemonBundleLibInstall(t *testing.T) { + env, cli := createEnvForDaemon(t) + defer env.CleanUp() + + grpcInst := cli.Create() + require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { + fmt.Printf("INIT> %v\n", ir.GetMessage()) + })) + + // Install libraries in bundled dir + { + instCl, err := grpcInst.LibraryInstall(context.Background(), "Arduino_BuiltIn", "", false, false, true) + require.NoError(t, err) + for { + msg, err := instCl.Recv() + if err == io.EOF { + break + } + require.NoError(t, err) + fmt.Printf("LIB INSTALL> %+v\n", msg) + } + } + + // Check if libraries are installed as expected + { + resp, err := grpcInst.LibraryList(context.Background(), "", "", true, false) + require.NoError(t, err) + libsAndLocation := map[string]commands.LibraryLocation{} + for _, lib := range resp.GetInstalledLibraries() { + libsAndLocation[lib.Library.Name] = lib.Library.Location + } + require.Contains(t, libsAndLocation, "Ethernet") + require.Contains(t, libsAndLocation, "SD") + require.Contains(t, libsAndLocation, "Firmata") + require.Equal(t, libsAndLocation["Ethernet"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) + require.Equal(t, libsAndLocation["SD"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) + require.Equal(t, libsAndLocation["Firmata"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) + } + + // Install a library in sketchbook to override bundled + { + instCl, err := grpcInst.LibraryInstall(context.Background(), "Ethernet", "", false, false, false) + require.NoError(t, err) + for { + msg, err := instCl.Recv() + if err == io.EOF { + break + } + require.NoError(t, err) + fmt.Printf("LIB INSTALL> %+v\n", msg) + } + } + + // Check if libraries are installed as expected + installedEthernetVersion := "" + { + resp, err := grpcInst.LibraryList(context.Background(), "", "", true, false) + require.NoError(t, err) + libsAndLocation := map[string]commands.LibraryLocation{} + for _, lib := range resp.GetInstalledLibraries() { + libsAndLocation[lib.Library.Name] = lib.Library.Location + if lib.Library.Name == "Ethernet" { + installedEthernetVersion = lib.Library.Version + } + } + require.Contains(t, libsAndLocation, "Ethernet") + require.Contains(t, libsAndLocation, "SD") + require.Contains(t, libsAndLocation, "Firmata") + require.Equal(t, libsAndLocation["Ethernet"], commands.LibraryLocation_LIBRARY_LOCATION_USER) + require.Equal(t, libsAndLocation["SD"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) + require.Equal(t, libsAndLocation["Firmata"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) + } + + // Remove library from sketchbook + { + uninstCl, err := grpcInst.LibraryUninstall(context.Background(), "Ethernet", installedEthernetVersion) + require.NoError(t, err) + for { + msg, err := uninstCl.Recv() + if err == io.EOF { + break + } + require.NoError(t, err) + fmt.Printf("LIB INSTALL> %+v\n", msg) + } + } + + // Check if libraries are installed as expected + { + resp, err := grpcInst.LibraryList(context.Background(), "", "", true, false) + require.NoError(t, err) + libsAndLocation := map[string]commands.LibraryLocation{} + for _, lib := range resp.GetInstalledLibraries() { + libsAndLocation[lib.Library.Name] = lib.Library.Location + } + require.Contains(t, libsAndLocation, "Ethernet") + require.Contains(t, libsAndLocation, "SD") + require.Contains(t, libsAndLocation, "Firmata") + require.Equal(t, libsAndLocation["Ethernet"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) + require.Equal(t, libsAndLocation["SD"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) + require.Equal(t, libsAndLocation["Firmata"], commands.LibraryLocation_LIBRARY_LOCATION_BUILTIN) + } + + // Un-Set builtin libraries dir + err := cli.SetValue("directories.builtin.libraries", `""`) + require.NoError(t, err) + + // Re-init + require.NoError(t, grpcInst.Init("", "", func(ir *commands.InitResponse) { + fmt.Printf("INIT> %v\n", ir.GetMessage()) + })) + + // Install libraries in bundled dir (should now fail) + { + instCl, err := grpcInst.LibraryInstall(context.Background(), "Arduino_BuiltIn", "", false, false, true) + require.NoError(t, err) + for { + msg, err := instCl.Recv() + if err == io.EOF { + require.FailNow(t, "LibraryInstall is supposed to fail because builtin libraries directory is not set") + } + if err != nil { + fmt.Println("LIB INSTALL ERROR:", err) + break + } + fmt.Printf("LIB INSTALL> %+v\n", msg) + } + } +}