Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions src/mtconnect/entity/data_set.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,6 @@ namespace mtconnect::entity {
const TableCellValue &m_other;
};

/// @brief Compares to cells
/// @param other the other cell
bool same(const TableCell &other) const
{
const auto &ov = other.m_value;
return m_key == other.m_key && m_removed == other.m_removed &&
std::visit(SameValue(ov), m_value);
}

/// @brief compares two cell values
/// @param other the other cell value to compare
bool sameValue(const TableCell &other) const
Expand All @@ -117,6 +108,13 @@ namespace mtconnect::entity {
return std::visit(SameValue(ov), m_value);
}

/// @brief Compares to cells
/// @param other the other cell
bool same(const TableCell &other) const
{
return m_key == other.m_key && m_removed == other.m_removed && sameValue(other);
}

std::string m_key;
TableCellValue m_value;
bool m_removed {false};
Expand Down Expand Up @@ -280,7 +278,7 @@ namespace mtconnect::entity {
for (const auto &e1 : v)
{
const auto &e2 = oset.find(e1);
if (e2 == oset.end() || e2->sameValue(e1))
if (e2 == oset.end() || !e2->sameValue(e1))
return false;
}

Expand All @@ -297,7 +295,7 @@ namespace mtconnect::entity {
return std::holds_alternative<T>(m_other) && std::get<T>(m_other) == v;
}

const DataSetValue &m_other; //! the other data set value
const DataSetValue &m_other; //! the other data set value
};

inline bool DataSetEntry::same(const DataSetEntry &other) const
Expand Down
169 changes: 165 additions & 4 deletions test_package/table_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class TableTest : public testing::Test
void SetUp() override
{ // Create an agent with only 16 slots and 8 data items.
m_agentTestHelper = make_unique<AgentTestHelper>();
m_agentTestHelper->createAgent("/samples/data_set.xml", 8, 4, "1.6", 25);
m_agentTestHelper->createAgent("/samples/data_set.xml", 8, 4, "2.0", 25);
m_agentId = to_string(getCurrentTimeInSec());

m_checkpoint = nullptr;
Expand Down Expand Up @@ -323,7 +323,7 @@ TEST_F(TableTest, JsonCurrent)
"U=10.0}");

{
PARSE_JSON_RESPONSE("/current");
PARSE_JSON_RESPONSE("/LinuxCNC/current");

auto streams = doc.at("/MTConnectStreams/Streams/0/DeviceStream/ComponentStreams"_json_pointer);
ASSERT_EQ(4_S, streams.size());
Expand Down Expand Up @@ -379,7 +379,7 @@ TEST_F(TableTest, JsonCurrentText)
"G53.3={X=7.0 Y=8.0 Z=9 U=10.0}");

{
PARSE_JSON_RESPONSE("/current");
PARSE_JSON_RESPONSE("/LinuxCNC/current");

auto streams = doc.at("/MTConnectStreams/Streams/0/DeviceStream/ComponentStreams"_json_pointer);
ASSERT_EQ(4_S, streams.size());
Expand Down Expand Up @@ -501,7 +501,7 @@ TEST_F(TableTest, JsonDefinitionTest)
m_agentTestHelper->addAdapter();

{
PARSE_JSON_RESPONSE("/probe");
PARSE_JSON_RESPONSE("/LinuxCNC/probe");

auto devices = doc.at("/MTConnectDevices/Devices"_json_pointer);
auto device = devices.at(0).at("/Device"_json_pointer);
Expand Down Expand Up @@ -778,3 +778,164 @@ TEST_F(TableTest, shoud_parse_table_with_no_space)
nullptr);
}
}

