diff --git a/.github/workflows/check-certificates.yml b/.github/workflows/check-certificates.yml index 694792dcd..0c80be9c7 100644 --- a/.github/workflows/check-certificates.yml +++ b/.github/workflows/check-certificates.yml @@ -6,6 +6,8 @@ on: push: paths: - ".github/workflows/check-certificates.ya?ml" + tags-ignore: + - '*' pull_request: paths: - ".github/workflows/check-certificates.ya?ml" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3b2a210d9..0e31abff3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -286,6 +286,11 @@ jobs: -k "${{ env.KEYCHAIN_PASSWORD }}" \ "${{ env.KEYCHAIN }}" + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + - name: Install gon for code signing uses: actions/checkout@v4 with: @@ -309,7 +314,7 @@ jobs: bundle_id = "cc.arduino.${{ env.PROJECT_NAME }}" sign { - application_identity = "Developer ID Application: ARDUINO SA (7KT7ZWMCJT)" + application_identity = "Massimo Banzi Apple Key" deep = true } @@ -577,7 +582,7 @@ jobs: bundle_id = "cc.arduino.${{ env.PROJECT_NAME }}-installer" sign { - application_identity = "Developer ID Application: ARDUINO SA (7KT7ZWMCJT)" + application_identity = "Massimo Banzi Apple Key" } # Ask Gon for zip output to force notarization process to take place. diff --git a/bufferflow.go b/bufferflow.go deleted file mode 100644 index a9fef8e51..000000000 --- a/bufferflow.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2022 Arduino SA -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package main - -// Bufferflow interface -type Bufferflow interface { - Init() - OnIncomingData(data string) // implement this method - Close() // implement this method -} diff --git a/bufferflow_default.go b/bufferflow_default.go deleted file mode 100644 index 959737d54..000000000 --- a/bufferflow_default.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2022 Arduino SA -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package main - -import ( - "encoding/json" - - log "github.com/sirupsen/logrus" -) - -// BufferflowDefault is the default bufferflow, whick means no buffering -type BufferflowDefault struct { - port string - output chan<- []byte - input chan string - done chan bool -} - -// NewBufferflowDefault create a new default bufferflow -func NewBufferflowDefault(port string, output chan<- []byte) *BufferflowDefault { - return &BufferflowDefault{ - port: port, - output: output, - input: make(chan string), - done: make(chan bool), - } -} - -// Init will initialize the bufferflow -func (b *BufferflowDefault) Init() { - log.Println("Initting default buffer flow (which means no buffering)") - go b.consumeInput() -} - -func (b *BufferflowDefault) consumeInput() { -Loop: - for { - select { - case data := <-b.input: - m := SpPortMessage{b.port, data} - message, _ := json.Marshal(m) - b.output <- message - case <-b.done: - break Loop //this is required, a simple break statement would only exit the innermost switch statement - } - } - close(b.input) // close the input channel at the end of the computation -} - -// OnIncomingData will forward the data -func (b *BufferflowDefault) OnIncomingData(data string) { - b.input <- data -} - -// Close will close the bufferflow -func (b *BufferflowDefault) Close() { - b.done <- true - close(b.done) -} diff --git a/bufferflow_timed.go b/bufferflow_timed.go index 36aaf08bf..6c5fab04a 100644 --- a/bufferflow_timed.go +++ b/bufferflow_timed.go @@ -33,8 +33,8 @@ type BufferflowTimed struct { bufferedOutput string } -// NewBufferflowTimed will create a new timed bufferflow -func NewBufferflowTimed(port string, output chan<- []byte) *BufferflowTimed { +// NewBufferFlowTimed will create a new timed bufferflow +func NewBufferFlowTimed(port string, output chan<- []byte) *BufferflowTimed { return &BufferflowTimed{ port: port, output: output, @@ -48,7 +48,7 @@ func NewBufferflowTimed(port string, output chan<- []byte) *BufferflowTimed { // Init will initialize the bufferflow func (b *BufferflowTimed) Init() { - log.Println("Initting timed buffer flow (output once every 16ms)") + log.Println("Start consuming from serial port (output once every 16ms)") go b.consumeInput() } diff --git a/bufferflow_timedraw.go b/bufferflow_timedraw.go deleted file mode 100644 index 08b34cab6..000000000 --- a/bufferflow_timedraw.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2022 Arduino SA -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package main - -import ( - "encoding/json" - "time" - - log "github.com/sirupsen/logrus" -) - -// BufferflowTimedRaw sends raw data once every 16ms -type BufferflowTimedRaw struct { - port string - output chan<- []byte - input chan string - done chan bool - ticker *time.Ticker - bufferedOutputRaw []byte - sPortRaw string -} - -// NewBufferflowTimedRaw will create a new raw bufferflow -func NewBufferflowTimedRaw(port string, output chan<- []byte) *BufferflowTimedRaw { - return &BufferflowTimedRaw{ - port: port, - output: output, - input: make(chan string), - done: make(chan bool), - ticker: time.NewTicker(16 * time.Millisecond), - bufferedOutputRaw: nil, - sPortRaw: "", - } -} - -// Init will initialize the bufferflow -func (b *BufferflowTimedRaw) Init() { - log.Println("Initting timed buffer raw flow (output once every 16ms)") - go b.consumeInput() -} - -func (b *BufferflowTimedRaw) consumeInput() { -Loop: - for { - select { - case data := <-b.input: // use the buffer and append data to it - b.bufferedOutputRaw = append(b.bufferedOutputRaw, []byte(data)...) - b.sPortRaw = b.port - case <-b.ticker.C: // after 16ms send the buffered output message - if b.bufferedOutputRaw != nil { - m := SpPortMessageRaw{b.sPortRaw, b.bufferedOutputRaw} - buf, _ := json.Marshal(m) - // since bufferedOutputRaw is a []byte is base64-encoded by json.Marshal() function automatically - b.output <- buf - // reset the buffer and the port - b.bufferedOutputRaw = nil - b.sPortRaw = "" - } - case <-b.done: - break Loop //this is required, a simple break statement would only exit the innermost switch statement - } - } - close(b.input) -} - -// OnIncomingData will forward the data -func (b *BufferflowTimedRaw) OnIncomingData(data string) { - b.input <- data -} - -// Close will close the bufferflow -func (b *BufferflowTimedRaw) Close() { - b.ticker.Stop() - b.done <- true - close(b.done) -} diff --git a/config/config.go b/config/config.go index 69d29eeee..50978eb82 100644 --- a/config/config.go +++ b/config/config.go @@ -142,3 +142,41 @@ func SetInstallCertsIni(filename string, value string) error { } return nil } + +func GetConfigPath() *paths.Path { + // Let's handle the config + configDir := GetDefaultConfigDir() + var configPath *paths.Path + + // see if the env var is defined, if it is take the config from there, this will override the default path + if envConfig := os.Getenv("ARDUINO_CREATE_AGENT_CONFIG"); envConfig != "" { + configPath = paths.New(envConfig) + if configPath.NotExist() { + log.Panicf("config from env var %s does not exists", envConfig) + } + log.Infof("using config from env variable: %s", configPath) + } else if defaultConfigPath := configDir.Join("config.ini"); defaultConfigPath.Exist() { + // by default take the config from the ~/.arduino-create/config.ini file + configPath = defaultConfigPath + log.Infof("using config from default: %s", configPath) + } else { + // Fall back to the old config.ini location + src, _ := os.Executable() + oldConfigPath := paths.New(src).Parent().Join("config.ini") + if oldConfigPath.Exist() { + err := oldConfigPath.CopyTo(defaultConfigPath) + if err != nil { + log.Errorf("cannot copy old %s, to %s, generating new config", oldConfigPath, configPath) + } else { + configPath = defaultConfigPath + log.Infof("copied old %s, to %s", oldConfigPath, configPath) + } + } + } + if configPath == nil { + configPath = GenerateConfig(configDir) + } + + return configPath + +} diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 000000000..76e6988c0 --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,61 @@ +package config + +import ( + "fmt" + "os" + "testing" + + "github.com/arduino/go-paths-helper" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestGetConfigPath(t *testing.T) { + t.Run("read config.ini from ARDUINO_CREATE_AGENT_CONFIG", func(t *testing.T) { + os.Setenv("ARDUINO_CREATE_AGENT_CONFIG", "./testdata/fromenv/config.ini") + defer os.Unsetenv("ARDUINO_CREATE_AGENT_CONFIG") + configPath := GetConfigPath() + assert.Equal(t, "./testdata/fromenv/config.ini", configPath.String()) + }) + + t.Run("panic if config.ini does not exist", func(t *testing.T) { + os.Setenv("ARDUINO_CREATE_AGENT_CONFIG", "./testdata/nonexistent_config.ini") + defer os.Unsetenv("ARDUINO_CREATE_AGENT_CONFIG") + + defer func() { + if r := recover(); r != nil { + entry, ok := r.(*logrus.Entry) + if !ok { + t.Errorf("Expected panic of type *logrus.Entry but got %T", r) + } else { + assert.Equal(t, "config from env var ./testdata/nonexistent_config.ini does not exists", entry.Message) + } + } else { + t.Errorf("Expected panic but did not get one") + } + }() + + GetConfigPath() + }) + + t.Run("read config.ini from $HOME", func(t *testing.T) { + os.Setenv("HOME", "./testdata/home") + defer os.Unsetenv("HOME") + configPath := GetConfigPath() + assert.Equal(t, "testdata/home/.config/ArduinoCreateAgent/config.ini", configPath.String()) + }) + + t.Run("fallback old : read config.ini where the binary is launched", func(t *testing.T) { + src, _ := os.Executable() + paths.New(src).Parent().Join("config.ini").Create() // create a config.ini in the same directory as the binary + // The fallback path is the directory where the binary is launched + fmt.Println(src) + os.Setenv("HOME", "./testdata/noconfig") // force to not have a config in the home directory + defer os.Unsetenv("HOME") + + // expect it creates a config.ini in the same directory as the binary + configPath := GetConfigPath() + assert.Equal(t, "testdata/home/.config/ArduinoCreateAgent/config.ini", configPath.String()) + }) + +} diff --git a/config/testdata/fromenv/config.ini b/config/testdata/fromenv/config.ini new file mode 100644 index 000000000..5b31315b9 --- /dev/null +++ b/config/testdata/fromenv/config.ini @@ -0,0 +1,8 @@ +gc = std +hostname = unknown-hostname +regex = usb|acm|com +v = true +appName = CreateAgent/Stable +updateUrl = https://downloads.arduino.cc/ +origins = https://local.arduino.cc:8000, https://local.arduino.cc:8001, https://create-dev.arduino.cc, https://*.sparklyunicorn.cc, https://*.iot-cloud-arduino-cc.pages.dev, https://cloud.oniudra.cc, https://app.oniudra.cc,https://*.iot-cloud-arduino-cc.pages.dev +crashreport = false diff --git a/config/testdata/home/.config/ArduinoCreateAgent/config.ini b/config/testdata/home/.config/ArduinoCreateAgent/config.ini new file mode 100644 index 000000000..92f231faf --- /dev/null +++ b/config/testdata/home/.config/ArduinoCreateAgent/config.ini @@ -0,0 +1,8 @@ +gc = std +hostname = unknown-hostname +regex = usb|acm|com +v = true +appName = config-from-home-dir +updateUrl = https://downloads.arduino.cc/ +origins = https://local.arduino.cc:8000, https://local.arduino.cc:8001, https://*.iot-cloud-arduino-cc.pages.dev +crashreport = false diff --git a/config/testdata/noconfig/.config/ArduinoCreateAgent/config.ini b/config/testdata/noconfig/.config/ArduinoCreateAgent/config.ini new file mode 100644 index 000000000..f63377db5 --- /dev/null +++ b/config/testdata/noconfig/.config/ArduinoCreateAgent/config.ini @@ -0,0 +1,10 @@ +gc = std # Type of garbage collection. std = Normal garbage collection allowing system to decide (this has been known to cause a stop the world in the middle of a CNC job which can cause lost responses from the CNC controller and thus stalled jobs. use max instead to solve.), off = let memory grow unbounded (you have to send in the gc command manually to garbage collect or you will run out of RAM eventually), max = Force garbage collection on each recv or send on a serial port (this minimizes stop the world events and thus lost serial responses, but increases CPU usage) +hostname = unknown-hostname # Override the hostname we get from the OS +regex = usb|acm|com # Regular expression to filter serial port list +v = true # show debug logging +appName = CreateAgent/Stable +updateUrl = https://downloads.arduino.cc/ +origins = https://local.arduino.cc:8000 +#httpProxy = http://your.proxy:port # Proxy server for HTTP requests +crashreport = false # enable crashreport logging +autostartMacOS = true # the Arduino Create Agent is able to start automatically after login on macOS (launchd agent) \ No newline at end of file diff --git a/hub.go b/hub.go index a162dd01a..81a169121 100755 --- a/hub.go +++ b/hub.go @@ -58,7 +58,7 @@ var h = hub{ const commands = `{ "Commands": [ "list", - "open [bufferAlgorithm: ({default}, timed, timedraw)]", + "open ", "(send, sendnobuf, sendraw) ", "close ", "restart", @@ -146,15 +146,13 @@ func checkCmd(m []byte) { go spErr("Problem converting baud rate " + args[2]) return } - // pass in buffer type now as string. if user does not - // ask for a buffer type pass in empty string - bufferAlgorithm := "default" // use the default buffer if none is specified + + // Ignore extra "buffer type" param for backward compatibility if len(args) > 3 { - // cool. we got a buffer type request - buftype := strings.Replace(args[3], "\n", "", -1) - bufferAlgorithm = buftype + log.Warn(fmt.Sprintf("Unexpected arguments for the open command. Ignored arguments: '%s'.", args[3:])) } - go spHandlerOpen(args[1], baud, bufferAlgorithm) + + go spHandlerOpen(args[1], baud) } else if strings.HasPrefix(sl, "close") { diff --git a/main.go b/main.go index 1ca857b02..a83d7f4c5 100755 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( _ "embed" "encoding/json" "flag" + "fmt" "html/template" "io" "os" @@ -188,38 +189,9 @@ func loop() { h.broadcastSys <- mapB } - // Let's handle the config - configDir := config.GetDefaultConfigDir() - var configPath *paths.Path + configPath := config.GetConfigPath() - // see if the env var is defined, if it is take the config from there, this will override the default path - if envConfig := os.Getenv("ARDUINO_CREATE_AGENT_CONFIG"); envConfig != "" { - configPath = paths.New(envConfig) - if configPath.NotExist() { - log.Panicf("config from env var %s does not exists", envConfig) - } - log.Infof("using config from env variable: %s", configPath) - } else if defaultConfigPath := configDir.Join("config.ini"); defaultConfigPath.Exist() { - // by default take the config from the ~/.arduino-create/config.ini file - configPath = defaultConfigPath - log.Infof("using config from default: %s", configPath) - } else { - // Fall back to the old config.ini location - src, _ := os.Executable() - oldConfigPath := paths.New(src).Parent().Join("config.ini") - if oldConfigPath.Exist() { - err := oldConfigPath.CopyTo(defaultConfigPath) - if err != nil { - log.Errorf("cannot copy old %s, to %s, generating new config", oldConfigPath, configPath) - } else { - configPath = defaultConfigPath - log.Infof("copied old %s, to %s", oldConfigPath, configPath) - } - } - } - if configPath == nil { - configPath = config.GenerateConfig(configDir) - } + fmt.Println("configPath: ", configPath) // if the default browser is Safari, prompt the user to install HTTPS certificates // and eventually install them diff --git a/serial.go b/serial.go index 64e5b8f7f..f58414571 100755 --- a/serial.go +++ b/serial.go @@ -67,7 +67,7 @@ var sh = serialhub{ func (sh *serialhub) Register(port *serport) { sh.mu.Lock() //log.Print("Registering a port: ", p.portConf.Name) - h.broadcastSys <- []byte("{\"Cmd\":\"Open\",\"Desc\":\"Got register/open on port.\",\"Port\":\"" + port.portConf.Name + "\",\"Baud\":" + strconv.Itoa(port.portConf.Baud) + ",\"BufferType\":\"" + port.BufferType + "\"}") + h.broadcastSys <- []byte("{\"Cmd\":\"Open\",\"Desc\":\"Got register/open on port.\",\"Port\":\"" + port.portConf.Name + "\",\"Baud\":" + strconv.Itoa(port.portConf.Baud) + "}") sh.ports[port] = true sh.mu.Unlock() } diff --git a/serialport.go b/serialport.go index 0d386bbfc..9a06c7f9c 100755 --- a/serialport.go +++ b/serialport.go @@ -22,7 +22,6 @@ import ( "strconv" "sync/atomic" "time" - "unicode/utf8" log "github.com/sirupsen/logrus" serial "go.bug.st/serial" @@ -57,10 +56,7 @@ type serport struct { // channel containing raw base64 encoded binary data (outbound messages) sendRaw chan string - // Do we have an extra channel/thread to watch our buffer? - BufferType string - //bufferwatcher *BufferflowDummypause - bufferwatcher Bufferflow + bufferFlow *BufferflowTimed } // SpPortMessage is the serial port message @@ -75,15 +71,13 @@ type SpPortMessageRaw struct { D []byte // the data, i.e. G0 X0 Y0 } -func (p *serport) reader(buftype string) { +func (p *serport) reader() { timeCheckOpen := time.Now() - var bufferedCh bytes.Buffer serialBuffer := make([]byte, 1024) for { n, err := p.portIo.Read(serialBuffer) - bufferPart := serialBuffer[:n] //if we detect that port is closing, break out of this for{} loop. if p.isClosing.Load() { @@ -96,39 +90,8 @@ func (p *serport) reader(buftype string) { // read can return legitimate bytes as well as an error // so process the n bytes red, if n > 0 if n > 0 && err == nil { - - log.Print("Read " + strconv.Itoa(n) + " bytes ch: " + string(bufferPart[:n])) - - data := "" - switch buftype { - case "timedraw", "timed": - data = string(bufferPart[:n]) - // give the data to our bufferflow so it can do it's work - // to read/translate the data to see if it wants to block - // writes to the serialport. each bufferflow type will decide - // this on its own based on its logic - p.bufferwatcher.OnIncomingData(data) - case "default": // the bufferbuftype is actually called default 🤷‍♂️ - // save the left out bytes for the next iteration due to UTF-8 encoding - bufferPart = append(bufferedCh.Bytes(), bufferPart[:n]...) - n += len(bufferedCh.Bytes()) - bufferedCh.Reset() - for i, w := 0, 0; i < n; i += w { - runeValue, width := utf8.DecodeRune(bufferPart[i:n]) // try to decode the first i bytes in the buffer (UTF8 runes do not have a fixed length) - if runeValue == utf8.RuneError { - bufferedCh.Write(bufferPart[i:n]) - break - } - if i == n { - bufferedCh.Reset() - } - data += string(runeValue) - w = width - } - p.bufferwatcher.OnIncomingData(data) - default: - log.Panicf("unknown buffer type %s", buftype) - } + log.Print("Read " + strconv.Itoa(n) + " bytes ch: " + string(serialBuffer[:n])) + p.bufferFlow.OnIncomingData(string(serialBuffer[:n])) } // double check that we got characters in the buffer @@ -273,7 +236,7 @@ func (p *serport) writerRaw() { h.broadcastSys <- []byte(msgstr) } -func spHandlerOpen(portname string, baud int, buftype string) { +func spHandlerOpen(portname string, baud int) { log.Print("Inside spHandler") @@ -312,23 +275,10 @@ func spHandlerOpen(portname string, baud int, buftype string) { portConf: conf, portIo: sp, portName: portname, - BufferType: buftype} - - var bw Bufferflow - - switch buftype { - case "timed": - bw = NewBufferflowTimed(portname, h.broadcastSys) - case "timedraw": - bw = NewBufferflowTimedRaw(portname, h.broadcastSys) - case "default": - bw = NewBufferflowDefault(portname, h.broadcastSys) - default: - log.Panicf("unknown buffer type: %s", buftype) } - bw.Init() - p.bufferwatcher = bw + p.bufferFlow = NewBufferFlowTimed(portname, h.broadcastSys) + p.bufferFlow.Init() sh.Register(p) defer sh.Unregister(p) @@ -343,7 +293,7 @@ func spHandlerOpen(portname string, baud int, buftype string) { // this is thread to send to serial port but with base64 decoding go p.writerRaw() - p.reader(buftype) + p.reader() serialPorts.List() } @@ -351,7 +301,7 @@ func spHandlerOpen(portname string, baud int, buftype string) { func (p *serport) Close() { p.isClosing.Store(true) - p.bufferwatcher.Close() + p.bufferFlow.Close() p.portIo.Close() serialPorts.MarkPortAsClosed(p.portName) serialPorts.List() diff --git a/tests/test_ws.py b/tests/test_ws.py index b8004649d..2f3ee9fa5 100644 --- a/tests/test_ws.py +++ b/tests/test_ws.py @@ -49,77 +49,28 @@ def test_list(socketio, message): running_on_ci(), reason="VMs have no serial ports", ) -def test_open_serial_default(socketio, serial_port, baudrate, message): - general_open_serial(socketio, serial_port, baudrate, message, "default") - - -@pytest.mark.skipif( - running_on_ci(), - reason="VMs have no serial ports", -) -def test_open_serial_timed(socketio, serial_port, baudrate, message): - general_open_serial(socketio, serial_port, baudrate, message, "timed") - - -@pytest.mark.skipif( - running_on_ci(), - reason="VMs have no serial ports", -) -def test_open_serial_timedraw(socketio, serial_port, baudrate, message): - general_open_serial(socketio, serial_port, baudrate, message, "timedraw") - +def test_open_serial(socketio, serial_port, baudrate, message): + general_open_serial(socketio, serial_port, baudrate, message) # NOTE run the following tests with a board connected to the PC and with the sketch found in tests/testdata/SerialEcho.ino on it be sure to change serial_address in conftest.py @pytest.mark.skipif( running_on_ci(), reason="VMs have no serial ports", ) -def test_send_serial_default(socketio, close_port, serial_port, baudrate, message): - general_send_serial(socketio, close_port, serial_port, baudrate, message, "default") - - -@pytest.mark.skipif( - running_on_ci(), - reason="VMs have no serial ports", -) -def test_send_serial_timed(socketio, close_port, serial_port, baudrate, message): - general_send_serial(socketio, close_port, serial_port, baudrate, message, "timed") +def test_send_serial(socketio, close_port, serial_port, baudrate, message): + general_send_serial(socketio, close_port, serial_port, baudrate, message) @pytest.mark.skipif( running_on_ci(), reason="VMs have no serial ports", ) -def test_send_serial_timedraw(socketio, close_port, serial_port, baudrate, message): - general_send_serial(socketio, close_port, serial_port, baudrate, message, "timedraw") +def test_send_emoji_serial(socketio, close_port, serial_port, baudrate, message): + general_send_emoji_serial(socketio, close_port, serial_port, baudrate, message) -@pytest.mark.skipif( - running_on_ci(), - reason="VMs have no serial ports", -) -def test_send_emoji_serial_default(socketio, close_port, serial_port, baudrate, message): - general_send_emoji_serial(socketio, close_port, serial_port, baudrate, message, "default") - - -@pytest.mark.skipif( - running_on_ci(), - reason="VMs have no serial ports", -) -def test_send_emoji_serial_timed(socketio, close_port, serial_port, baudrate, message): - general_send_emoji_serial(socketio, close_port, serial_port, baudrate, message, "timed") - - -@pytest.mark.skipif( - running_on_ci(), - reason="VMs have no serial ports", -) -def test_send_emoji_serial_timedraw(socketio, close_port, serial_port, baudrate, message): - general_send_emoji_serial(socketio, close_port, serial_port, baudrate, message, "timedraw") - - -def general_open_serial(socketio, serial_port, baudrate, message, buffertype): - open_serial_port(socketio, serial_port, baudrate, message, buffertype) +def general_open_serial(socketio, serial_port, baudrate, message): + open_serial_port(socketio, serial_port, baudrate, message) # test the closing of the serial port, we are gonna use close_port for the other tests socketio.emit('command', 'close ' + serial_port) time.sleep(.2) @@ -128,8 +79,8 @@ def general_open_serial(socketio, serial_port, baudrate, message, buffertype): assert any("\"IsOpen\": false," in i for i in message) -def general_send_serial(socketio, close_port, serial_port, baudrate, message, buffertype): - open_serial_port(socketio, serial_port, baudrate, message, buffertype) +def general_send_serial(socketio, close_port, serial_port, baudrate, message): + open_serial_port(socketio, serial_port, baudrate, message) # send the string "ciao" using the serial connection socketio.emit('command', 'send ' + serial_port + ' ciao') time.sleep(1) @@ -137,33 +88,27 @@ def general_send_serial(socketio, close_port, serial_port, baudrate, message, bu # check if the send command has been registered assert any("send " + serial_port + " ciao" in i for i in message) #check if message has been sent back by the connected board - if buffertype == "timedraw": - output = decode_output(extract_serial_data(message)) - elif buffertype in ("default", "timed"): - output = extract_serial_data(message) + output = extract_serial_data(message) assert "ciao" in output # the serial connection is closed by close_port() fixture: even if in case of test failure -def general_send_emoji_serial(socketio, close_port, serial_port, baudrate, message, buffertype): - open_serial_port(socketio, serial_port, baudrate, message, buffertype) +def general_send_emoji_serial(socketio, close_port, serial_port, baudrate, message): + open_serial_port(socketio, serial_port, baudrate, message) # send a lot of emoji: they can be messed up socketio.emit('command', 'send ' + serial_port + ' /"🧀🧀🧀🧀🧀🧀🧀🧀🧀🧀/"') time.sleep(1) print(message) # check if the send command has been registered assert any("send " + serial_port + " /\"🧀🧀🧀🧀🧀🧀🧀🧀🧀🧀/\"" in i for i in message) - if buffertype == "timedraw": - output = decode_output(extract_serial_data(message)) - elif buffertype in ("default", "timed"): - output = extract_serial_data(message) + output = extract_serial_data(message) assert "/\"🧀🧀🧀🧀🧀🧀🧀🧀🧀🧀/\"" in output # the serial connection is closed by close_port() fixture: even if in case of test failure -def open_serial_port(socketio, serial_port, baudrate, message, buffertype): - #open a new serial connection with the specified buffertype - socketio.emit('command', 'open ' + serial_port + ' ' + baudrate + ' ' + buffertype) +def open_serial_port(socketio, serial_port, baudrate, message): + #open a new serial connection + socketio.emit('command', 'open ' + serial_port + ' ' + baudrate) # give time to the message var to be filled time.sleep(.5) print(message) @@ -176,7 +121,7 @@ def open_serial_port(socketio, serial_port, baudrate, message, buffertype): reason="VMs have no serial ports", ) def test_sendraw_serial(socketio, close_port, serial_port, baudrate, message): - open_serial_port(socketio, serial_port, baudrate, message, "timedraw") + open_serial_port(socketio, serial_port, baudrate, message) #test with bytes integers = [1, 2, 3, 4, 5] bytes_array=bytearray(integers) @@ -202,7 +147,7 @@ def extract_serial_data(msg): serial_data+=json.loads(i)["D"] print("serialdata:"+serial_data) return serial_data - + def decode_output(raw_output): # print(raw_output) base64_bytes = raw_output.encode('ascii') #encode rawoutput message into a bytes-like object