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
                {
165
                    tcmd.command = content;
326 andreas 166
                }
330 andreas 167
                else if (command == "port")
326 andreas 168
                {
330 andreas 169
                    int p = atoi(content.c_str());
326 andreas 170
 
330 andreas 171
                    if (p > 0)
172
                        port = p;
326 andreas 173
                }
330 andreas 174
                else if (command == "result")
326 andreas 175
                {
330 andreas 176
                    tcmd.result = content;
177
                }
178
                else if (command == "compare")
179
                {
180
                    tcmd.compare = isTrue(content);
181
                }
182
                else if (command == "wait")
183
                {
184
                    unsigned long ms = atol(content.c_str());
326 andreas 185
 
330 andreas 186
                    if (ms != 0)
187
                        std::this_thread::sleep_for(std::chrono::milliseconds(ms));
326 andreas 188
                }
330 andreas 189
                else if (command == "exec")
327 andreas 190
                {
191
                    if (tcmd.command.empty())
192
                    {
193
                       MSG_WARNING("Nothing to execute on line " << line << "!");
194
                       line++;
195
                       continue;
196
                    }
197
 
198
                    inform("Testing command: " + tcmd.command);
330 andreas 199
                    inject(port, tcmd.command);
327 andreas 200
                    testSuccess(tcmd);
201
 
202
                    tcmd.command.clear();
203
                    tcmd.result.clear();
204
                    tcmd.compare = false;
330 andreas 205
                    port = 1;
327 andreas 206
                }
207
 
208
                line++;
326 andreas 209
            }
210
 
211
            is.close();
212
        }
213
        catch (std::exception& e)
214
        {
215
            MSG_ERROR("Error on file " << *iter << ": " << e.what());
216
            continue;
217
        }
218
    }
219
 
220
    isRunning = false;
221
    delete this;
222
}
223
 
224
void _TestMode::inject(int port, const string& c)
225
{
325 andreas 226
    if (!gPageManager)
227
        return;
228
 
326 andreas 229
    gLastCommand = c;
325 andreas 230
    int channel = TConfig::getChannel();
231
    int system = TConfig::getSystem();
232
 
233
    amx::ANET_COMMAND cmd;
234
    cmd.MC = 0x000c;
235
    cmd.device1 = channel;
236
    cmd.port1 = port;
237
    cmd.system = system;
238
    cmd.data.message_string.system = system;
239
    cmd.data.message_string.device = channel;
240
    cmd.data.message_string.port = port;    // Must be the address port of button
326 andreas 241
    cmd.data.message_string.type = DTSZ_STRING;    // Char string
325 andreas 242
    cmd.data.message_string.length = min(c.length(), sizeof(cmd.data.message_string.content));
326 andreas 243
    memset(cmd.data.message_string.content, 0, sizeof(cmd.data.message_string.content));
244
    memcpy ((char *)cmd.data.message_string.content, c.c_str(), cmd.data.message_string.length);
325 andreas 245
    gPageManager->doCommand(cmd);
246
}
326 andreas 247
 
327 andreas 248
void _TestMode::testSuccess(_TESTCMD& tc)
326 andreas 249
{
250
    ulong total = 0;
251
 
252
    while (!__done && total < TIMEOUT)
253
    {
254
        usleep(DELAY);
255
        total += DELAY;
256
    }
257
 
258
    if (total >= TIMEOUT)
259
    {
260
        MSG_WARNING("Command \"" << gLastCommand << "\" timed out!");
261
#ifdef __ANDROID__
328 andreas 262
        __android_log_print(ANDROID_LOG_INFO, "tpanel", "Command \"%s\" timed out!", gLastCommand.c_str());
326 andreas 263
#else
264
        cout << "Command \"" << gLastCommand << "\" timed out!" << endl;
265
#endif
266
    }
267
 
327 andreas 268
    if (tc.compare && tc.result == verify)
269
    {
270
        MSG_WARNING("The result \"" << verify << "\" is equal to \"" << tc.result << "\"!");
271
        __success = true;
272
    }
273
    else if (tc.compare)
274
    {
275
        MSG_WARNING("The result \"" << verify << "\" does not match the expected result of \"" << tc.result << "\"!");
276
        __success = false;
277
    }
278
 
326 andreas 279
    if (__success)
280
    {
281
        MSG_INFO("SUCCESS (" << gLastCommand << ")");
282
#ifdef __ANDROID__
328 andreas 283
        __android_log_print(ANDROID_LOG_INFO, "tpanel", "SUCCESS (%s)", gLastCommand.c_str());
326 andreas 284
#else
285
        cout << "   SUCCESS (" << gLastCommand << ")" << endl;
286
#endif
287
    }
288
    else
289
    {
290
        MSG_ERROR("FAILED (" << gLastCommand << ")");
291
#ifdef __ANDROID__
328 andreas 292
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "FAILED (%s)", gLastCommand.c_str());
326 andreas 293
#else
294
        cout << "   FAILED (" << gLastCommand << ")" << endl;
295
#endif
296
    }
297
 
298
    __success = false;
299
    __done = false;
300
}
301
 
302
void _TestMode::inform(const string& msg)
303
{
304
    MSG_INFO(msg);
305
#ifdef __ANDROID__
306
    __android_log_print(ANDROID_LOG_ERROR, "tpanel", "%s", msg.c_str());
307
#else
308
    cout << msg << endl;
309
#endif
310
}