• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 7252848206

18 Dec 2023 07:26PM UTC coverage: 32.067% (+0.001%) from 32.066%
7252848206

push

github

mcallegari
Code style review #1427

199 of 628 new or added lines in 101 files covered. (31.69%)

8 existing lines in 2 files now uncovered.

15169 of 47304 relevant lines covered (32.07%)

23733.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.72
/engine/src/rgbscript.cpp
1
/*
2
  Q Light Controller
3
  rgbscript.cpp
4

5
  Copyright (c) Heikki Junnila
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#include <QXmlStreamReader>
21
#include <QXmlStreamWriter>
22
#include <QCoreApplication>
23
#include <QScriptEngine>
24
#include <QScriptValue>
25
#include <QStringList>
26
#include <QDebug>
27
#include <QFile>
28
#include <QSize>
29
#include <QDir>
30

31
#if defined(WIN32) || defined(Q_OS_WIN)
32
#   include <windows.h>
33
#else
34
#   include <unistd.h>
35
#endif
36

37
#include "rgbscript.h"
38
#include "rgbscriptscache.h"
39

40
QScriptEngine* RGBScript::s_engine = NULL;
41
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
42
  QMutex* RGBScript::s_engineMutex = NULL;
43
#else
44
  QRecursiveMutex* RGBScript::s_engineMutex = NULL;
45
#endif
46

47
/****************************************************************************
48
 * Initialization
49
 ****************************************************************************/
50

51
RGBScript::RGBScript(Doc * doc)
341✔
52
    : RGBAlgorithm(doc)
53
    , m_apiVersion(0)
341✔
54
{
55
}
341✔
56

57
RGBScript::RGBScript(const RGBScript& s)
69✔
58
    : RGBAlgorithm(s.doc())
59
    , m_fileName(s.m_fileName)
69✔
60
    , m_contents(s.m_contents)
69✔
61
    , m_apiVersion(0)
69✔
62
{
63
    evaluate();
69✔
64
    foreach (RGBScriptProperty cap, s.m_properties)
305✔
65
    {
66
        setProperty(cap.m_name, s.property(cap.m_name));
118✔
67
    }
68
}
69✔
69

70
RGBScript::~RGBScript()
91✔
71
{
72
}
91✔
73

74
RGBScript &RGBScript::operator=(const RGBScript &s)
1✔
75
{
76
    if (this != &s)
1✔
77
    {
78
        m_fileName = s.m_fileName;
1✔
79
        m_contents = s.m_contents;
1✔
80
        m_apiVersion = s.m_apiVersion;
1✔
81
        evaluate();
1✔
82
        foreach (RGBScriptProperty cap, s.m_properties)
3✔
83
        {
84
            setProperty(cap.m_name, s.property(cap.m_name));
1✔
85
        }
86
    }
87

88
    return *this;
1✔
89
}
90

91
bool RGBScript::operator==(const RGBScript& s) const
×
92
{
93
    if (this->fileName().isEmpty() == false && this->fileName() == s.fileName())
×
94
        return true;
×
95
    else
96
        return false;
×
97
}
98

99
RGBAlgorithm* RGBScript::clone() const
17✔
100
{
101
    RGBScript* script = new RGBScript(*this);
17✔
102
    return static_cast<RGBAlgorithm*> (script);
17✔
103
}
104

105
/****************************************************************************
106
 * Load & Evaluation
107
 ****************************************************************************/
108