TEST_F(TableTest, shoud_handle_complex_sequences)
{
m_agentTestHelper->addAdapter();

{
PARSE_XML_RESPONSE("/current");
ASSERT_XML_PATH_EQUAL(doc, "//m:DeviceStream//m:WorkOffsetTable[@dataItemId='wp1']",
"UNAVAILABLE");
ASSERT_XML_PATH_EQUAL(doc, "//m:DeviceStream//m:WorkOffsetTable[@dataItemId='wp1']@count", "0");
}

QueryMap query {{"path", "//DataItem[@id='wp1']"}};

m_agentTestHelper->m_adapter->processData(
"2021-02-01T12:00:00Z|wp1|A={X=1.0 Y=2.0 Z=3.0} B={X=4.0 Y=5.0 Z=6.0} C={X=7.0 Y=8.0 Z=9} "
"D={title=\"Testing\"}");

m_agentTestHelper->m_adapter->processData(
"2021-02-01T12:00:00Z|wp1|A= B C={X=107.0 Y=108.0 Z=109.0}");

m_agentTestHelper->m_adapter->processData("2021-02-01T12:00:00Z|wp1|A={X=101.0 Y=102.0 Z=103.0}");

ValueResponse s1, s2, s3;

{
PARSE_XML_RESPONSE_QUERY("/sample", query);
ASSERT_XML_PATH_EQUAL(doc, "//m:DeviceStream//m:WorkOffsetTable[1]", "UNAVAILABLE");

s1 = XML_PATH_VALUE(doc, "//m:DeviceStream//m:WorkOffsetTable[2]@sequence");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[2]/m:Entry[@key='A']/m:Cell[@key='X']", "1");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[2]/m:Entry[@key='A']/m:Cell[@key='Y']", "2");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[2]/m:Entry[@key='A']/m:Cell[@key='Z']", "3");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[2]/m:Entry[@key='B']/m:Cell[@key='X']", "4");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[2]/m:Entry[@key='B']/m:Cell[@key='Y']", "5");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[2]/m:Entry[@key='B']/m:Cell[@key='Z']", "6");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[2]/m:Entry[@key='C']/m:Cell[@key='X']", "7");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[2]/m:Entry[@key='C']/m:Cell[@key='Y']", "8");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[2]/m:Entry[@key='C']/m:Cell[@key='Z']", "9");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[2]/m:Entry[@key='D']/m:Cell[@key='title']",
"Testing");

s2 = XML_PATH_VALUE(doc, "//m:DeviceStream//m:WorkOffsetTable[3]@sequence");

ASSERT_XML_PATH_EQUAL(doc, "//m:DeviceStream//m:WorkOffsetTable[3]/m:Entry[@key='A']@removed",
"true");

ASSERT_XML_PATH_EQUAL(doc, "//m:DeviceStream//m:WorkOffsetTable[3]/m:Entry[@key='B']@removed",
"true");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[3]/m:Entry[@key='C']/m:Cell[@key='X']", "107");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[3]/m:Entry[@key='C']/m:Cell[@key='Y']", "108");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[3]/m:Entry[@key='C']/m:Cell[@key='Z']", "109");

s3 = XML_PATH_VALUE(doc, "//m:DeviceStream//m:WorkOffsetTable[4]@sequence");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[4]/m:Entry[@key='A']/m:Cell[@key='X']", "101");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[4]/m:Entry[@key='A']/m:Cell[@key='Y']", "102");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[4]/m:Entry[@key='A']/m:Cell[@key='Z']", "103");
}

ASSERT_TRUE(s1.first);
ASSERT_TRUE(s2.first);
ASSERT_TRUE(s3.first);

query.clear();
query["at"] = *s1.first;

{
PARSE_XML_RESPONSE_QUERY("/current", query);
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='A']/m:Cell[@key='X']", "1");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='A']/m:Cell[@key='Y']", "2");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='A']/m:Cell[@key='Z']", "3");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='B']/m:Cell[@key='X']", "4");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='B']/m:Cell[@key='Y']", "5");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='B']/m:Cell[@key='Z']", "6");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='C']/m:Cell[@key='X']", "7");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='C']/m:Cell[@key='Y']", "8");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='C']/m:Cell[@key='Z']", "9");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='D']/m:Cell[@key='title']",
"Testing");
}

query["at"] = *s2.first;

{
PARSE_XML_RESPONSE_QUERY("/current", query);
ASSERT_XML_PATH_EQUAL(doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='A']", nullptr);

ASSERT_XML_PATH_EQUAL(doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='B']", nullptr);

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='C']/m:Cell[@key='X']", "107");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='C']/m:Cell[@key='Y']", "108");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='C']/m:Cell[@key='Z']", "109");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='D']/m:Cell[@key='title']",
"Testing");
}

query["at"] = *s3.first;

