Merge pull request #44 from smartergridsolutions/feature/PR-665

Add tests for glue generator as preliminary for for adding support for DNP3
This commit is contained in:
Thiago Alves 2019-04-15 11:04:43 -05:00 committed by GitHub
commit 14b6ad3928
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 15239 additions and 70 deletions

11
.gitignore vendored
View File

@ -7,5 +7,12 @@ public/
*.pyc
# IDE files for Visual Studio Code
*.vscode/
# IDE files for Visual Studio Code and Visual Studio
CMakeSettings.json
*.vscode/
*.vs/
# Build directories
webserver/bin
webserver/build
obj/

View File

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.0.0)
# CMake build for OpenPLC glue generator. The glue generator takes
# generated C code from the MATIEC compiler and then generates necessary
# glue to bind the MATIEC code with the OpenPLC runtime.
project(openplc_gluegenerator)
option(WERROR "Set all warnings to errors" ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
# Optional build capabilities
option(OPLCGLUE_ALL "Build all optional projects (tests)" ON)
option(OPLCGLUE_TEST "Build tests" OFF)
if(OPLCGLUE_ALL)
message("enabling all optional components")
set(OPLCGLUE_TEST ON)
endif()
# The main application that we always build
add_executable(glue_generator glue_generator.cpp)
#if(OPLCGLUE_TEST)
add_executable(glue_generator_test ./test/glue_generator_test.cpp)
#endif()

View File

@ -1,20 +1,16 @@
#include <iostream>
#include <fstream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <cstring>
#include <cstdlib>
using namespace std;
ifstream locatedVars;
ofstream glueVars;
void generateHeader()
/// Write the header to the output stream. The header is common among all glueVars files.
/// @param glueVars The output stream to write to.
void generateHeader(ostream& glueVars)
{
if (glueVars.is_open())
{
glueVars << "\
glueVars << "\
//-----------------------------------------------------------------------------\r\n\
// Copyright 2015 Thiago Alves\r\n\
// This file is part of the OpenPLC Software Stack.\r\n\
@ -78,58 +74,41 @@ IEC_LINT *special_functions[BUFFER_SIZE];\r\n\
\r\n\
void glueVars()\r\n\
{\r\n";
}
else
{
cout << "Error opening glueVars.cpp file!" << endl;
}
}
int parseIecVars(char *varName, char *varType)
int parseIecVars(istream& locatedVars, char *varName, char *varType)
{
string line;
char buffer[1024];
if (locatedVars.is_open())
{
if (getline(locatedVars, line))
{
int i = 0, j = 0;
strncpy(buffer, line.c_str(), 1024);
for (i = 0; buffer[i] != '('; i++);
i++;
if (getline(locatedVars, line))
{
int i = 0, j = 0;
strncpy(buffer, line.c_str(), 1024);
for (i = 0; buffer[i] != '('; i++);
i++;
while (buffer[i] != ',')
{
varType[j] = buffer[i];
i++; j++;
varType[j] = '\0';
}
i++; j=0;
while (buffer[i] != ',')
{
varType[j] = buffer[i];
i++; j++;
varType[j] = '\0';
}
i++; j=0;
while (buffer[i] != ',')
{
varName[j] = buffer[i];
i++; j++;
varName[j] = '\0';
}
while (buffer[i] != ',')
{
varName[j] = buffer[i];
i++; j++;
varName[j] = '\0';
}
return 1;
}
else
{
return 0;
}
}
else
{
cout << "Error opening located variables file!" << endl;
return 0;
}
return 1;
}
else
{
return 0;
}
}
void findPositions(char *varName, int *pos1, int *pos2)
@ -162,7 +141,7 @@ void findPositions(char *varName, int *pos1, int *pos2)
*pos2 = atoi(tempBuffer);
}
void glueVar(char *varName, char *varType)
void glueVar(ostream& glueVars, char *varName, char *varType)
{
cout << "varName: " << varName << "\tvarType: " << varType << endl;
int pos1, pos2;
@ -198,7 +177,7 @@ void glueVar(char *varName, char *varType)
case 'X':
glueVars << "\tbool_output[" << pos1 << "][" << pos2 << "] = " << varName << ";\r\n";
break;
case 'B':
case 'B':
glueVars << "\tbyte_output[" << pos1 << "] = " << varName << ";\r\n";
break;
case 'W':
@ -227,7 +206,7 @@ void glueVar(char *varName, char *varType)
}
}
void generateBottom()
void generateBottom(ostream& glueVars)
{
glueVars << "}\r\n\
\r\n\
@ -243,21 +222,66 @@ void updateTime()\r\n\
}";
}
int main()
void generateBody(istream& locatedVars, ostream& glueVars) {
// Start the generation process.
char iecVar_name[100];
char iecVar_type[100];
while (parseIecVars(locatedVars, iecVar_name, iecVar_type))
{
glueVar(glueVars, iecVar_name, iecVar_type);
}
}
/// This is our main function. We define it with a different name and then
/// call it from the main function so that we can mock it for the purpose
/// of testing.
int mainImpl(int argc, char *argv[])
{
char iecVar_name[100];
char iecVar_type[100];
locatedVars.open("LOCATED_VARIABLES.h", ios::in);
glueVars.open("glueVars.cpp", ios::trunc);
generateHeader();
while (parseIecVars(iecVar_name, iecVar_type))
{
glueVar(iecVar_name, iecVar_type);
// Parse the command line arguments - if they exist. Show the help if there are too many arguments
// or if the first argument is for help.
bool show_help = argc >= 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0);
if (show_help || (argc != 1 && argc != 3)) {
cout << "Usage " << endl << endl;
cout << " glue_generator [options] <path-to-located-variables.h> <path-to-glue-vars.cpp>" << endl << endl;
cout << "Reads the LOCATED_VARIABLES.h file generated by the MATIEC compiler and produces" << endl;
cout << "glueVars.cpp for the OpenPLC runtime. If not specified, paths are relative to" << endl;
cout << "the current directory." << endl << endl;
cout << "Options" << endl;
cout << " --help,-h = Print usage information and exit." << endl;
return 0;
}
generateBottom();
// If we have 3 arguments, then the user provided input and output paths
string input_file_name("LOCATED_VARIABLES.h");
string output_file_name("glueVars.cpp");
if (argc == 3) {
input_file_name = argv[1];
output_file_name = argv[2];
}
// Try to open the files for reading and writing.
ifstream locatedVars(input_file_name, ios::in);
if (!locatedVars.is_open()) {
cout << "Error opening located variables file at " << input_file_name << endl;
return 1;
}
ofstream glueVars(output_file_name, ios::trunc);
if (!glueVars.is_open()) {
cout << "Error opening glue variables file at " << output_file_name << endl;
return 2;
}
generateHeader(glueVars);
generateBody(locatedVars, glueVars);
generateBottom(glueVars);
return 0;
}
// For testing, we need to allow omitting the main function defined here.
#ifndef OPLCGLUE_OMIT_MAIN
int main(int argc, char *argv[]) {
return mainImpl(argc, argv);
}
#endif // OPLCGLUE_OMIT_MAIN

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,115 @@
// Catch2 will provide a main() function
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include <sstream>
// Our application is defined in a single CPP file, so to write tests against it
// we need to include it directly here. That's ok so long as the application is
// no more than one CPP file.
#define OPLCGLUE_OMIT_MAIN
#include "../glue_generator.cpp"
using namespace std;
SCENARIO("Commmand line", "[main]") {
GIVEN("<no pre-conditions>") {
WHEN("-h command line arguments") {
char* args[2] = { "glue_generator", "-h" };
REQUIRE(mainImpl(2, args) == 0);
}
WHEN("--help command line arguments") {
char* args[2] = { "glue_generator", "-h" };
REQUIRE(mainImpl(2, args) == 0);
}
}
}
SCENARIO("", "") {
GIVEN("IO as streams") {
std::stringstream output_stream;
WHEN("Contains single BOOL at %IX0") {
std::stringstream input_stream("__LOCATED_VAR(BOOL,__IX0,I,X,0)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tbool_input[0][0] = __IX0;\r\n");
}
WHEN("Contains single BOOL at %QX0") {
std::stringstream input_stream("__LOCATED_VAR(BOOL,__QX0,Q,X,0)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tbool_output[0][0] = __QX0;\r\n");
}
WHEN("Contains single BYTE at %IB0") {
std::stringstream input_stream("__LOCATED_VAR(BYTE,__IB0,I,B,0)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tbyte_input[0] = __IB0;\r\n");
}
WHEN("Contains single SINT at %IB1") {
std::stringstream input_stream("__LOCATED_VAR(SINT,__IB1,I,B,1)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tbyte_input[1] = __IB1;\r\n");
}
WHEN("Contains single SINT at %QB1") {
std::stringstream input_stream("__LOCATED_VAR(SINT,__QB1,Q,B,1)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tbyte_output[1] = __QB1;\r\n");
}
WHEN("Contains single USINT at %IB2") {
std::stringstream input_stream("__LOCATED_VAR(USINT,__IB2,I,B,2)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tbyte_input[2] = __IB2;\r\n");
}
WHEN("Contains single WORD at %IW0") {
std::stringstream input_stream("__LOCATED_VAR(WORD,__IW0,I,W,0)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tint_input[0] = __IW0;\r\n");
}
WHEN("Contains single WORD at %QW0") {
std::stringstream input_stream("__LOCATED_VAR(WORD,__QW0,Q,W,0)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tint_output[0] = __QW0;\r\n");
}
WHEN("Contains single INT at %IW1") {
std::stringstream input_stream("__LOCATED_VAR(INT,__IW1,I,W,1)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tint_input[1] = __IW1;\r\n");
}
WHEN("Contains single UINT at %IW2") {
std::stringstream input_stream("__LOCATED_VAR(UINT,__IW2,I,W,2)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tint_input[2] = __IW2;\r\n");
}
WHEN("Contains single INT at %MW2") {
std::stringstream input_stream("__LOCATED_VAR(INT,__MW2,M,W,2)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tint_memory[2] = __MW2;\r\n");
}
WHEN("Contains single DWORD at %MD0") {
std::stringstream input_stream("__LOCATED_VAR(DWORD,__MD2,M,D,2)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tdint_memory[2] = (IEC_DINT *)__MD2;\r\n");
}
WHEN("Contains single LINT at %ML1") {
std::stringstream input_stream("__LOCATED_VAR(LINT,__ML1,M,L,1)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tlint_memory[1] = (IEC_LINT *)__ML1;\r\n");
}
WHEN("Contains single LINT at %ML1024") {
std::stringstream input_stream("__LOCATED_VAR(LINT,__ML1024,M,L,1024)");
generateBody(input_stream, output_stream);
REQUIRE(output_stream.str() == "\tspecial_functions[0] = (IEC_LINT *)__ML1024;\r\n");
}
}
}