109
bool RGBScript::load(const QDir& dir, const QString& fileName)
160✔
110
{
111
    // Create the script engine when it's first needed
112
    initEngine();
160✔
113

114
    QMutexLocker engineLocker(s_engineMutex);
320✔
115

116
    m_contents.clear();
160✔
117
    m_script = QScriptValue();
160✔
118
    m_rgbMap = QScriptValue();
160✔
119
    m_rgbMapStepCount = QScriptValue();
160✔
120
    m_apiVersion = 0;
160✔
121

122
    m_fileName = fileName;
160✔
123
    QFile file(dir.absoluteFilePath(m_fileName));
320✔
124
    if (file.open(QIODevice::ReadOnly) == false)
160✔
125
    {
126
        qWarning() << "Unable to load RGB script" << m_fileName << "from" << dir.absolutePath();
×
127
        return false;
×
128
    }
129

130
    QTextStream stream(&file);
320✔
131
    m_contents = stream.readAll();
160✔
132
    file.close();
160✔
133

134
    QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(m_contents);
320✔
135
    if (result.state() == QScriptSyntaxCheckResult::Valid)
160✔
136
        return evaluate();
160✔
137
    else
138
    {
139
        qWarning() << m_fileName << "Error at line:" << result.errorLineNumber()
×
140
                   << ", column:" << result.errorColumnNumber()
×
141
                   << ":" << result.errorMessage();
×
142
        return false;
×
143
    }
144
}
145

146
QString RGBScript::fileName() const
82✔
147
{
148
    return m_fileName;
82✔
149
}
150

151
bool RGBScript::evaluate()
234✔
152
{
153
    QMutexLocker engineLocker(s_engineMutex);
468✔
154

155
    m_rgbMap = QScriptValue();
234✔
156
    m_rgbMapStepCount = QScriptValue();
234✔
157
    m_apiVersion = 0;
234✔
158

159
    m_script = s_engine->evaluate(m_contents, m_fileName);
234✔
160
    if (s_engine->hasUncaughtException() == true)
234✔
161
    {
162
        QString msg("%1: %2");
1✔
163
        qWarning() << msg.arg(m_fileName).arg(s_engine->uncaughtException().toString());
1✔
164
        foreach (QString s, s_engine->uncaughtExceptionBacktrace())
2✔
165
            qDebug() << s;
×
166
        return false;
1✔
167
    }
168
    else
169
    {
170
        m_rgbMap = m_script.property("rgbMap");
233✔
171
        if (m_rgbMap.isFunction() == false)
233✔
172
        {
173
            qWarning() << m_fileName << "is missing the rgbMap() function!";
4✔
174
            return false;
4✔
175
        }
176

177
        m_rgbMapStepCount = m_script.property("rgbMapStepCount");
229✔
178
        if (m_rgbMapStepCount.isFunction() == false)
229✔
179
        {
180
            qWarning() << m_fileName << "is missing the rgbMapStepCount() function!";
1✔
181
            return false;
1✔
182
        }
183

184
        m_apiVersion = m_script.property("apiVersion").toInteger();
228✔
185
        if (m_apiVersion > 0)
228✔
186
        {
187
            if (m_apiVersion == 2)
227✔
188
                return loadProperties();
182✔
189
            return true;
45✔
190
        }
191
        else
192
        {
193
            qWarning() << m_fileName << "has an invalid apiVersion:" << m_apiVersion;
1✔
194
            return false;
1✔
195
        }
196
    }
197
}
198

199
void RGBScript::initEngine()
160✔
200
{
201
    if (s_engineMutex == NULL)
160✔
202
    {
203
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
204
        s_engineMutex = new QMutex(QMutex::Recursive);
205
#else
206
        s_engineMutex = new QRecursiveMutex();
3✔
207
#endif
208
        s_engine = new QScriptEngine(QCoreApplication::instance());
3✔
209
    }
210
    Q_ASSERT(s_engineMutex != NULL);
160✔
211
    Q_ASSERT(s_engine != NULL);
160✔
212
}
160✔
213

214
void RGBScript::displayError(QScriptValue e, const QString& fileName)
×
215
{
216
    if (e.isError()) 
×
217
    {
218
        QString msg("%1: Exception at line %2. Error: %3");
×
219
        qWarning() << msg.arg(fileName)
×
220
                         .arg(e.property("lineNumber").toInt32())
×
221
                         .arg(e.toString());
×
222
        qDebug() << "Stack: " << e.property("stack").toString();
×
223
    }
224
}
×
225

