##// END OF EJS Templates
Parser refactoring (3)...
Alexandre Leroux -
r987:49134789d5f3
parent child
Show More
@@ -0,0 +1,35
1 #ifndef SCIQLOP_AMDARESULTPARSERDEFS_H
2 #define SCIQLOP_AMDARESULTPARSERDEFS_H
3
4 #include <QtCore/QRegularExpression>
5 #include <QtCore/QString>
6 #include <QtCore/QVariantHash>
7
8 // ////////// //
9 // Properties //
10 // ////////// //
11
12 /// Alias to represent properties read in the header of AMDA file
13 using Properties = QVariantHash;
14
15 extern const QString X_AXIS_UNIT_PROPERTY;
16
17 // /////////////////// //
18 // Regular expressions //
19 // /////////////////// //
20
21 // AMDA V2
22 // /// ... PARAMETER_UNITS : nT ...
23 // /// ... PARAMETER_UNITS:nT ...
24 // /// ... PARAMETER_UNITS: mΒ² ...
25 // /// ... PARAMETER_UNITS : m/s ...
26 // const auto UNIT_REGEX = QRegularExpression{QStringLiteral("\\s*PARAMETER_UNITS\\s*:\\s*(.+)")};
27
28 /// Regex to find x-axis unit in a line. Examples of valid lines:
29 /// ... - Units : nT - ...
30 /// ... -Units:nT- ...
31 /// ... -Units: mΒ²- ...
32 /// ... - Units : m/s - ...
33 extern const QRegularExpression DEFAULT_X_AXIS_UNIT_REGEX;
34
35 #endif // SCIQLOP_AMDARESULTPARSERDEFS_H
@@ -0,0 +1,6
1 #include "AmdaResultParserDefs.h"
2
3 const QString X_AXIS_UNIT_PROPERTY = QStringLiteral("xAxisUnit");
4
5 const QRegularExpression DEFAULT_X_AXIS_UNIT_REGEX
6 = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
@@ -1,23 +1,27
1 1 #ifndef SCIQLOP_UNIT_H
2 2 #define SCIQLOP_UNIT_H
3 3
4 #include <Common/MetaTypes.h>
5
4 6 #include <QString>
5 7 #include <tuple>
6 8
7 9 struct Unit {
8 10 explicit Unit(const QString &name = {}, bool timeUnit = false)
9 11 : m_Name{name}, m_TimeUnit{timeUnit}
10 12 {
11 13 }
12 14
13 15 inline bool operator==(const Unit &other) const
14 16 {
15 17 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
16 18 }
17 19 inline bool operator!=(const Unit &other) const { return !(*this == other); }
18 20
19 21 QString m_Name; ///< Unit name
20 22 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
21 23 };
22 24
25 SCIQLOP_REGISTER_META_TYPE(UNIT_REGISTRY, Unit)
26
23 27 #endif // SCIQLOP_UNIT_H
@@ -1,151 +1,150
1 1 #include <Data/ArrayData.h>
2 2 #include <Data/OptionalAxis.h>
3 3
4 4 #include <QObject>
5 5 #include <QtTest>
6 6
7 7 Q_DECLARE_METATYPE(OptionalAxis)
8 Q_DECLARE_METATYPE(Unit)
9 8
10 9 class TestOptionalAxis : public QObject {
11 10 Q_OBJECT
12 11
13 12 private slots:
14 13 /// Tests the creation of a undefined axis
15 14 void testNotDefinedAxisCtor();
16 15
17 16 /// Tests the creation of a undefined axis
18 17 void testDefinedAxisCtor_data();
19 18 void testDefinedAxisCtor();
20 19
21 20 /// Tests @sa OptionalAxis::at() method
22 21 void testAt_data();
23 22 void testAt();
24 23
25 24 /// Tests @sa OptionalAxis::size() method
26 25 void testSize_data();
27 26 void testSize();
28 27
29 28 /// Tests @sa OptionalAxis::unit() method
30 29 void testUnit_data();
31 30 void testUnit();
32 31 };
33 32
34 33 void TestOptionalAxis::testNotDefinedAxisCtor()
35 34 {
36 35 OptionalAxis notDefinedAxis{};
37 36 QVERIFY(!notDefinedAxis.isDefined());
38 37 }
39 38
40 39 void TestOptionalAxis::testDefinedAxisCtor_data()
41 40 {
42 41 QTest::addColumn<bool>("noData"); // If set to true, nullptr is passed as data of the axis
43 42 QTest::addColumn<std::vector<double> >(
44 43 "data"); // Values assigned to the axis when 'noData' flag is set to false
45 44 QTest::addColumn<Unit>("unit"); // Unit assigned to the axis
46 45
47 46 QTest::newRow("validData") << false << std::vector<double>{1, 2, 3} << Unit{"Hz"};
48 47 QTest::newRow("invalidData") << true << std::vector<double>{} << Unit{"Hz"};
49 48 }
50 49
51 50 void TestOptionalAxis::testDefinedAxisCtor()
52 51 {
53 52 QFETCH(bool, noData);
54 53 QFETCH(Unit, unit);
55 54
56 55 // When there is no data, we expect that the constructor returns exception
57 56 if (noData) {
58 57 QVERIFY_EXCEPTION_THROWN(OptionalAxis(nullptr, unit), std::invalid_argument);
59 58 }
60 59 else {
61 60 QFETCH(std::vector<double>, data);
62 61
63 62 OptionalAxis axis{std::make_shared<ArrayData<1> >(data), unit};
64 63 QVERIFY(axis.isDefined());
65 64 }
66 65 }
67 66
68 67 void TestOptionalAxis::testAt_data()
69 68 {
70 69 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
71 70 QTest::addColumn<int>("index"); // Index to test in the axis
72 71 QTest::addColumn<double>("expectedValue"); // Expected axis value for the index
73 72
74 73 OptionalAxis definedAxis{std::make_shared<ArrayData<1> >(std::vector<double>{1, 2, 3}),
75 74 Unit{"Hz"}};
76 75
77 76 QTest::newRow("data1") << definedAxis << 0 << 1.;
78 77 QTest::newRow("data2") << definedAxis << 1 << 2.;
79 78 QTest::newRow("data3") << definedAxis << 2 << 3.;
80 79 QTest::newRow("data4 (index out of bounds)")
81 80 << definedAxis << 3
82 81 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for out of bounds index
83 82 QTest::newRow("data5 (index out of bounds)")
84 83 << definedAxis << -1
85 84 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for out of bounds index
86 85 QTest::newRow("data6 (axis not defined)")
87 86 << OptionalAxis{} << 0
88 87 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for undefined axis
89 88 }
90 89
91 90 void TestOptionalAxis::testAt()
92 91 {
93 92 QFETCH(OptionalAxis, axis);
94 93 QFETCH(int, index);
95 94 QFETCH(double, expectedValue);
96 95
97 96 auto value = axis.at(index);
98 97 QVERIFY((std::isnan(value) && std::isnan(expectedValue)) || value == expectedValue);
99 98 }
100 99
101 100 void TestOptionalAxis::testSize_data()
102 101 {
103 102 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
104 103 QTest::addColumn<int>("expectedSize"); // Expected number of data in the axis
105 104
106 105 // Lambda that creates default defined axis (with the values passed in parameter)
107 106 auto axis = [](std::vector<double> values) {
108 107 return OptionalAxis{std::make_shared<ArrayData<1> >(std::move(values)), Unit{"Hz"}};
109 108 };
110 109
111 110 QTest::newRow("data1") << axis({}) << 0;
112 111 QTest::newRow("data2") << axis({1, 2, 3}) << 3;
113 112 QTest::newRow("data3") << axis({1, 2, 3, 4}) << 4;
114 113 QTest::newRow("data4 (axis not defined)") << OptionalAxis{}
115 114 << 0; // Expects 0 for undefined axis
116 115 }
117 116
118 117 void TestOptionalAxis::testSize()
119 118 {
120 119 QFETCH(OptionalAxis, axis);
121 120 QFETCH(int, expectedSize);
122 121
123 122 QCOMPARE(axis.size(), expectedSize);
124 123 }
125 124
126 125 void TestOptionalAxis::testUnit_data()
127 126 {
128 127 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
129 128 QTest::addColumn<Unit>("expectedUnit"); // Expected unit for the axis
130 129
131 130 // Lambda that creates default defined axis (with the unit passed in parameter)
132 131 auto axis = [](Unit unit) {
133 132 return OptionalAxis{std::make_shared<ArrayData<1> >(std::vector<double>{1, 2, 3}), unit};
134 133 };
135 134
136 135 QTest::newRow("data1") << axis(Unit{"Hz"}) << Unit{"Hz"};
137 136 QTest::newRow("data2") << axis(Unit{"t", true}) << Unit{"t", true};
138 137 QTest::newRow("data3 (axis not defined)") << OptionalAxis{}
139 138 << Unit{}; // Expects default unit for undefined axis
140 139 }
141 140
142 141 void TestOptionalAxis::testUnit()
143 142 {
144 143 QFETCH(OptionalAxis, axis);
145 144 QFETCH(Unit, expectedUnit);
146 145
147 146 QCOMPARE(axis.unit(), expectedUnit);
148 147 }
149 148
150 149 QTEST_MAIN(TestOptionalAxis)
151 150 #include "TestOptionalAxis.moc"
@@ -1,68 +1,76
1 1 #ifndef SCIQLOP_AMDARESULTPARSERHELPER_H
2 2 #define SCIQLOP_AMDARESULTPARSERHELPER_H
3 3
4 #include "AmdaResultParserDefs.h"
5
4 6 #include <QtCore/QLoggingCategory>
5 7 #include <QtCore/QString>
6 8
7 9 #include <memory>
8 10
9 11 class IDataSeries;
10 12
11 13 Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParserHelper)
12 14
13 15 /**
14 16 * Helper used to interpret the data of an AMDA result file and generate the corresponding data
15 17 * series.
16 18 *
17 19 * It proposes methods allowing to read line by line an AMDA file and to extract the properties
18 20 * (from the header) and the values corresponding to the data series
19 21 *
20 22 * @sa DataSeries
21 23 */
22 24 struct IAmdaResultParserHelper {
23 25 virtual ~IAmdaResultParserHelper() noexcept = default;
24 26
25 27 /// Verifies that the extracted properties are well formed and possibly applies other treatments
26 28 /// on them
27 29 /// @return true if the properties are well formed, false otherwise
28 30 virtual bool checkProperties() = 0;
29 31
30 32 /// Creates the data series from the properties and values extracted from the AMDA file.
31 33 /// @warning as the data are moved in the data series, the helper shouldn't be used after
32 34 /// calling this method
33 35 /// @return the data series created
34 36 virtual std::shared_ptr<IDataSeries> createSeries() = 0;
35 37
36 38 /// Reads a line from the AMDA file to extract a property that will be used to generate the data
37 39 /// series
38 40 /// @param line tahe line to interpret
39 41 virtual void readPropertyLine(const QString &line) = 0;
40 42
41 43 /// Reads a line from the AMDA file to extract a value that will be set in the data series
42 44 /// @param line the line to interpret
43 45 virtual void readResultLine(const QString &line) = 0;
44 46 };
45 47
46 48 /**
47 49 * Implementation of @sa IAmdaResultParserHelper for scalars
48 50 */
49 51 class ScalarParserHelper : public IAmdaResultParserHelper {
50 52 public:
51 53 bool checkProperties() override;
52 54 std::shared_ptr<IDataSeries> createSeries() override;
53 55 void readPropertyLine(const QString &line) override;
54 56 void readResultLine(const QString &line) override;
57
58 private:
59 Properties m_Properties{};
55 60 };
56 61
57 62 /**
58 63 * Implementation of @sa IAmdaResultParserHelper for vectors
59 64 */
60 65 class VectorParserHelper : public IAmdaResultParserHelper {
61 66 public:
62 67 bool checkProperties() override;
63 68 std::shared_ptr<IDataSeries> createSeries() override;
64 69 void readPropertyLine(const QString &line) override;
65 70 void readResultLine(const QString &line) override;
71
72 private:
73 Properties m_Properties{};
66 74 };
67 75
68 76 #endif // SCIQLOP_AMDARESULTPARSERHELPER_H
@@ -1,74 +1,75
1 1
2 2 amdaplugin_moc_headers = [
3 3 'include/AmdaPlugin.h',
4 4 'include/AmdaProvider.h'
5 5 ]
6 6
7 7 amdaplugin_sources = [
8 8 'src/AmdaDefs.cpp',
9 9 'src/AmdaParser.cpp',
10 10 'src/AmdaPlugin.cpp',
11 11 'src/AmdaProvider.cpp',
12 12 'src/AmdaResultParser.cpp'
13 'src/AmdaResultParserDefs.cpp'
13 14 'src/AmdaResultParserHelper.cpp'
14 15 ]
15 16
16 17 amdaplugin_ui_files = []
17 18 amdaplugin_resources_files = [
18 19 'resources/amdaresources.qrc'
19 20 ]
20 21
21 22 amdaplugin_inc = include_directories(['include', '../../plugin/include'])
22 23
23 24 moc_gen = generator(moc,
24 25 output : 'moc_@BASENAME@.cpp',
25 26 arguments : ['@INPUT@',
26 27 '-DPLUGIN_JSON_FILE_PATH="'+meson.source_root()+'/plugins/amda/resources/amda.json"',
27 28 '-I', meson.current_source_dir()+'/include',
28 29 '-I', meson.current_source_dir()+'/../../plugin/include',
29 30 '-o', '@OUTPUT@'])
30 31
31 32 rcc_gen = generator(rcc,
32 33 output : 'qrc_@BASENAME@.cpp',
33 34 arguments : ['--name=@BASENAME@"',
34 35 '--output',
35 36 '@OUTPUT@',
36 37 '@INPUT@'])
37 38
38 39 amdaplugin_moc_plugin_files = moc_gen.process(amdaplugin_moc_headers)
39 40
40 41 amdaplugin_rcc_plugin_files = rcc_gen.process(amdaplugin_resources_files)
41 42
42 43 #amdaplugin_rcc_plugin_files = qt5.preprocess(
43 44 # qresources : amdaplugin_resources_files)
44 45
45 46 amdaplugin_moc_files = qt5.preprocess(
46 47 ui_files : amdaplugin_ui_files)
47 48
48 49 sciqlop_amdaplugin = library('amdaplugin',
49 50 amdaplugin_sources,
50 51 amdaplugin_moc_files,
51 52 amdaplugin_rcc_plugin_files,
52 53 amdaplugin_moc_plugin_files,
53 54 cpp_args : ['-DAMDA_LIB','-DQT_PLUGIN'],
54 55 include_directories : [amdaplugin_inc],
55 56 dependencies : [sciqlop_core, sciqlop_gui],
56 57 install : true
57 58 )
58 59
59 60
60 61 tests = [
61 62 [['tests/TestAmdaParser.cpp'],'test_amda_parser','AMDA parser test'],
62 63 [['tests/TestAmdaResultParser.cpp'],'test_amda_result_parser','AMDA result parser test'],
63 64 [['tests/TestAmdaAcquisition.cpp'],'test_amda_acquisition','AMDA Acquisition test']
64 65 ]
65 66
66 67 foreach unit_test : tests
67 68 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
68 69 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
69 70 link_with : [sciqlop_amdaplugin],
70 71 include_directories : [amdaplugin_inc],
71 72 cpp_args : ['-DAMDA_TESTS_RESOURCES_DIR="'+meson.current_source_dir()+'/tests-resources"'],
72 73 dependencies : [sciqlop_core, sciqlop_gui, qt5test])
73 74 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])], timeout: 3 * 60)
74 75 endforeach
@@ -1,51 +1,120
1 1 #include "AmdaResultParserHelper.h"
2 2
3 #include <Data/Unit.h>
4
3 5 Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper")
4 6
7 namespace {
8
9 // /////// //
10 // Methods //
11 // /////// //
12
13 /**
14 * Checks that the properties contain a specific unit and that this unit is valid
15 * @param properties the properties map in which to search unit
16 * @param key the key to search for the unit in the properties
17 * @param errorMessage the error message to log in case the unit is invalid
18 * @return true if the unit is valid, false it it's invalid or was not found in the properties
19 */
20 bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage)
21 {
22 auto unit = properties.value(key).value<Unit>();
23 if (unit.m_Name.isEmpty()) {
24 qCWarning(LOG_AmdaResultParserHelper()) << errorMessage;
25 return false;
26 }
27
28 return true;
29 }
30
31 /**
32 * Reads a line from the AMDA file and tries to extract a property from it
33 * @param properties the properties map in which to put the property extracted from the line
34 * @param key the key to which the property is added in the properties map
35 * @param line the line to read to extract the property
36 * @param regex the expected regex to extract the property. If the line matches this regex, the
37 * property is generated
38 * @param fun the function used to generate the property
39 * @return true if the property could be generated, false if the line does not match the regex, or
40 * if a property has already been generated for the key
41 */
42 template <typename GeneratePropertyFun>
43 bool tryReadProperty(Properties &properties, const QString &key, const QString &line,
44 const QRegularExpression &regex, GeneratePropertyFun fun)
45 {
46 if (properties.contains(key)) {
47 return false;
48 }
49
50 auto match = regex.match(line);
51 if (match.hasMatch()) {
52 properties.insert(key, fun(match));
53 }
54
55 return match.hasMatch();
56 }
57
58 /**
59 * Reads a line from the AMDA file and tries to extract a unit from it
60 * @sa tryReadProperty()
61 */
62 bool tryReadUnit(Properties &properties, const QString &key, const QString &line,
63 const QRegularExpression &regex, bool timeUnit = false)
64 {
65 return tryReadProperty(properties, key, line, regex, [timeUnit](const auto &match) {
66 return QVariant::fromValue(Unit{match.captured(1), timeUnit});
67 });
68 }
69
70 } // namespace
71
5 72 // ////////////////// //
6 73 // ScalarParserHelper //
7 74 // ////////////////// //
8 75
9 76 bool ScalarParserHelper::checkProperties()
10 77 {
11 /// @todo ALX
78 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
79 QObject::tr("The x-axis unit could not be found in the file"));
12 80 }
13 81
14 82 std::shared_ptr<IDataSeries> ScalarParserHelper::createSeries()
15 83 {
16 84 /// @todo ALX
17 85 }
18 86
19 87 void ScalarParserHelper::readPropertyLine(const QString &line)
20 88 {
21 /// @todo ALX
89 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
22 90 }
23 91
24 92 void ScalarParserHelper::readResultLine(const QString &line)
25 93 {
26 94 /// @todo ALX
27 95 }
28 96
29 97 // ////////////////// //
30 98 // VectorParserHelper //
31 99 // ////////////////// //
32 100
33 101 bool VectorParserHelper::checkProperties()
34 102 {
35 /// @todo ALX
103 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
104 QObject::tr("The x-axis unit could not be found in the file"));
36 105 }
37 106
38 107 std::shared_ptr<IDataSeries> VectorParserHelper::createSeries()
39 108 {
40 109 /// @todo ALX
41 110 }
42 111
43 112 void VectorParserHelper::readPropertyLine(const QString &line)
44 113 {
45 /// @todo ALX
114 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
46 115 }
47 116
48 117 void VectorParserHelper::readResultLine(const QString &line)
49 118 {
50 119 /// @todo ALX
51 120 }
General Comments 0
You need to be logged in to leave comments. Login now