Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
325 andreas 1
/*
2
 * Copyright (C) 2023 by Andreas Theofilu <andreas@theosys.at>
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
#include <cstring>
326 andreas 19
#include <iomanip>
20
#include <fstream>
325 andreas 21
 
22
#include "testmode.h"
23
#include "tpagemanager.h"
326 andreas 24
#include "tdirectory.h"
325 andreas 25
#include "tconfig.h"
326 andreas 26
#include "tresources.h"
325 andreas 27
#include "terror.h"
328 andreas 28
#ifdef __ANDROID__
29
#include <android/log.h>
30
#endif
325 andreas 31
 
326 andreas 32
#define TIMEOUT     5000000  // Microseconds (5 seconds)
33
#define DELAY       100      // Wait this amount of microseconds
34
 
35
using std::string;
325 andreas 36
using std::strncpy;
37
using std::min;
326 andreas 38
using std::atomic;
39
using std::vector;
40
using std::cout;
41
using std::endl;
42
using std::ifstream;
43
using namespace dir;
325 andreas 44
 
326 andreas 45
atomic<bool> __success{false};
46
atomic<bool> __done{false};
47
bool _testmode{false};
330 andreas 48
bool _run_test_ready{false};
326 andreas 49
string gLastCommand;
327 andreas 50
_TestMode *_gTestMode{nullptr};
326 andreas 51
 
52
extern amx::TAmxNet *gAmxNet;
53
 
54
_TestMode::_TestMode(const string& path)
55
    : mPath(path)
56
{
325 andreas 57
#if TESTMODE == 1
326 andreas 58
    DECL_TRACER("_TestMode::_TestMode(const string& path)");
325 andreas 59
 
326 andreas 60
    TDirectory dir(mPath);
61
    dir.scanFiles(".tst");
62
    size_t num = dir.getNumEntries();
63
    size_t pos = 0;
64
 
65
    while (pos < num)
66
    {
67
        DFILES_T f = dir.getEntry(pos);
68
 
69
        if (dir.testText(f.attr))
70
            mCmdFiles.push_back(f.name);
71
 
72
        pos++;
73
    }
325 andreas 74
#endif
75
}
76
 
326 andreas 77
_TestMode::~_TestMode()
325 andreas 78
{
326 andreas 79
    DECL_TRACER("_TestMode::~_TestMode()");
80
 
81
    _gTestMode = nullptr;
82
    gLastCommand.clear();
83
    prg_stopped = true;
84
    killed = true;
85
 
86
    if (gAmxNet)
87
        gAmxNet->stop();
88
 
89
    if (gPageManager)
90
        gPageManager->callShutdown();
91
}
92
 
93
void _TestMode::run()
94
{
95
#if TESTMODE == 1
96
    DECL_TRACER("_TestMode::run()");
97
 
98
    if (isRunning)
99
        return;
100
 
101
    try
102
    {
103
        mThread = std::thread([=] { this->start(); });
104
        mThread.detach();
105
    }
106
    catch (std::exception& e)
107
    {
108
        MSG_ERROR("Error spawning thread: " << e.what());
109
    }
110
#endif
111
}
112
 
113
void _TestMode::start()
114
{
115
    isRunning = true;
116
    DECL_TRACER("_TestMode::start()");
117
 
330 andreas 118
    while (!_run_test_ready)
119
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
120
 
326 andreas 121
    vector<string>::iterator iter;
122
 
123
    for (iter = mCmdFiles.begin(); iter != mCmdFiles.end(); ++iter)
124
    {
125
        _TESTCMD tcmd;
126
 
127
        try
128
        {
129
            ifstream is(*iter);
130
            char buffer[4096];
327 andreas 131
            int line = 1;
330 andreas 132
            int port = 1;
133
            string command, content;
326 andreas 134
            tcmd.command.clear();
135
            tcmd.result.clear();
136
            tcmd.compare = false;
137
 
138
            while (is.getline(buffer, sizeof(buffer)))
139
            {
140
                if (buffer[0] == '#')
141
                    continue;
142
 
330 andreas 143
                string scmd(buffer);
144
                command.clear();
145
                content.clear();
146
                size_t pos = scmd.find("=");
147
 
148
                // Parse the command line and extract the command and the content
149
                if (scmd == "exec")
150
                    command = scmd;
151
                else if (pos == string::npos)
326 andreas 152
                {
330 andreas 153
                    MSG_WARNING("Line " << line << ": Invalid or malformed command!");
154
                    continue;
155
                }
156
                else
157
                {
158
                    command = scmd.substr(0, pos);
159
                    content = scmd.substr(pos + 1);
160
                }
326 andreas 161
 
330 andreas 162
                // Test for the command and take the desired action.
163
                if (command == "command")
164
                    tcmd.command = content;
165
                else if (command == "port")
326 andreas 166
                {
330 andreas 167
                    int p = atoi(content.c_str());
326 andreas 168
 
330 andreas 169
                    if (p > 0)
170
                        port = p;
326 andreas 171
                }
330 andreas 172
                else if (command == "result")
173
                    tcmd.result = content;
174
                else if (command == "compare")
175
                    tcmd.compare = isTrue(content);
176
                else if (command == "wait")
177
                {
178
                    unsigned long ms = atol(content.c_str());
326 andreas 179
 
330 andreas 180
                    if (ms != 0)
181
                        std::this_thread::sleep_for(std::chrono::milliseconds(ms));
326 andreas 182
                }
331 andreas 183
                else if (command == "nowait")
184
                    tcmd.nowait = isTrue(content);
330 andreas 185
                else if (command == "exec")
327 andreas 186
                {
187
                    if (tcmd.command.empty())
188
                    {
189
                       MSG_WARNING("Nothing to execute on line " << line << "!");
190
                       line++;
191
                       continue;
192
                    }
193
 
194
                    inform("Testing command: " + tcmd.command);
330 andreas 195
                    inject(port, tcmd.command);
327 andreas 196
 
331 andreas 197
                    if (!tcmd.nowait)
198
                        testSuccess(tcmd);
199
                    else
200
                    {
201
                        MSG_WARNING("Skipped result check!");
202
                        inform("   Result check skipped!");
203
                    }
204
 
327 andreas 205
                    tcmd.command.clear();
206
                    tcmd.result.clear();
207
                    tcmd.compare = false;
331 andreas 208
                    tcmd.nowait = false;
330 andreas 209
                    port = 1;
327 andreas 210
                }
211
 
212
                line++;
326 andreas 213
            }
214
 
215
            is.close();
216
        }
217
        catch (std::exception& e)
218
        {
219
            MSG_ERROR("Error on file " << *iter << ": " << e.what());
220
            continue;
221
        }
222
    }
223
 
224
    isRunning = false;
225
    delete this;
226
}
227
 
228
void _TestMode::inject(int port, const string& c)
229
{
325 andreas 230
    if (!gPageManager)
231
        return;
232
 
326 andreas 233
    gLastCommand = c;
325 andreas 234
    int channel = TConfig::getChannel();
235
    int system = TConfig::getSystem();
236
 
237
    amx::ANET_COMMAND cmd;
238
    cmd.MC = 0x000c;
239
    cmd.device1 = channel;
240
    cmd.port1 = port;
241
    cmd.system = system;
242
    cmd.data.message_string.system = system;
243
    cmd.data.message_string.device = channel;
244
    cmd.data.message_string.port = port;    // Must be the address port of button
326 andreas 245
    cmd.data.message_string.type = DTSZ_STRING;    // Char string
325 andreas 246
    cmd.data.message_string.length = min(c.length(), sizeof(cmd.data.message_string.content));
326 andreas 247
    memset(cmd.data.message_string.content, 0, sizeof(cmd.data.message_string.content));
248
    memcpy ((char *)cmd.data.message_string.content, c.c_str(), cmd.data.message_string.length);
325 andreas 249
    gPageManager->doCommand(cmd);
250
}
326 andreas 251
 
327 andreas 252
void _TestMode::testSuccess(_TESTCMD& tc)
326 andreas 253
{
254
    ulong total = 0;
255
 
256
    while (!__done && total < TIMEOUT)
257
    {
258
        usleep(DELAY);
259
        total += DELAY;
260
    }
261
 
262
    if (total >= TIMEOUT)
263
    {
264
        MSG_WARNING("Command \"" << gLastCommand << "\" timed out!");
265
#ifdef __ANDROID__
328 andreas 266
        __android_log_print(ANDROID_LOG_INFO, "tpanel", "Command \"%s\" timed out!", gLastCommand.c_str());
326 andreas 267
#else
268
        cout << "Command \"" << gLastCommand << "\" timed out!" << endl;
269
#endif
270
    }
271
 
327 andreas 272
    if (tc.compare && tc.result == verify)
273
    {
274
        MSG_WARNING("The result \"" << verify << "\" is equal to \"" << tc.result << "\"!");
275
        __success = true;
276
    }
277
    else if (tc.compare)
278
    {
279
        MSG_WARNING("The result \"" << verify << "\" does not match the expected result of \"" << tc.result << "\"!");
280
        __success = false;
281
    }
282
 
326 andreas 283
    if (__success)
284
    {
285
        MSG_INFO("SUCCESS (" << gLastCommand << ")");
286
#ifdef __ANDROID__
328 andreas 287
        __android_log_print(ANDROID_LOG_INFO, "tpanel", "SUCCESS (%s)", gLastCommand.c_str());
326 andreas 288
#else
289
        cout << "   SUCCESS (" << gLastCommand << ")" << endl;
290
#endif
291
    }
292
    else
293
    {
294
        MSG_ERROR("FAILED (" << gLastCommand << ")");
295
#ifdef __ANDROID__
328 andreas 296
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "FAILED (%s)", gLastCommand.c_str());
326 andreas 297
#else
298
        cout << "   FAILED (" << gLastCommand << ")" << endl;
299
#endif
300
    }
301
 
302
    __success = false;
303
    __done = false;
304
}
305
 
306
void _TestMode::inform(const string& msg)
307
{
308
    MSG_INFO(msg);
309
#ifdef __ANDROID__
310
    __android_log_print(ANDROID_LOG_ERROR, "tpanel", "%s", msg.c_str());
311
#else
312
    cout << msg << endl;
313
#endif
314
}