226
/****************************************************************************
227
 * Script API
228
 ****************************************************************************/
229

230
int RGBScript::rgbMapStepCount(const QSize& size)
54✔
231
{
232
    QMutexLocker engineLocker(s_engineMutex);
108✔
233

234
    if (m_rgbMapStepCount.isValid() == false)
54✔
235
        return -1;
1✔
236

237
    QScriptValueList args;
106✔
238
    args << size.width() << size.height();
53✔
239
    QScriptValue value = m_rgbMapStepCount.call(QScriptValue(), args);
106✔
240
    if (value.isError())
53✔
241
    {
242
        displayError(value, m_fileName);
×
243
        return -1;
×
244
    } 
245
    else 
246
    {
247
        int ret = value.isNumber() ? value.toInteger() : -1;
53✔
248
        return ret;
53✔
249
    }
250
}
251

252
void RGBScript::rgbMap(const QSize& size, uint rgb, int step, RGBMap &map)
2,562✔
253
{
254
    QMutexLocker engineLocker(s_engineMutex);
2,562✔
255

256
    if (m_rgbMap.isValid() == false)
2,562✔
257
        return;
1✔
258

259
    QScriptValueList args;
5,122✔
260
    args << size.width() << size.height() << rgb << step;
2,561✔
261
    QScriptValue yarray = m_rgbMap.call(QScriptValue(), args);
5,122✔
262

263
    if (yarray.isError())
2,561✔
264
        displayError(yarray, m_fileName);
×
265

266
    if (yarray.isArray())
2,561✔
267
    {
268
        int ylen = yarray.property("length").toInteger();
2,561✔
269
        map.resize(ylen);
2,561✔
270
        for (int y = 0; y < ylen && y < size.height(); y++)
32,485✔
271
        {
272
            QScriptValue xarray = yarray.property(QString::number(y));
59,848✔
273
            int xlen = xarray.property("length").toInteger();
29,924✔
274
            map[y].resize(xlen);
29,924✔
275
            for (int x = 0; x < xlen && x < size.width(); x++)
257,526✔
276
            {
277
                QScriptValue yx = xarray.property(QString::number(x));
227,602✔
278
                map[y][x] = yx.toInteger();
227,602✔
279
            }
280
        }
281
    }
282
    else
283
    {
284
        qWarning() << "Returned value is not an array within an array!";
×
285
    }
286
}
287

288
QString RGBScript::name() const
1,773✔
289
{
290
    QMutexLocker engineLocker(s_engineMutex);
3,546✔
291

292
    QScriptValue name = m_script.property("name");
3,546✔
293
    QString ret = name.isValid() ? name.toString() : QString();
1,773✔
294
    return ret;
3,546✔
295
}
296

297
QString RGBScript::author() const
42✔
298
{
299
    QMutexLocker engineLocker(s_engineMutex);
84✔
300

301
    QScriptValue author = m_script.property("author");
84✔
302
    QString ret = author.isValid() ? author.toString() : QString();
42✔
303
    return ret;
84✔
304
}
305

306
int RGBScript::apiVersion() const
127✔
307
{
308
    return m_apiVersion;
127✔
309
}
310

311
RGBAlgorithm::Type RGBScript::type() const
58✔
312
{
313
    return RGBAlgorithm::Script;
58✔
314
}
315

316
int RGBScript::acceptColors() const
47,640✔
317
{
318
    QMutexLocker engineLocker(s_engineMutex);
95,280✔
319

320
    QScriptValue accColors = m_script.property("acceptColors");
95,280✔
321
    if (accColors.isValid())
47,640✔
322
        return accColors.toInt32();
24,354✔
323
    // if no property is provided, let's assume the script
324
    // will accept both start and end colors
325
    return 2;
23,286✔
326
}
327

