Instrument Neutral Distributed Interface INDI 2.2.5
test_connection_serial.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2026 Jasem Mutlaq. All rights reserved.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
17*******************************************************************************/
18
19// Regression tests for the serial connection plugin configuration handling:
20// - Baud rates are loaded by name, so configurations saved before the slower baud rates were
21// added (different switch order) still select the right baud rate.
22// - Saving a single property whose saved layout differs from the current one rewrites it with
23// the current layout instead of dropping the new switches.
24
25#include <gtest/gtest.h>
26
27#include <cstdio>
28#include <fstream>
29#include <memory>
30#include <string>
31#include <unistd.h>
32
33#include "defaultdevice.h"
34#include "indidriver.h"
35#include "indilogger.h"
37
38namespace
39{
40
41const char *DEVICE_NAME = "MockSerialDevice";
42
43class SerialDevice : public INDI::DefaultDevice
44{
45 public:
46 explicit SerialDevice(Connection::Serial::BaudRate defaultBaudRate) : m_DefaultBaudRate(defaultBaudRate)
47 {
48 setDeviceName(DEVICE_NAME);
49 }
50
51 const char *getDefaultName() override
52 {
53 return DEVICE_NAME;
54 }
55
56 bool initProperties() override
57 {
59 serialConnection = new Connection::Serial(this);
60 serialConnection->setDefaultBaudRate(m_DefaultBaudRate);
61 registerConnection(serialConnection);
62 return true;
63 }
64
66
67 Connection::Serial *serialConnection {nullptr};
68
69 private:
70 Connection::Serial::BaudRate m_DefaultBaudRate;
71};
72
73class SerialConfigTest : public ::testing::Test
74{
75 protected:
76 void SetUp() override
77 {
78 // Use a configuration file private to this process, never the user's ~/.indi
79 m_ConfigFile = "/tmp/test_connection_serial_" + std::to_string(getpid()) + "_config.xml";
80 setenv("INDICONFIG", m_ConfigFile.c_str(), 1);
81 removeConfig();
82 }
83
84 void TearDown() override
85 {
86 removeConfig();
87 }
88
89 void removeConfig()
90 {
91 std::remove(m_ConfigFile.c_str());
92 // Saving the whole configuration also writes a default configuration copy
93 std::remove((m_ConfigFile + ".default").c_str());
94 }
95
96 // Configuration as saved before 1200, 2400 and 4800 baud were added.
97 void writeOldLayoutConfig(const std::string &onBaudRate)
98 {
99 std::ofstream out(m_ConfigFile);
100 out << "<INDIDriver>\n";
101 out << "<newSwitchVector device='" << DEVICE_NAME << "' name='CONNECTION_MODE'>\n";
102 out << " <oneSwitch name='CONNECTION_SERIAL'>On</oneSwitch>\n";
103 out << "</newSwitchVector>\n";
104 out << "<newTextVector device='" << DEVICE_NAME << "' name='DEVICE_PORT'>\n";
105 out << " <oneText name='PORT'>/dev/null</oneText>\n";
106 out << "</newTextVector>\n";
107 out << "<newSwitchVector device='" << DEVICE_NAME << "' name='DEVICE_BAUD_RATE'>\n";
108 for (const char *rate : {"9600", "19200", "38400", "57600", "115200", "230400"})
109 out << " <oneSwitch name='" << rate << "'>" << (onBaudRate == rate ? "On" : "Off") << "</oneSwitch>\n";
110 out << "</newSwitchVector>\n";
111 out << "</INDIDriver>\n";
112 }
113
114 std::unique_ptr<SerialDevice> startDevice(Connection::Serial::BaudRate defaultBaudRate)
115 {
116 auto device = std::make_unique<SerialDevice>(defaultBaudRate);
117 device->initProperties();
118 device->ISGetProperties(nullptr);
119 return device;
120 }
121
122 std::string savedBaudRate() const
123 {
124 char name[MAXINDINAME] = {0};
125 if (IUGetConfigOnSwitchName(DEVICE_NAME, "DEVICE_BAUD_RATE", name, MAXINDINAME) != 0)
126 return "";
127 return name;
128 }
129
130 std::string m_ConfigFile;
131};
132
133TEST_F(SerialConfigTest, DefaultBaudRateWithoutConfig)
134{
135 auto device = startDevice(Connection::Serial::B_4800);
136 EXPECT_EQ(device->serialConnection->baud(), 4800u);
137}
138
139TEST_F(SerialConfigTest, OldLayoutConfigLoadsBaudRateByName)
140{
141 // Index 4 in the old layout is 115200, but 19200 in the new one.
142 writeOldLayoutConfig("115200");
143 auto device = startDevice(Connection::Serial::B_9600);
144 EXPECT_EQ(device->serialConnection->baud(), 115200u);
145}
146
147TEST_F(SerialConfigTest, OldLayoutConfigOverridesDriverDefault)
148{
149 writeOldLayoutConfig("57600");
150 auto device = startDevice(Connection::Serial::B_115200);
151 EXPECT_EQ(device->serialConnection->baud(), 57600u);
152}
153
154TEST_F(SerialConfigTest, SavingNewBaudRateOverOldLayoutConfig)
155{
156 writeOldLayoutConfig("57600");
157 auto device = startDevice(Connection::Serial::B_9600);
158
159 const char *names[] = {"4800"};
160 ISState states[] = {ISS_ON};
161 ASSERT_TRUE(device->ISNewSwitch(DEVICE_NAME, "DEVICE_BAUD_RATE", states, const_cast<char **>(names), 1));
162 ASSERT_EQ(device->serialConnection->baud(), 4800u);
163
164 // Single-property save, as done by the plugin after a successful handshake.
165 ASSERT_TRUE(device->saveConfig(true, "DEVICE_BAUD_RATE"));
166 EXPECT_EQ(savedBaudRate(), "4800");
167
168 // Other saved properties must survive the rewrite.
169 char port[MAXINDINAME] = {0};
170 ASSERT_EQ(IUGetConfigText(DEVICE_NAME, "DEVICE_PORT", "PORT", port, MAXINDINAME), 0);
171 EXPECT_STREQ(port, "/dev/null");
172
173 // A restarted driver picks up the new baud rate.
174 device.reset();
175 auto restarted = startDevice(Connection::Serial::B_9600);
176 EXPECT_EQ(restarted->serialConnection->baud(), 4800u);
177}
178
179TEST_F(SerialConfigTest, SavingOverCurrentLayoutConfig)
180{
181 auto device = startDevice(Connection::Serial::B_9600);
182 ASSERT_TRUE(device->saveConfig(true));
183 ASSERT_EQ(savedBaudRate(), "9600");
184
185 const char *names[] = {"2400"};
186 ISState states[] = {ISS_ON};
187 ASSERT_TRUE(device->ISNewSwitch(DEVICE_NAME, "DEVICE_BAUD_RATE", states, const_cast<char **>(names), 1));
188 ASSERT_TRUE(device->saveConfig(true, "DEVICE_BAUD_RATE"));
189 EXPECT_EQ(savedBaudRate(), "2400");
190}
191
192}
193
194int main(int argc, char **argv)
195{
198 ::testing::InitGoogleTest(&argc, argv);
199 return RUN_ALL_TESTS();
200}
hid_device * device
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
BaudRate
Supported baud rates.
void setDeviceName(const char *dev)
Set the device name.
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
virtual const char * getDefaultName()=0
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
int main()
ISState
Switch state.
Definition indiapi.h:148
@ ISS_ON
Definition indiapi.h:150
#define MAXINDINAME
Definition indiapi.h:189
int IUGetConfigOnSwitchName(const char *dev, const char *property, char *name, size_t size)
IUGetConfigOnSwitchLabel Opens configuration file and returns the name of the ON switch property,...
Definition indidriver.c:783
int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
IUGetConfigText Opens configuration file and reads single text property.
Definition indidriver.c:908
static const loggerConf file_off
Definition indilogger.h:219
void configure(const std::string &outputFile, const loggerConf configuration, const int fileVerbosityLevel, const int screenVerbosityLevel)
Method to configure the logger. Called by the DEBUG_CONF() macro. To make implementation easier,...
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
TEST_F(AlignmentPluginTest, Test_BuiltIn_ErrorRecovery)