{
PARSE_XML_RESPONSE_QUERY("/current", query);
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='A']/m:Cell[@key='X']", "101");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='A']/m:Cell[@key='Y']", "102");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='A']/m:Cell[@key='Z']", "103");

ASSERT_XML_PATH_EQUAL(doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='B']", nullptr);

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='C']/m:Cell[@key='X']", "107");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='C']/m:Cell[@key='Y']", "108");
ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='C']/m:Cell[@key='Z']", "109");

ASSERT_XML_PATH_EQUAL(
doc, "//m:DeviceStream//m:WorkOffsetTable[1]/m:Entry[@key='D']/m:Cell[@key='title']",
"Testing");
}
}
63 changes: 46 additions & 17 deletions test_package/test_utilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ inline void fillAttribute(std::string &xmlString, const std::string &attribute,
#define ASSERT_XML_PATH_EQUAL(doc, path, expected) \
xpathTest(doc, path, expected, __FILE__, __LINE__)

#define XML_PATH_VALUE(doc, path) xpathValue(doc, path, __FILE__, __LINE__)

#define PARSE_XML(expr) \
string result = expr; \
auto doc = xmlParseMemory(result.c_str(), static_cast<int>(result.length())); \
Expand Down Expand Up @@ -127,8 +129,10 @@ inline void assertIf(bool condition, const std::string &message, const std::stri
ASSERT_TRUE(condition) << file << "(" << line << "): Failed " << message;
}

inline void xpathTest(xmlDocPtr doc, const char *xpath, const char *expected,
const std::string &file, int line)
using ValueResponse = std::pair<std::optional<std::string>, std::optional<std::string>>;

inline ValueResponse xpathValue(xmlDocPtr doc, const char *xpath, const std::string &file, int line,
bool noValue = false)
{
using namespace std;

Expand Down Expand Up @@ -178,19 +182,20 @@ inline void xpathTest(xmlDocPtr doc, const char *xpath, const char *expected,
<< ((const char *)memory);
xmlFree(memory);

FAIL() << message.str();

if (obj)
xmlXPathFreeObject(obj);

xmlXPathFreeContext(xpathCtx);
return;

if (noValue)
return ValueResponse(std::nullopt, std::nullopt);
else
return ValueResponse(std::nullopt, message.str());
}

// Special case when no children are expected
xmlNodePtr first = obj->nodesetval->nodeTab[0];

if (expected == nullptr)
if (noValue)
{
bool has_content = false;
stringstream message;
Expand Down Expand Up @@ -228,8 +233,10 @@ inline void xpathTest(xmlDocPtr doc, const char *xpath, const char *expected,
xmlXPathFreeObject(obj);
xmlXPathFreeContext(xpathCtx);

failIf(has_content, message.str(), file, line);
return;
if (has_content)
return ValueResponse(std::nullopt, message.str());
else
return ValueResponse(std::nullopt, std::nullopt);
}

string actual;
Expand Down Expand Up @@ -275,16 +282,38 @@ inline void xpathTest(xmlDocPtr doc, const char *xpath, const char *expected,
xmlXPathFreeContext(xpathCtx);

actual = mtconnect::trim(actual);
string message = (string) "Incorrect value for path " + xpath;

if (expected[0] != '!')
{
failNotEqualIf(actual != expected, expected, actual, message, file, line);
}
else
return ValueResponse(actual, std::nullopt);
}

inline void xpathTest(xmlDocPtr doc, const char *xpath, const char *expected,
const std::string &file, int line)
{
using namespace std;

auto res = xpathValue(doc, xpath, file, line, expected == nullptr);
if (res.second)
ADD_FAILURE_AT(file.c_str(), line) << *res.second;
else if (expected != nullptr)
{
expected += 1;
failNotEqualIf(actual == expected, expected, actual, message, file, line);
if (res.first)
{
string message = (string) "Incorrect value for path " + xpath;
auto actual = *res.first;
if (expected[0] != '!')
{
failNotEqualIf(actual != expected, expected, actual, message, file, line);
}
else
{
expected += 1;
failNotEqualIf(actual == expected, expected, actual, message, file, line);
}
}
else
{
ADD_FAILURE_AT(file.c_str(), line) << "No value for " << xpath;
}
}
}

Expand Down