328
bool RGBScript::loadXML(QXmlStreamReader &root)
×
329
{
330
    Q_UNUSED(root)
331

332
    return false;
×
333
}
334

335
bool RGBScript::saveXML(QXmlStreamWriter *doc) const
1✔
336
{
337
    Q_ASSERT(doc != NULL);
1✔
338

339
    if (apiVersion() > 0 && name().isEmpty() == false)
1✔
340
    {
341
        doc->writeStartElement(KXMLQLCRGBAlgorithm);
1✔
342
        doc->writeAttribute(KXMLQLCRGBAlgorithmType, KXMLQLCRGBScript);
1✔
343
        doc->writeCharacters(name());
1✔
344
        doc->writeEndElement();
1✔
345
        return true;
1✔
346
    }
347
    else
348
    {
349
        return false;
×
350
    }
351
}
352

353
/************************************************************************
354
 * Capabilities
355
 ************************************************************************/
356

357
QList<RGBScriptProperty> RGBScript::properties()
123✔
358
{
359
    return m_properties;
123✔
360
}
361

362
QHash<QString, QString> RGBScript::propertiesAsStrings()
×
363
{
364
    QMutexLocker engineLocker(s_engineMutex);
×
365

366
    QHash<QString, QString> properties;
×
NEW
367
    foreach (RGBScriptProperty cap, m_properties)
×
368
    {
369
        QScriptValue readMethod = m_script.property(cap.m_readMethod);
×
370
        if (readMethod.isFunction())
×
371
        {
372
            QScriptValueList args;
×
373
            QScriptValue value = readMethod.call(QScriptValue(), args);
×
374
            if (value.isError())
×
375
            {
376
                displayError(value, m_fileName);
×
377
            }
378
            else if (value.isValid())
×
379
            {
380
                properties.insert(cap.m_name, value.toString());
×
381
            }
382
        }
383
    }
384
    return properties;
×
385
}
386

387
bool RGBScript::setProperty(QString propertyName, QString value)
768✔
388
{
389
    QMutexLocker engineLocker(s_engineMutex);
1,536✔
390

391
    foreach (RGBScriptProperty cap, m_properties)
4,676✔
392
    {
393
        if (cap.m_name == propertyName)
2,722✔
394
        {
395
            QScriptValue writeMethod = m_script.property(cap.m_writeMethod);
1,536✔
396
            if (writeMethod.isFunction() == false)
768✔
397
            {
398
                qWarning() << name() << "doesn't have a write function for" << propertyName;
×
399
                return false;
×
400
            }
401
            QScriptValueList args;
1,536✔
402
            args << value;
768✔
403
            QScriptValue written = writeMethod.call(QScriptValue(), args);
1,536✔
404
            if (written.isError())
768✔
405
            {
406
                displayError(written, m_fileName);
×
407
                return false;
×
408
            }
409
            else
410
            {
411
                return true;
768✔
412
            }
413
        }
414
    }
415
    return false;
×
416
}
417

418
QString RGBScript::property(QString propertyName) const
1,580✔
419
{
420
    QMutexLocker engineLocker(s_engineMutex);
3,160✔
421

422
    foreach (RGBScriptProperty cap, m_properties)
9,674✔
423
    {
424
        if (cap.m_name == propertyName)
5,626✔
425
        {
426
            QScriptValue readMethod = m_script.property(cap.m_readMethod);
3,158✔
427
            if (readMethod.isFunction() == false)
1,579✔
428
            {
429
                qWarning() << name() << "doesn't have a read function for" << propertyName;
×
430
                return QString();
×
431
            }
432
            QScriptValueList args;
3,158✔
433
            QScriptValue value = readMethod.call(QScriptValue(), args);
3,158✔
434
            if (value.isError())
1,579✔
435
            {
436
                displayError(value, m_fileName);
×
437
                return QString();
×
438
            }
439
            else if (value.isValid())
1,579✔
440
            {
441
                return value.toString();
1,579✔
442
            }
443
            else
444
            {
445
                return QString();
×
446
            }
447
        }
448
    }
449
    return QString();
1✔
450
}
451

452
bool RGBScript::loadProperties()
213✔
453
{
454
    QMutexLocker engineLocker(s_engineMutex);
426✔
455

456
    QScriptValue svCaps = m_script.property("properties");
426✔
457
    if (svCaps.isArray() == false)
213✔
458
    {
459
        qWarning() << m_fileName << "properties is not an array!";
×
460
        return false;
×
461
    }
462
    QVariant varCaps = svCaps.toVariant();
426✔
463
    if (varCaps.isValid() == false)
213✔
464
    {
465
        qWarning() << m_fileName << "has invalid properties!";
×
466
        return false;
×
467
    }
468

469
    m_properties.clear();
213✔
470

471
    QStringList slCaps = varCaps.toStringList();
213✔
472
    foreach (QString cap, slCaps)
1,371✔
473
    {
474
        RGBScriptProperty newCap;
1,158✔
475

476
        QStringList propsList = cap.split("|");
1,158✔
477
        foreach (QString prop, propsList)
7,527✔
478
        {
479
            QStringList keyValue = prop.split(":");
3,474✔
480
            if (keyValue.length() < 2)
3,474✔
481
            {
482
                qWarning() << prop << ": malformed property. Please fix it.";
×
483
                continue;
×
484
            }
485
            QString key = keyValue.at(0).simplified();
6,948✔
486
            QString value = keyValue.at(1);
6,948✔
487
            if (key == "name")
3,474✔
488
            {
489
                newCap.m_name = value;
579✔
490
            }
491
            else if (key == "type")
2,895✔
492
            {
493
                if (value == "list") newCap.m_type = RGBScriptProperty::List;
579✔
494
                else if (value == "integer") newCap.m_type = RGBScriptProperty::Integer;
216✔
495
                else if (value == "range") newCap.m_type = RGBScriptProperty::Range;
216✔
496
                else if (value == "string") newCap.m_type = RGBScriptProperty::String;
×
497
            }
498
            else if (key == "display")
2,316✔
499
            {
500
                newCap.m_displayName = value.simplified();
579✔
501
            }
502
            else if (key == "values")
1,737✔
503
            {
504
                QStringList values = value.split(",");
1,158✔
505
                switch(newCap.m_type)
579✔
506
                {
507
                    case RGBScriptProperty::List:
363✔
508
                        newCap.m_listValues = values;
363✔
509
                    break;
363✔
510
                    case RGBScriptProperty::Range:
216✔
511
                    {
512
                        if (values.length() < 2)
216✔
513
                        {
514
                            qWarning() << value << ": malformed property. A range should be defined as 'min,max'. Please fix it.";
×
515
                        }
516
                        else
517
                        {
518
                            newCap.m_rangeMinValue = values.at(0).toInt();
216✔
519
                            newCap.m_rangeMaxValue = values.at(1).toInt();
216✔
520
                        }
521
                    }
522
                    break;
216✔
523
                    default:
×
524
                        qWarning() << value << ": values cannot be applied before the 'type' property or on type:integer and type:string";
×
525
                    break;
×
526
                }
527
            }
528
            else if (key == "write")
1,158✔
529
            {
530
                newCap.m_writeMethod = value.simplified();
579✔
531
            }
532
            else if (key == "read")
579✔
533
            {
534
                newCap.m_readMethod = value.simplified();
579✔
535
            }
536
            else
537
            {
538
                qWarning() << value << ": unknown property!";
×
539
            }
540
        }
541

542
        if (newCap.m_name.isEmpty() == false &&
1,158✔
543
            newCap.m_type != RGBScriptProperty::None)
579✔
544
                m_properties.append(newCap);
579✔
545
    }
546

547
    return true;
213✔
548
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc