diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bfc06f..304cd07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,9 @@ else() if (WIN32) message("Use g++ compiler under Windows (mingw)...") add_compile_definitions(UNICODE _UNICODE main=wmain) + # MinGW COFF object files can hit section limits for large translation units. + # Enable big object format to avoid assembler 'too many sections' failures. + add_compile_options(-Wa,-mbig-obj) add_link_options(-municode) else() message("Use g++ compiler...") diff --git a/README.md b/README.md index 52737c9..8b86c76 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ +The 'Eclipse Open Vehicle API' project is supported by Chips JU. + +arch-module-if + +arch-module-if + +The HAL4SDV project (Grant Agreement No. 101139789) is supported by the Chips Joint Undertaking and its members, including top-up funding by the national authorities. + +arch-module-if + +The Shift2SDV project (Grant Agreement No. 101194245) is supported by the Chips Joint Undertaking and its members, including top-up funding by the national authorities. + +arch-module-if + +--- + + # This is the latest news about 'Eclipse Open Vehicle API' # # for more information see documentation: https://eclipse.dev/openvehicle-api/ diff --git a/doxygen.config b/doxygen.config index d7d3f12..6376615 100644 --- a/doxygen.config +++ b/doxygen.config @@ -1786,515 +1786,515 @@ MATHJAX_CODEFILE = # the search. The filter options can be selected when the cursor is inside the # search box by pressing +. Also here use the # to select a filter and or - to activate or cancel the filter - # option. - # The default value is: YES. - # This tag requires that the tag GENERATE_HTML is set to YES. - - SEARCHENGINE = YES - - # When the SERVER_BASED_SEARCH tag is enabled the search engine will be - # implemented using a web server instead of a web client using JavaScript. There - # are two flavors of web server based searching depending on the EXTERNAL_SEARCH - # setting. When disabled, doxygen will generate a PHP script for searching and - # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing - # and searching needs to be provided by external tools. See the section - # "External Indexing and Searching" for details. - # The default value is: NO. - # This tag requires that the tag SEARCHENGINE is set to YES. - - SERVER_BASED_SEARCH = NO - - # When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP - # script for searching. Instead the search results are written to an XML file - # which needs to be processed by an external indexer. Doxygen will invoke an - # external search engine pointed to by the SEARCHENGINE_URL option to obtain the - # search results. - # - # Doxygen ships with an example indexer (doxyindexer) and search engine - # (doxysearch.cgi) which are based on the open source search engine library - # Xapian (see: - # https://xapian.org/). - # - # See the section "External Indexing and Searching" for details. - # The default value is: NO. - # This tag requires that the tag SEARCHENGINE is set to YES. - - EXTERNAL_SEARCH = NO - - # The SEARCHENGINE_URL should point to a search engine hosted by a web server - # which will return the search results when EXTERNAL_SEARCH is enabled. - # - # Doxygen ships with an example indexer (doxyindexer) and search engine - # (doxysearch.cgi) which are based on the open source search engine library - # Xapian (see: - # https://xapian.org/). See the section "External Indexing and Searching" for - # details. - # This tag requires that the tag SEARCHENGINE is set to YES. - - SEARCHENGINE_URL = - - # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed - # search data is written to a file for indexing by an external tool. With the - # SEARCHDATA_FILE tag the name of this file can be specified. - # The default file is: searchdata.xml. - # This tag requires that the tag SEARCHENGINE is set to YES. - - SEARCHDATA_FILE = searchdata.xml - - # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the - # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is - # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple - # projects and redirect the results back to the right project. - # This tag requires that the tag SEARCHENGINE is set to YES. - - EXTERNAL_SEARCH_ID = - - # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen - # projects other than the one defined by this configuration file, but that are - # all added to the same external search index. Each project needs to have a - # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of - # to a relative location where the documentation can be found. The format is: - # EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... - # This tag requires that the tag SEARCHENGINE is set to YES. - - EXTRA_SEARCH_MAPPINGS = - - #--------------------------------------------------------------------------- - # Configuration options related to the LaTeX output - #--------------------------------------------------------------------------- - - # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. - # The default value is: YES. - - GENERATE_LATEX = NO - - # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a - # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of - # it. - # The default directory is: latex. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_OUTPUT = latex - - # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be - # invoked. - # - # Note that when not enabling USE_PDFLATEX the default is latex when enabling - # USE_PDFLATEX the default is pdflatex and when in the later case latex is - # chosen this is overwritten by pdflatex. For specific output languages the - # default can have been set differently, this depends on the implementation of - # the output language. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_CMD_NAME = latex - - # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate - # index for LaTeX. - # Note: This tag is used in the Makefile / make.bat. - # See also: LATEX_MAKEINDEX_CMD for the part in the generated output file - # (.tex). - # The default file is: makeindex. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - MAKEINDEX_CMD_NAME = makeindex - - # The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to - # generate index for LaTeX. In case there is no backslash (\) as first character - # it will be automatically added in the LaTeX code. - # Note: This tag is used in the generated output file (.tex). - # See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. - # The default value is: makeindex. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_MAKEINDEX_CMD = makeindex - - # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX - # documents. This may be useful for small projects and may help to save some - # trees in general. - # The default value is: NO. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - COMPACT_LATEX = NO - - # The PAPER_TYPE tag can be used to set the paper type that is used by the - # printer. - # Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x - # 14 inches) and executive (7.25 x 10.5 inches). - # The default value is: a4. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - PAPER_TYPE = a4 - - # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names - # that should be included in the LaTeX output. The package can be specified just - # by its name or with the correct syntax as to be used with the LaTeX - # \usepackage command. To get the times font for instance you can specify : - # EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} - # To use the option intlimits with the amsmath package you can specify: - # EXTRA_PACKAGES=[intlimits]{amsmath} - # If left blank no extra packages will be included. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - EXTRA_PACKAGES = - - # The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for - # the generated LaTeX document. The header should contain everything until the - # first chapter. If it is left blank doxygen will generate a standard header. It - # is highly recommended to start with a default header using - # doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty - # and then modify the file new_header.tex. See also section "Doxygen usage" for - # information on how to generate the default header that doxygen normally uses. - # - # Note: Only use a user-defined header if you know what you are doing! - # Note: The header is subject to change so you typically have to regenerate the - # default header when upgrading to a newer version of doxygen. The following - # commands have a special meaning inside the header (and footer): For a - # description of the possible markers and block names see the documentation. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_HEADER = - - # The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for - # the generated LaTeX document. The footer should contain everything after the - # last chapter. If it is left blank doxygen will generate a standard footer. See - # LATEX_HEADER for more information on how to generate a default footer and what - # special commands can be used inside the footer. See also section "Doxygen - # usage" for information on how to generate the default footer that doxygen - # normally uses. Note: Only use a user-defined footer if you know what you are - # doing! - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_FOOTER = - - # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined - # LaTeX style sheets that are included after the standard style sheets created - # by doxygen. Using this option one can overrule certain style aspects. Doxygen - # will copy the style sheet files to the output directory. - # Note: The order of the extra style sheet files is of importance (e.g. the last - # style sheet in the list overrules the setting of the previous ones in the - # list). - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_EXTRA_STYLESHEET = - - # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or - # other source files which should be copied to the LATEX_OUTPUT output - # directory. Note that the files will be copied as-is; there are no commands or - # markers available. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_EXTRA_FILES = - - # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is - # prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will - # contain links (just like the HTML output) instead of page references. This - # makes the output suitable for online browsing using a PDF viewer. - # The default value is: YES. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - PDF_HYPERLINKS = YES - - # If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as - # specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX - # files. Set this option to YES, to get a higher quality PDF documentation. - # - # See also section LATEX_CMD_NAME for selecting the engine. - # The default value is: YES. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - USE_PDFLATEX = YES - - # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode - # command to the generated LaTeX files. This will instruct LaTeX to keep running - # if errors occur, instead of asking the user for help. - # The default value is: NO. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_BATCHMODE = NO - - # If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the - # index chapters (such as File Index, Compound Index, etc.) in the output. - # The default value is: NO. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_HIDE_INDICES = NO - - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the - # bibliography, e.g. plainnat, or ieeetr. See - # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. - # The default value is: plain. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_BIB_STYLE = plain - - # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) - # path from which the emoji images will be read. If a relative path is entered, - # it will be relative to the LATEX_OUTPUT directory. If left blank the - # LATEX_OUTPUT directory will be used. - # This tag requires that the tag GENERATE_LATEX is set to YES. - - LATEX_EMOJI_DIRECTORY = - - #--------------------------------------------------------------------------- - # Configuration options related to the RTF output - #--------------------------------------------------------------------------- - - # If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The - # RTF output is optimized for Word 97 and may not look too pretty with other RTF - # readers/editors. - # The default value is: NO. - - GENERATE_RTF = NO - - # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a - # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of - # it. - # The default directory is: rtf. - # This tag requires that the tag GENERATE_RTF is set to YES. - - RTF_OUTPUT = rtf - - # If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF - # documents. This may be useful for small projects and may help to save some - # trees in general. - # The default value is: NO. - # This tag requires that the tag GENERATE_RTF is set to YES. - - COMPACT_RTF = NO - - # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will - # contain hyperlink fields. The RTF file will contain links (just like the HTML - # output) instead of page references. This makes the output suitable for online - # browsing using Word or some other Word compatible readers that support those - # fields. - # - # Note: WordPad (write) and others do not support links. - # The default value is: NO. - # This tag requires that the tag GENERATE_RTF is set to YES. - - RTF_HYPERLINKS = NO +# to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using JavaScript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: +# https://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. +# +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO - # Load stylesheet definitions from file. Syntax is similar to doxygen's - # configuration file, i.e. a series of assignments. You only have to provide - # replacements, missing definitions are set to their default value. - # - # See also section "Doxygen usage" for information on how to generate the - # default style sheet that doxygen normally uses. - # This tag requires that the tag GENERATE_RTF is set to YES. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. - RTF_STYLESHEET_FILE = - - # Set optional variables used in the generation of an RTF document. Syntax is - # similar to doxygen's configuration file. A template extensions file can be - # generated using doxygen -e rtf extensionFile. - # This tag requires that the tag GENERATE_RTF is set to YES. +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. - RTF_EXTENSIONS_FILE = +RTF_EXTENSIONS_FILE = - #--------------------------------------------------------------------------- - # Configuration options related to the man page output - #--------------------------------------------------------------------------- - - # If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for - # classes and files. - # The default value is: NO. +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. - GENERATE_MAN = NO +GENERATE_MAN = NO - # The MAN_OUTPUT tag is used to specify where the man pages will be put. If a - # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of - # it. A directory man3 will be created inside the directory specified by - # MAN_OUTPUT. - # The default directory is: man. - # This tag requires that the tag GENERATE_MAN is set to YES. +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. - MAN_OUTPUT = man +MAN_OUTPUT = man - # The MAN_EXTENSION tag determines the extension that is added to the generated - # man pages. In case the manual section does not start with a number, the number - # 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is - # optional. - # The default value is: .3. - # This tag requires that the tag GENERATE_MAN is set to YES. +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. - MAN_EXTENSION = .3 +MAN_EXTENSION = .3 - # The MAN_SUBDIR tag determines the name of the directory created within - # MAN_OUTPUT in which the man pages are placed. If defaults to man followed by - # MAN_EXTENSION with the initial . removed. - # This tag requires that the tag GENERATE_MAN is set to YES. +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. - MAN_SUBDIR = - - # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it - # will generate one additional man file for each entity documented in the real - # man page(s). These additional files only source the real man page, but without - # them the man command would be unable to find the correct page. - # The default value is: NO. - # This tag requires that the tag GENERATE_MAN is set to YES. +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. - MAN_LINKS = NO +MAN_LINKS = NO - #--------------------------------------------------------------------------- - # Configuration options related to the XML output - #--------------------------------------------------------------------------- +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- - # If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that - # captures the structure of the code including all documentation. - # The default value is: NO. +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. - GENERATE_XML = YES +GENERATE_XML = YES - # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a - # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of - # it. - # The default directory is: xml. - # This tag requires that the tag GENERATE_XML is set to YES. +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. - XML_OUTPUT = xml - - # If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program - # listings (including syntax highlighting and cross-referencing information) to - # the XML output. Note that enabling this will significantly increase the size - # of the XML output. - # The default value is: YES. - # This tag requires that the tag GENERATE_XML is set to YES. +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. - XML_PROGRAMLISTING = YES +XML_PROGRAMLISTING = YES - # If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include - # namespace members in file scope as well, matching the HTML output. - # The default value is: NO. - # This tag requires that the tag GENERATE_XML is set to YES. +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. - XML_NS_MEMB_FILE_SCOPE = NO +XML_NS_MEMB_FILE_SCOPE = NO - #--------------------------------------------------------------------------- - # Configuration options related to the DOCBOOK output - #--------------------------------------------------------------------------- +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- - # If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files - # that can be used to generate PDF. - # The default value is: NO. +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. - GENERATE_DOCBOOK = NO +GENERATE_DOCBOOK = NO - # The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. - # If a relative path is entered the value of OUTPUT_DIRECTORY will be put in - # front of it. - # The default directory is: docbook. - # This tag requires that the tag GENERATE_DOCBOOK is set to YES. +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - DOCBOOK_OUTPUT = docbook +DOCBOOK_OUTPUT = docbook - #--------------------------------------------------------------------------- - # Configuration options for the AutoGen Definitions output - #--------------------------------------------------------------------------- +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- - # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an - # AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures - # the structure of the code including all documentation. Note that this feature - # is still experimental and incomplete at the moment. - # The default value is: NO. +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. +# The default value is: NO. - GENERATE_AUTOGEN_DEF = NO +GENERATE_AUTOGEN_DEF = NO - #--------------------------------------------------------------------------- - # Configuration options related to the Perl module output - #--------------------------------------------------------------------------- - - # If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module - # file that captures the structure of the code including all documentation. - # - # Note that this feature is still experimental and incomplete at the moment. - # The default value is: NO. +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. - GENERATE_PERLMOD = NO - - # If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary - # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI - # output from the Perl module output. - # The default value is: NO. - # This tag requires that the tag GENERATE_PERLMOD is set to YES. +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. - PERLMOD_LATEX = NO +PERLMOD_LATEX = NO - # If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely - # formatted so it can be parsed by a human reader. This is useful if you want to - # understand what is going on. On the other hand, if this tag is set to NO, the - # size of the Perl module output will be much smaller and Perl will parse it - # just the same. - # The default value is: YES. - # This tag requires that the tag GENERATE_PERLMOD is set to YES. - - PERLMOD_PRETTY = YES +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES - # The names of the make variables in the generated doxyrules.make file are - # prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful - # so different doxyrules.make files included by the same Makefile don't - # overwrite each other's variables. - # This tag requires that the tag GENERATE_PERLMOD is set to YES. +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. - PERLMOD_MAKEVAR_PREFIX = +PERLMOD_MAKEVAR_PREFIX = - #--------------------------------------------------------------------------- - # Configuration options related to the preprocessor - #--------------------------------------------------------------------------- +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- - # If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all - # C-preprocessor directives found in the sources and include files. - # The default value is: YES. +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. - ENABLE_PREPROCESSING = YES - - # If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names - # in the source code. If set to NO, only conditional compilation will be - # performed. Macro expansion can be done in a controlled way by setting - # EXPAND_ONLY_PREDEF to YES. - # The default value is: NO. - # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - - MACRO_EXPANSION = YES +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = YES - # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then - # the macro expansion is limited to the macros specified with the PREDEFINED and - # EXPAND_AS_DEFINED tags. - # The default value is: NO. - # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - EXPAND_ONLY_PREDEF = NO +EXPAND_ONLY_PREDEF = NO - # If the SEARCH_INCLUDES tag is set to YES, the include files in the - # INCLUDE_PATH will be searched if a #include is found. - # The default value is: YES. - # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - SEARCH_INCLUDES = YES - - # The INCLUDE_PATH tag can be used to specify one or more directories that - # contain include files that are not input files but should be processed by the - # preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of - # RECURSIVE has no effect here. - # This tag requires that the tag SEARCH_INCLUDES is set to YES. +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. - INCLUDE_PATH = - - # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard - # patterns (like *.h and *.hpp) to filter out the header-files in the - # directories. If left blank, the patterns specified with FILE_PATTERNS will be - # used. - # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - INCLUDE_FILE_PATTERNS = - - # The PREDEFINED tag can be used to specify one or more macro names that are - # defined before the preprocessor is started (similar to the -D option of e.g. - # gcc). The argument of the tag is a list of macros of the form: name or - # name=definition (no spaces). If the definition and the "=" are omitted, "=1" - # is assumed. To prevent a macro definition from being undefined via #undef or - # recursively expanded use the := operator instead of the = operator. - # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. PREDEFINED = DOXYGEN_IGNORE \ __declspec(x)= \ @@ -2307,7 +2307,7 @@ BEGIN_SDV_INTERFACE_MAP()= \ SDV_INTERFACE_ENTRY(x)= \ END_SDV_INTERFACE_MAP()= \ SDV_INTERFACE_USE_NAMESPACE(x)= \ -SDV_INTERFACE_ENTRY_MEMBER(x)= \ +SDV_INTERFACE_ENTRY_MEMBER(x, y)= \ SDV_INTERFACE_ENTRY_INDIRECT(x, y)= \ SDV_INTERFACE_DENY_ENTRY(x)= \ SDV_INTERFACE_CHAIN_BASE(x)= \ @@ -2317,94 +2317,119 @@ SDV_INTERFACE_SET_SECTION_CONDITION(x, y)= \ SDV_INTERFACE_SET_SECTION(x)= \ SDV_INTERFACE_DEFAULT_SECTION()= \ SDV_INTERFACE_SECTION(x)= \ +SDV_CONCAT_IMPL(a, b)= \ +SDV_CONCAT(a, b)= \ +SDV_UNIQUE_VAR(prefix)= \ +BEGIN_SDV_LABEL_MAP(enum_type)= \ +SDV_LABEL_ENTRY(enum_element, name_string)= \ +END_SDV_LABEL_MAP()= \ +BEGIN_SDV_PARAM_MAP()= \ +END_SDV_PARAM_MAP()= \ +SDV_PARAM_GROUP(group_string)= \ +SDV_PARAM_NO_GROUP()= \ +SDV_PARAM_RESET_ATTRIBUTES()= \ +SDV_PARAM_SET_READONLY()= \ +SDV_PARAM_RESET_READONLY()= \ +SDV_PARAM_ENABLE_LOCKING()= \ +SDV_PARAM_DISABLE_LOCKING()= \ +SDV_PARAM_SET_TEMPORARY()= \ +SDV_PARAM_RESET_TEMPORARY()= \ +SDV_PARAM_ENTRY(var, name_string, default_val, unit_string, description_string)= \ +SDV_PARAM_NUMBER_ENTRY(var, name_string, default_val, low_limit, high_limit, unit_string, description_string)= \ +SDV_PARAM_ENUM_ENTRY(var, name_string, default_val, description_string)= \ +SDV_PARAM_STRING_ENTRY(var, name_string, default_val, pattern_string, unit_string, description_string)= \ +SDV_PARAM_PATH_ENTRY(var, name_string, default_val, description_string)= \ +SDV_PARAM_BITMASK_ENTRY(TEnum, var, name_string, default_val, description_string)= \ +SDV_PARAM_CHAIN_BASE(base_class)= \ +SDV_PARAM_CHAIN_MEMBER(member)= \ DEFINE_SDV_OBJECT(x)= \ switch= - # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this - # tag can be used to specify a list of macro names that should be expanded. The - # macro definition that is found in the sources will be used. Use the PREDEFINED - # tag if you want to use a different macro definition that overrules the - # definition found in the source code. - # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = - # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will - # remove all references to function-like macros that are alone on a line, have - # an all uppercase name, and do not end with a semicolon. Such function macros - # are typically used for boiler-plate code, and will confuse the parser if not - # removed. - # The default value is: YES. - # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - SKIP_FUNCTION_MACROS = YES +SKIP_FUNCTION_MACROS = YES - #--------------------------------------------------------------------------- - # Configuration options related to external references - #--------------------------------------------------------------------------- +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- - # The TAGFILES tag can be used to specify one or more tag files. For each tag - # file the location of the external documentation should be added. The format of - # a tag file without this location is as follows: - # TAGFILES = file1 file2 ... - # Adding location for the tag files is done as follows: - # TAGFILES = file1=loc1 "file2 = loc2" ... - # where loc1 and loc2 can be relative or absolute paths or URLs. See the - # section "Linking to external documentation" for more information about the use - # of tag files. - # Note: Each tag file must have a unique name (where the name does NOT include - # the path). If a tag file is not located in the directory in which doxygen is - # run, you must also specify the path to the tagfile here. +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. - TAGFILES = +TAGFILES = - # When a file name is specified after GENERATE_TAGFILE, doxygen will create a - # tag file that is based on the input files it reads. See section "Linking to - # external documentation" for more information about the usage of tag files. +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. - GENERATE_TAGFILE = +GENERATE_TAGFILE = - # If the ALLEXTERNALS tag is set to YES, all external class will be listed in - # the class index. If set to NO, only the inherited external classes will be - # listed. - # The default value is: NO. +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. - ALLEXTERNALS = NO +ALLEXTERNALS = NO - # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed - # in the modules index. If set to NO, only the current project's groups will be - # listed. - # The default value is: YES. +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. - EXTERNAL_GROUPS = YES +EXTERNAL_GROUPS = YES - # If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in - # the related pages index. If set to NO, only the current project's pages will - # be listed. - # The default value is: YES. +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. - EXTERNAL_PAGES = YES +EXTERNAL_PAGES = YES - #--------------------------------------------------------------------------- - # Configuration options related to the dot tool - #--------------------------------------------------------------------------- +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- - # You can include diagrams made with dia in doxygen documentation. Doxygen will - # then run dia to produce the diagram and insert it in the documentation. The - # DIA_PATH tag allows you to specify the directory where the dia binary resides. - # If left empty dia is assumed to be found in the default search path. +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. - DIA_PATH = +DIA_PATH = - # If set to YES the inheritance and collaboration graphs will hide inheritance - # and usage relations if the target is undocumented or is not a class. - # The default value is: YES. +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. - HIDE_UNDOC_RELATIONS = YES +HIDE_UNDOC_RELATIONS = YES - # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is - # available from the path. This tool is part of Graphviz (see: - # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp index 40a2191..13d780a 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp @@ -353,7 +353,7 @@ void CConsole::StartUpdateDataThread() m_bThreadStarted = true; m_bRunning = true; - m_threadReadTxSignals = std::thread(&CConsole::UpdateDataThreadFunc, this); + m_threadReadTxSignals = sdv::core::secure_thread(&CConsole::UpdateDataThreadFunc, this); } void CConsole::StopUpdateDataThread() diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h index ab13c36..ec35250 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h @@ -200,33 +200,33 @@ private: */ std::string AlignString(const std::string& message, uint32_t desiredLength = 0); - mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message - std::thread m_threadReadTxSignals; ///< Simulation datalink thread. - bool m_bThreadStarted = false; ///< Set when initialized. - std::atomic_bool m_bRunning = false; ///< When set, the application is running. + mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message + sdv::core::secure_thread m_threadReadTxSignals; ///< Simulation datalink thread. + bool m_bThreadStarted = false; ///< Set when initialized. + std::atomic_bool m_bRunning = false; ///< When set, the application is running. - sdv::core::CSignal m_signalCurrentLatitude; ///< Signal Current latitude - sdv::core::CSignal m_signalCurrentLongitude; ///< Signal Current longitude - sdv::core::CSignal m_signalHeadlight; ///< Signal Headlight status + sdv::core::CSignal m_signalCurrentLatitude; ///< Signal Current latitude + sdv::core::CSignal m_signalCurrentLongitude; ///< Signal Current longitude + sdv::core::CSignal m_signalHeadlight; ///< Signal Headlight status - float m_fCurrentLongitude = 0.0f; //< Current latitude - float m_fCurrentLatitude = 0.0f; //< Current longitude - bool m_bHeadLight = true; ///< Head light + float m_fCurrentLongitude = 0.0f; //< Current latitude + float m_fCurrentLatitude = 0.0f; //< Current longitude + bool m_bHeadLight = true; ///< Head light - float m_fVehicleDeviceCurrentLatitude = 0.0f; ///< Current latitude (Vehicle Device) - float m_fVehicleDeviceCurrentLongitude = 0.0; ///< Current longitude (Vehicle Device) + float m_fVehicleDeviceCurrentLatitude = 0.0f; ///< Current latitude (Vehicle Device) + float m_fVehicleDeviceCurrentLongitude = 0.0; ///< Current longitude (Vehicle Device) - float m_fBasicServiceCurrentLatitude = 0.0f; ///< Current latitude (basic Service) - float m_fBasicServiceCurrentLongitude = 0.0; ///< Current longitude (basic Service) + float m_fBasicServiceCurrentLatitude = 0.0f; ///< Current latitude (basic Service) + float m_fBasicServiceCurrentLongitude = 0.0; ///< Current longitude (basic Service) - IAutoheadlightService* m_pIAutoheadlightComplexService = nullptr; ///< Autoheadlight Service interface pointer. + IAutoheadlightService* m_pIAutoheadlightComplexService = nullptr; ///< Autoheadlight Service interface pointer. #ifdef _WIN32 - DWORD m_dwConsoleOutMode = 0u; ///< The console mode before switching on ANSI support. - DWORD m_dwConsoleInMode = 0u; ///< The console mode before switching on ANSI support. + DWORD m_dwConsoleOutMode = 0u; ///< The console mode before switching on ANSI support. + DWORD m_dwConsoleInMode = 0u; ///< The console mode before switching on ANSI support. #elif defined __unix__ - struct termios m_sTermAttr {}; ///< The terminal attributes before disabling echo. + struct termios m_sTermAttr {}; ///< The terminal attributes before disabling echo. int m_iFileStatus = 0; ///< The file status flags for STDIN. #else #error The OS is not supported! diff --git a/examples/auto_headlamp_example/fmu_files/model.cpp b/examples/auto_headlamp_example/fmu_files/model.cpp index ad6b52c..524a9e0 100644 --- a/examples/auto_headlamp_example/fmu_files/model.cpp +++ b/examples/auto_headlamp_example/fmu_files/model.cpp @@ -94,7 +94,10 @@ bool CreateCoreServiceTomlFile(const std::string& resources) std::ofstream tomlFile("sdv_core_reloc.toml"); if (tomlFile.is_open()) { - tomlFile << "# Location of the SDV binaries and configuration files\ndirectory = \""; + tomlFile << R"toml(# Location of the SDV binaries and configuration files +[CoreLocation] +Version = 100 +Framework = ")toml"; tomlFile << resources; tomlFile << "\"\n"; tomlFile.close(); diff --git a/examples/door_demo_example/CMakeLists.txt b/examples/door_demo_example/CMakeLists.txt index cbc7141..65e2043 100644 --- a/examples/door_demo_example/CMakeLists.txt +++ b/examples/door_demo_example/CMakeLists.txt @@ -43,59 +43,63 @@ file (COPY ${PROJECT_SOURCE_DIR}/door_example_receiver.asc DESTINATION ${CMAKE_B ###################################################################################################################################################################### # Execute sdv_vss_util to create IDL files for devices and basic services. +# In this example we split into into front and rear (frontdoors_proxystub.sdv and frontdoors_proxystub.sdv) +# because all interfaces combined in doors_proxystub.sdv would be to large message("Create interface code for devices and basic services of doors example.") -execute_process(COMMAND "${SDV_VSS_UTIL}" "${PROJECT_SOURCE_DIR}/vss_doors_example.csv" "-O${PROJECT_SOURCE_DIR}/generated/" --prefixdoors --version1.0.0.1 --enable_components) +execute_process(COMMAND "${SDV_VSS_UTIL}" "${PROJECT_SOURCE_DIR}/vss_doors_front_example.csv" "-O${PROJECT_SOURCE_DIR}/generated/front/" --prefixfrontdoors --version1.0.0.1 --enable_components) +execute_process(COMMAND "${SDV_VSS_UTIL}" "${PROJECT_SOURCE_DIR}/vss_doors_rear_example.csv" "-O${PROJECT_SOURCE_DIR}/generated/rear/" --prefixreardoors --version1.0.0.1 --enable_components) message("Compiling Interface Door01Left RX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01left_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01left_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/front/vss_files/vss_vehiclebodydooraxle_01left_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/front/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/front/vss_files/vss_vehiclebodydooraxle_01left_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/front/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namefrontdoors_proxystub) message("Compiling Interface Door01Right RX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01right_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01right_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) - -message("Compiling Interface Door02Left RX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02left_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02left_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) - -message("Compiling Interface Door02Right RX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02right_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02right_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) - +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/front/vss_files/vss_vehiclebodydooraxle_01right_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/front/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/front/vss_files/vss_vehiclebodydooraxle_01right_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/front/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namefrontdoors_proxystub) message("Compiling Interface Door01Left TX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01left_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01left_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/front/vss_files/vss_vehiclebodydooraxle_01left_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/front/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/front/vss_files/vss_vehiclebodydooraxle_01left_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/front/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namefrontdoors_proxystub) message("Compiling Interface Door01Right TX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01right_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_01right_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/front/vss_files/vss_vehiclebodydooraxle_01right_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/front/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/front/vss_files/vss_vehiclebodydooraxle_01right_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/front/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namefrontdoors_proxystub) + + +message("Compiling Interface Door02Left RX") +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/rear/vss_files/vss_vehiclebodydooraxle_02left_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/rear/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/rear/vss_files/vss_vehiclebodydooraxle_02left_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/rear/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namereardoors_proxystub) + +message("Compiling Interface Door02Right RX") +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/rear/vss_files/vss_vehiclebodydooraxle_02right_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/rear/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/rear/vss_files/vss_vehiclebodydooraxle_02right_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/rear/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namereardoors_proxystub) message("Compiling Interface Door02Left TX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02left_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02left_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/rear/vss_files/vss_vehiclebodydooraxle_02left_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/rear/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/rear/vss_files/vss_vehiclebodydooraxle_02left_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/rear/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namereardoors_proxystub) message("Compiling Interface Door02Right TX") -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02right_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodydooraxle_02right_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedoors_proxystub) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/rear/vss_files/vss_vehiclebodydooraxle_02right_vd_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/rear/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/rear/vss_files/vss_vehiclebodydooraxle_02right_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/rear/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namereardoors_proxystub) # Execute the IDL compiler for the complex service to digest interface code. message("Compiling door_ifc.idl") execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/door_service/door_ifc.idl" "-O${PROJECT_SOURCE_DIR}/generated/door_service/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Iexample_service/ --ps_lib_namedoors_service_proxystub) add_subdirectory(generated/door_service/ps) -add_subdirectory(generated/vss_files/ps) +add_subdirectory(generated/front/vss_files/ps) +add_subdirectory(generated/rear/vss_files/ps) -add_subdirectory(generated/vss_files/vd_frontdoorleft) -add_subdirectory(generated/vss_files/vd_frontdoorright) -add_subdirectory(generated/vss_files/vd_reardoorleft) -add_subdirectory(generated/vss_files/vd_reardoorright) +add_subdirectory(generated/front/vss_files/vd_frontdoorleft) +add_subdirectory(generated/front/vss_files/vd_frontdoorright) +add_subdirectory(generated/front/vss_files/bs_frontdoorleft) +add_subdirectory(generated/front/vss_files/bs_frontdoorright) -add_subdirectory(generated/vss_files/bs_frontdoorleft) -add_subdirectory(generated/vss_files/bs_frontdoorright) -add_subdirectory(generated/vss_files/bs_reardoorleft) -add_subdirectory(generated/vss_files/bs_reardoorright) +add_subdirectory(generated/rear/vss_files/vd_reardoorleft) +add_subdirectory(generated/rear/vss_files/vd_reardoorright) +add_subdirectory(generated/rear/vss_files/bs_reardoorleft) +add_subdirectory(generated/rear/vss_files/bs_reardoorright) # Execute sdv_dbc_util to create data link code & FMU code. message("Create data link for doors example") @@ -167,7 +171,6 @@ else() target_link_libraries(door_external_app Rpcrt4.lib) endif() - ###################################################################################################################################################################### # create instance 3002 of door demo example ###################################################################################################################################################################### @@ -175,23 +178,25 @@ add_custom_target(vehicle_device_doors_config ALL DEPENDS can_dl_doors - doors_vd_frontdoorleft_rx - doors_vd_frontdoorright_rx - doors_vd_reardoorleft_rx - doors_vd_reardoorright_rx - COMMAND "${SDV_PACKAGER}" DIRECT_INSTALL VehicleDeviceDoors --instance3002 can_dl_doors.sdv doors_vd_frontdoorleft_rx.sdv doors_vd_frontdoorright_rx.sdv doors_vd_reardoorleft_rx.sdv doors_vd_reardoorright_rx.sdv "-I${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" --interface_config --overwrite + frontdoors_vd_frontdoorleft_rx + frontdoors_vd_frontdoorright_rx + reardoors_vd_reardoorleft_rx + reardoors_vd_reardoorright_rx + COMMAND "${SDV_PACKAGER}" DIRECT_INSTALL VehicleDeviceDoors --instance3002 can_dl_doors.sdv frontdoors_vd_frontdoorleft_rx.sdv frontdoors_vd_frontdoorright_rx.sdv reardoors_vd_reardoorleft_rx.sdv reardoors_vd_reardoorright_rx.sdv "-I${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" --interface_config --overwrite VERBATIM ) + add_custom_target(basic_service_doors_config ALL DEPENDS - doors_proxystub - doors_bs_frontdoorleft_rx - doors_bs_frontdoorright_rx - doors_bs_reardoorleft_rx - doors_bs_reardoorright_rx - COMMAND "${SDV_PACKAGER}" DIRECT_INSTALL BasicServiceDoors --instance3002 doors_proxystub.sdv doors_bs_frontdoorleft_rx.sdv doors_bs_frontdoorright_rx.sdv doors_bs_reardoorleft_rx.sdv doors_bs_reardoorright_rx.sdv "-I${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" --abstract_config --overwrite + frontdoors_proxystub + reardoors_proxystub + frontdoors_bs_frontdoorleft_rx + frontdoors_bs_frontdoorright_rx + reardoors_bs_reardoorleft_rx + reardoors_bs_reardoorright_rx + COMMAND "${SDV_PACKAGER}" DIRECT_INSTALL BasicServiceDoors --instance3002 frontdoors_proxystub.sdv reardoors_proxystub.sdv frontdoors_bs_frontdoorleft_rx.sdv frontdoors_bs_frontdoorright_rx.sdv reardoors_bs_reardoorleft_rx.sdv reardoors_bs_reardoorright_rx.sdv "-I${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" --abstract_config --overwrite VERBATIM ) diff --git a/examples/door_demo_example/config/front_left_door_example.toml b/examples/door_demo_example/config/front_left_door_example.toml index dd6699c..3019feb 100644 --- a/examples/door_demo_example/config/front_left_door_example.toml +++ b/examples/door_demo_example/config/front_left_door_example.toml @@ -2,11 +2,11 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorleft_rx.sdv" +Path = "frontdoors_vd_frontdoorleft_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Left_Device" [[Component]] -Path = "doors_bs_frontdoorleft_rx.sdv" +Path = "frontdoors_bs_frontdoorleft_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Left_Service" diff --git a/examples/door_demo_example/config/front_right_door_example.toml b/examples/door_demo_example/config/front_right_door_example.toml index 68f14bd..bce3013 100644 --- a/examples/door_demo_example/config/front_right_door_example.toml +++ b/examples/door_demo_example/config/front_right_door_example.toml @@ -2,11 +2,11 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorright_rx.sdv" +Path = "frontdoors_vd_frontdoorright_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Right_Device" [[Component]] -Path = "doors_bs_frontdoorright_rx.sdv" +Path = "frontdoors_bs_frontdoorright_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Right_Service" diff --git a/examples/door_demo_example/config/rear_left_door_example.toml b/examples/door_demo_example/config/rear_left_door_example.toml index 2da09dd..e26f52d 100644 --- a/examples/door_demo_example/config/rear_left_door_example.toml +++ b/examples/door_demo_example/config/rear_left_door_example.toml @@ -2,11 +2,11 @@ Version = 100 [[Component]] -Path = "doors_vd_reardoorleft_rx.sdv" +Path = "reardoors_vd_reardoorleft_rx.sdv" Class = "Vehicle.Body.Door.Axle._02.Left_Device" [[Component]] -Path = "doors_bs_reardoorleft_rx.sdv" +Path = "reardoors_bs_reardoorleft_rx.sdv" Class = "Vehicle.Body.Door.Axle._02.Left_Service" diff --git a/examples/door_demo_example/config/rear_right_door_example.toml b/examples/door_demo_example/config/rear_right_door_example.toml index 4f2069e..64acf61 100644 --- a/examples/door_demo_example/config/rear_right_door_example.toml +++ b/examples/door_demo_example/config/rear_right_door_example.toml @@ -2,9 +2,9 @@ Version = 100 [[Component]] -Path = "doors_vd_reardoorright_rx.sdv" +Path = "reardoors_vd_reardoorright_rx.sdv" Class = "Vehicle.Body.Door.Axle._02.Right_Device" [[Component]] -Path = "doors_bs_reardoorright_rx.sdv" +Path = "reardoors_bs_reardoorright_rx.sdv" Class = "Vehicle.Body.Door.Axle._02.Right_Service" diff --git a/examples/door_demo_example/door_app/console.cpp b/examples/door_demo_example/door_app/console.cpp index acd9dea..f4c3cc2 100644 --- a/examples/door_demo_example/door_app/console.cpp +++ b/examples/door_demo_example/door_app/console.cpp @@ -128,7 +128,6 @@ void CConsole::PrintHeader(const uint32_t numberOfDoors) PrintText(g_sTitle, title.c_str()); PrintText(g_sControlDescription, "Press 'X' to quit; Doors are toggled automatically"); } - } bool CConsole::PrepareDataConsumers() @@ -205,8 +204,7 @@ void CConsole::ResetSignals() // Set the cursor position at the end SetCursorPos(g_sCursor); - // Registrate for the vehicle device & basic service of the front left door. Front left door mzust exist, the others are optional - auto vehicleDevice = + auto vehicleDevice = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface < vss::Vehicle::Body::Door::Axle::_01 ::LeftDevice::IVSS_ReadIsOpen > (); if (vehicleDevice) vehicleDevice->UnregisterIsOpenEvent(dynamic_cast (this)); @@ -254,7 +252,7 @@ void CConsole::StartUpdateDataThread() m_bThreadStarted = true; m_bRunning = true; - m_threadReadTxSignals = std::thread(&CConsole::UpdateDataThreadFunc, this); + m_threadReadTxSignals = sdv::core::secure_thread(&CConsole::UpdateDataThreadFunc, this); } void CConsole::StopUpdateDataThread() @@ -299,21 +297,21 @@ bool CConsole::PrepareDataConsumersForStandAlone() { // Subscribe for the door and if available get TX signal. Either both exists or none of them sdv::core::CDispatchService dispatch; - m_SignalFrontLeftDoorIsOpen = dispatch.Subscribe(doors::dsLeftDoorIsOpen01, [&](sdv::any_t value) { CallbackFrontLeftDoorIsOpen(value); }); + m_SignalFrontLeftDoorIsOpen = dispatch.Subscribe(frontdoors::dsLeftDoorIsOpen01, [&](sdv::any_t value) { CallbackFrontLeftDoorIsOpen(value); }); if(m_SignalFrontLeftDoorIsOpen) - m_SignalFrontLeftDoorIsLocked = dispatch.RegisterTxSignal(doors::dsLeftLatch01, 0); + m_SignalFrontLeftDoorIsLocked = dispatch.RegisterTxSignal(frontdoors::dsLeftLatch01, 0); - m_SignalFrontRightDoorIsOpen = dispatch.Subscribe(doors::dsRightDoorIsOpen01, [&](sdv::any_t value) { CallbackFrontRightDoorIsOpen(value); }); + m_SignalFrontRightDoorIsOpen = dispatch.Subscribe(frontdoors::dsRightDoorIsOpen01, [&](sdv::any_t value) { CallbackFrontRightDoorIsOpen(value); }); if(m_SignalFrontRightDoorIsOpen) - m_SignalFrontRightDoorIsLocked = dispatch.RegisterTxSignal(doors::dsRightLatch01, 0); + m_SignalFrontRightDoorIsLocked = dispatch.RegisterTxSignal(frontdoors::dsRightLatch01, 0); - m_SignalRearLeftDoorIsOpen = dispatch.Subscribe(doors::dsLeftDoorIsOpen02, [&](sdv::any_t value) {CallbackRearLeftDoorIsOpen(value); }); + m_SignalRearLeftDoorIsOpen = dispatch.Subscribe(reardoors::dsLeftDoorIsOpen02, [&](sdv::any_t value) {CallbackRearLeftDoorIsOpen(value); }); if(m_SignalRearLeftDoorIsOpen) - m_SignalRearLeftDoorIsLocked = dispatch.RegisterTxSignal(doors::dsLeftLatch02, 0); + m_SignalRearLeftDoorIsLocked = dispatch.RegisterTxSignal(reardoors::dsLeftLatch02, 0); - m_SignalRearRightDoorIsOpen = dispatch.Subscribe(doors::dsRightDoorIsOpen02, [&](sdv::any_t value) { CallbackRearRightDoorIsOpen(value); }); + m_SignalRearRightDoorIsOpen = dispatch.Subscribe(reardoors::dsRightDoorIsOpen02, [&](sdv::any_t value) { CallbackRearRightDoorIsOpen(value); }); if(m_SignalRearRightDoorIsOpen) - m_SignalRearRightDoorIsLocked = dispatch.RegisterTxSignal(doors::dsRightLatch02, 0); + m_SignalRearRightDoorIsLocked = dispatch.RegisterTxSignal(reardoors::dsRightLatch02, 0); // Validate: Either both exists or none of them if (m_SignalFrontLeftDoorIsOpen != m_SignalFrontLeftDoorIsLocked) @@ -337,7 +335,7 @@ bool CConsole::PrepareDataConsumersForStandAlone() return false; } - // Registrate for the vehicle device & basic service of the front left door. Front left door mzust exist, the others are optional + // Register for the vehicle device & basic service of the front left door. Front left door must exist, the others are optional auto vehicleDevice = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface(); if (!vehicleDevice) { diff --git a/examples/door_demo_example/door_app/door_application.cpp b/examples/door_demo_example/door_app/door_application.cpp index 1f0ee6a..4015879 100644 --- a/examples/door_demo_example/door_app/door_application.cpp +++ b/examples/door_demo_example/door_app/door_application.cpp @@ -9,7 +9,8 @@ ********************************************************************************/ #include "../door_app/include/door_application.h" -#include "../generated/vss_files/signal_identifier.h" +#include "../generated/front/vss_files/signal_identifier.h" +#include "../generated/rear/vss_files/signal_identifier.h" #ifdef _WIN32 #include // Needed for _kbhit @@ -61,23 +62,23 @@ bool CDoorControl::RegisterSignals() { sdv::core::CDispatchService dispatch; - m_SignalFrontLeftDoorIsOpen = dispatch.RegisterRxSignal(doors::dsLeftDoorIsOpen01); - m_SignalFrontLeftDoorIsLocked = dispatch.RegisterTxSignal(doors::dsLeftLatch01, 0); + m_SignalFrontLeftDoorIsOpen = dispatch.RegisterRxSignal(frontdoors::dsLeftDoorIsOpen01); + m_SignalFrontLeftDoorIsLocked = dispatch.RegisterTxSignal(frontdoors::dsLeftLatch01, 0); if (m_iNumberOfDoors > 1) { - m_SignalFrontRightDoorIsOpen = dispatch.RegisterRxSignal(doors::dsRightDoorIsOpen01); - m_SignalFrontRightDoorIsLocked = dispatch.RegisterTxSignal(doors::dsRightLatch01, 0); + m_SignalFrontRightDoorIsOpen = dispatch.RegisterRxSignal(frontdoors::dsRightDoorIsOpen01); + m_SignalFrontRightDoorIsLocked = dispatch.RegisterTxSignal(frontdoors::dsRightLatch01, 0); if (m_iNumberOfDoors > 2) { - m_SignalRearLeftDoorIsOpen = dispatch.RegisterRxSignal(doors::dsLeftDoorIsOpen02); - m_SignalRearLeftDoorIsLocked = dispatch.RegisterTxSignal(doors::dsLeftLatch02, 0); + m_SignalRearLeftDoorIsOpen = dispatch.RegisterRxSignal(reardoors::dsLeftDoorIsOpen02); + m_SignalRearLeftDoorIsLocked = dispatch.RegisterTxSignal(reardoors::dsLeftLatch02, 0); if (m_iNumberOfDoors > 3) { - m_SignalRearRightDoorIsOpen = dispatch.RegisterRxSignal(doors::dsRightDoorIsOpen02); - m_SignalRearRightDoorIsLocked = dispatch.RegisterTxSignal(doors::dsRightLatch02, 0); + m_SignalRearRightDoorIsOpen = dispatch.RegisterRxSignal(reardoors::dsRightDoorIsOpen02); + m_SignalRearRightDoorIsLocked = dispatch.RegisterTxSignal(reardoors::dsRightLatch02, 0); } } } diff --git a/examples/door_demo_example/door_app/door_extern_application.cpp b/examples/door_demo_example/door_app/door_extern_application.cpp index 6cbdfbe..3189c1f 100644 --- a/examples/door_demo_example/door_app/door_extern_application.cpp +++ b/examples/door_demo_example/door_app/door_extern_application.cpp @@ -9,7 +9,8 @@ ********************************************************************************/ #include "../door_app/include/door_extern_application.h" -#include "../generated/vss_files/signal_identifier.h" +#include "../generated/front/vss_files/signal_identifier.h" +#include "../generated/rear/vss_files/signal_identifier.h" #ifdef _WIN32 #include // Needed for _kbhit diff --git a/examples/door_demo_example/door_app/include/console.h b/examples/door_demo_example/door_app/include/console.h index b88e257..bfe2e92 100644 --- a/examples/door_demo_example/door_app/include/console.h +++ b/examples/door_demo_example/door_app/include/console.h @@ -19,15 +19,16 @@ #include #include #include -#include "../../generated/vss_files/signal_identifier.h" +#include "../../generated/front/vss_files/signal_identifier.h" +#include "../../generated/rear/vss_files/signal_identifier.h" #include #include -#include "../../generated/vss_files/vss_vehiclebodydooraxle_01left_vd_rx.h" -#include "../../generated/vss_files/vss_vehiclebodydooraxle_01left_bs_rx.h" -#include "../../generated/vss_files/vss_vehiclebodydooraxle_01right_bs_rx.h" -#include "../../generated/vss_files/vss_vehiclebodydooraxle_02left_bs_rx.h" -#include "../../generated/vss_files/vss_vehiclebodydooraxle_02right_bs_rx.h" +#include "../../generated/front/vss_files/vss_vehiclebodydooraxle_01left_vd_rx.h" +#include "../../generated/front/vss_files/vss_vehiclebodydooraxle_01left_bs_rx.h" +#include "../../generated/front/vss_files/vss_vehiclebodydooraxle_01right_bs_rx.h" +#include "../../generated/rear/vss_files/vss_vehiclebodydooraxle_02left_bs_rx.h" +#include "../../generated/rear/vss_files/vss_vehiclebodydooraxle_02right_bs_rx.h" #ifdef __unix__ #include // Needed for tcgetattr and fcntl @@ -215,41 +216,41 @@ private: */ std::string AlignString(const std::string& message, uint32_t desiredLength = 0); - mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message - bool m_bThreadStarted = false; ///< Set when initialized. - std::atomic_bool m_bRunning = false; ///< When set, the application is running. - bool m_isExternalApp = false; ///< True when we have an external application + mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message + bool m_bThreadStarted = false; ///< Set when initialized. + std::atomic_bool m_bRunning = false; ///< When set, the application is running. + bool m_isExternalApp = false; ///< True when we have an external application - std::thread m_threadReadTxSignals; ///< Simulation datalink thread. + sdv::core::secure_thread m_threadReadTxSignals; ///< Simulation datalink thread. - sdv::core::CSignal m_SignalFrontLeftDoorIsOpen; ///< Front Left Door signal (RX input) - open / closed - sdv::core::CSignal m_SignalFrontRightDoorIsOpen; ///< Front Right Door signal (RX input) - open / closed - sdv::core::CSignal m_SignalRearLeftDoorIsOpen; ///< Rear Left Door signal (RX input) - open / closed - sdv::core::CSignal m_SignalRearRightDoorIsOpen; ///< Rear Right Door signal (RX input) - open / closed + sdv::core::CSignal m_SignalFrontLeftDoorIsOpen; ///< Front Left Door signal (RX input) - open / closed + sdv::core::CSignal m_SignalFrontRightDoorIsOpen; ///< Front Right Door signal (RX input) - open / closed + sdv::core::CSignal m_SignalRearLeftDoorIsOpen; ///< Rear Left Door signal (RX input) - open / closed + sdv::core::CSignal m_SignalRearRightDoorIsOpen; ///< Rear Right Door signal (RX input) - open / closed - bool m_FrontLeftDoorIsOpen = false; ///< Front Left Door value (RX input signal) - open / closed - bool m_FrontRightDoorIsOpen = false; ///< Front Right Door value (RX input signal) - open / closed - bool m_RearLeftDoorIsOpen = false; ///< Rear Left Door value (RX input signal) - open / closed - bool m_RearRightDoorIsOpen = false; ///< Rear Right Door value (RX input signal) - open / closed + bool m_FrontLeftDoorIsOpen = false; ///< Front Left Door value (RX input signal) - open / closed + bool m_FrontRightDoorIsOpen = false; ///< Front Right Door value (RX input signal) - open / closed + bool m_RearLeftDoorIsOpen = false; ///< Rear Left Door value (RX input signal) - open / closed + bool m_RearRightDoorIsOpen = false; ///< Rear Right Door value (RX input signal) - open / closed - sdv::core::CSignal m_SignalFrontLeftDoorIsLocked; ///< Front Left Door signal (TX output) - locked / unlocked - sdv::core::CSignal m_SignalFrontRightDoorIsLocked; ///< Front Right Door signal (TX output) - locked / unlocked - sdv::core::CSignal m_SignalRearLeftDoorIsLocked; ///< Rear Left Door signal (TX output) - locked / unlocked - sdv::core::CSignal m_SignalRearRightDoorIsLocked; ///< Rear Right Door signal (TX output) - locked / unlocked + sdv::core::CSignal m_SignalFrontLeftDoorIsLocked; ///< Front Left Door signal (TX output) - locked / unlocked + sdv::core::CSignal m_SignalFrontRightDoorIsLocked; ///< Front Right Door signal (TX output) - locked / unlocked + sdv::core::CSignal m_SignalRearLeftDoorIsLocked; ///< Rear Left Door signal (TX output) - locked / unlocked + sdv::core::CSignal m_SignalRearRightDoorIsLocked; ///< Rear Right Door signal (TX output) - locked / unlocked - bool m_FrontLeftDoorIsLocked = false; ///< Front Left Door value (TX output) - locked / unlocked - bool m_FrontRightDoorIsLocked = false; ///< Front Right Door value (TX output) - locked / unlocked - bool m_RearLeftDoorIsLocked = false; ///< Rear Left Door value (TX output) - locked / unlocked - bool m_RearRightDoorIsLocked = false; ///< Rear Right Door value (TX output) - locked / unlocked + bool m_FrontLeftDoorIsLocked = false; ///< Front Left Door value (TX output) - locked / unlocked + bool m_FrontRightDoorIsLocked = false; ///< Front Right Door value (TX output) - locked / unlocked + bool m_RearLeftDoorIsLocked = false; ///< Rear Left Door value (TX output) - locked / unlocked + bool m_RearRightDoorIsLocked = false; ///< Rear Right Door value (TX output) - locked / unlocked - IDoorService* m_pDoorService = nullptr; ///< Door service interface pointer. + IDoorService* m_pDoorService = nullptr; ///< Door service interface pointer. #ifdef _WIN32 - DWORD m_dwConsoleOutMode = 0u; ///< The console mode before switching on ANSI support. - DWORD m_dwConsoleInMode = 0u; ///< The console mode before switching on ANSI support. + DWORD m_dwConsoleOutMode = 0u; ///< The console mode before switching on ANSI support. + DWORD m_dwConsoleInMode = 0u; ///< The console mode before switching on ANSI support. #elif defined __unix__ - struct termios m_sTermAttr{}; ///< The terminal attributes before disabling echo. - int m_iFileStatus = 0; ///< The file status flags for STDIN. + struct termios m_sTermAttr{}; ///< The terminal attributes before disabling echo. + int m_iFileStatus = 0; ///< The file status flags for STDIN. #else #error The OS is not supported! #endif diff --git a/examples/door_demo_example/door_service/complex_service.h b/examples/door_demo_example/door_service/complex_service.h index c3b56fd..f25d963 100644 --- a/examples/door_demo_example/door_service/complex_service.h +++ b/examples/door_demo_example/door_service/complex_service.h @@ -19,14 +19,14 @@ #include // VSS interfaces - located in ../generated/vss_files/ -#include "../generated/vss_files/vss_vehiclebodydooraxle_01left_bs_rx.h" -#include "../generated/vss_files/vss_vehiclebodydooraxle_01left_bs_tx.h" -#include "../generated/vss_files/vss_vehiclebodydooraxle_01right_bs_rx.h" -#include "../generated/vss_files/vss_vehiclebodydooraxle_01right_bs_tx.h" -#include "../generated/vss_files/vss_vehiclebodydooraxle_02left_bs_rx.h" -#include "../generated/vss_files/vss_vehiclebodydooraxle_02left_bs_tx.h" -#include "../generated/vss_files/vss_vehiclebodydooraxle_02right_bs_rx.h" -#include "../generated/vss_files/vss_vehiclebodydooraxle_02right_bs_tx.h" +#include "../generated/front/vss_files/vss_vehiclebodydooraxle_01left_bs_rx.h" +#include "../generated/front/vss_files/vss_vehiclebodydooraxle_01left_bs_tx.h" +#include "../generated/front/vss_files/vss_vehiclebodydooraxle_01right_bs_rx.h" +#include "../generated/front/vss_files/vss_vehiclebodydooraxle_01right_bs_tx.h" +#include "../generated/rear/vss_files/vss_vehiclebodydooraxle_02left_bs_rx.h" +#include "../generated/rear/vss_files/vss_vehiclebodydooraxle_02left_bs_tx.h" +#include "../generated/rear/vss_files/vss_vehiclebodydooraxle_02right_bs_rx.h" +#include "../generated/rear/vss_files/vss_vehiclebodydooraxle_02right_bs_tx.h" #include "lock_doors_thread.h" diff --git a/examples/door_demo_example/door_service/lock_doors_thread.h b/examples/door_demo_example/door_service/lock_doors_thread.h index 510c138..0aa2cd3 100644 --- a/examples/door_demo_example/door_service/lock_doors_thread.h +++ b/examples/door_demo_example/door_service/lock_doors_thread.h @@ -40,7 +40,7 @@ public: m_timeIn100MilliSeconds = timeSpan; m_counter = 0; m_running = true; - m_thread = std::thread(&LockDoorsThread::run, this); + m_thread = sdv::core::secure_thread(&LockDoorsThread::run, this); } /** @@ -94,11 +94,11 @@ public: m_running = false; } - std::function m_callback; ///< callback function - std::atomic m_running; ///< status if loop is running - std::thread m_thread; ///< timer thread - uint32_t m_counter; ///< loop counter - uint32_t m_timeIn100MilliSeconds; ///< time before the callback will be executed + std::function m_callback; ///< callback function + std::atomic m_running; ///< status if loop is running + sdv::core::secure_thread m_thread; ///< timer thread + uint32_t m_counter; ///< loop counter + uint32_t m_timeIn100MilliSeconds; ///< time before the callback will be executed }; #endif // !define LOCK_DOORS_THREAD_H diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/model.cpp b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/model.cpp index fa4b743..d77b2e3 100644 --- a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/model.cpp +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/model.cpp @@ -100,7 +100,10 @@ bool CreateCoreServiceTomlFile(const std::string& resources) std::ofstream tomlFile("sdv_core_reloc.toml"); if (tomlFile.is_open()) { - tomlFile << "# Location of the SDV binaries and configuration files\ndirectory = \""; + tomlFile << R"toml(# Location of the SDV binaries and configuration files +[CoreLocation] +Version = 100 +Framework = ")toml"; tomlFile << resources; tomlFile << "\"\n"; tomlFile.close(); diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_left_door_example.toml b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_left_door_example.toml index 63c92da..e16120b 100644 --- a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_left_door_example.toml +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_left_door_example.toml @@ -2,11 +2,11 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorleft_rx.sdv" +Path = "frontdoors_vd_frontdoorleft_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Left_Device" [[Component]] -Path = "doors_bs_frontdoorleft_rx.sdv" +Path = "frontdoors_bs_frontdoorleft_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Left_Service" diff --git a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_right_door_example.toml b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_right_door_example.toml index 2aca0cd..05298a2 100644 --- a/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_right_door_example.toml +++ b/examples/door_demo_example/fmu_Doors2ExampleFMU/Doors2ExampleFMU/resources/front_right_door_example.toml @@ -2,10 +2,10 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorright_rx.sdv" +Path = "frontdoors_vd_frontdoorright_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Right_Device" [[Component]] -Path = "doors_bs_frontdoorright_rx.sdv" +Path = "frontdoors_bs_frontdoorright_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Right_Service" diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/model.cpp b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/model.cpp index 7dd9d35..77892c7 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/model.cpp +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/model.cpp @@ -125,7 +125,10 @@ bool CreateCoreServiceTomlFile(const std::string& resources) std::ofstream tomlFile("sdv_core_reloc.toml"); if (tomlFile.is_open()) { - tomlFile << "# Location of the SDV binaries and configuration files\ndirectory = \""; + tomlFile << R"toml(# Location of the SDV binaries and configuration files +[CoreLocation] +Version = 100 +Framework = ")toml"; tomlFile << resources; tomlFile << "\"\n"; tomlFile.close(); diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_left_door_example.toml b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_left_door_example.toml index e3162ea..142c14a 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_left_door_example.toml +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_left_door_example.toml @@ -2,11 +2,11 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorleft_rx.sdv" +Path = "frontdoors_vd_frontdoorleft_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Left_Device" [[Component]] -Path = "doors_bs_frontdoorleft_rx.sdv" +Path = "frontdoors_bs_frontdoorleft_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Left_Service" diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_right_door_example.toml b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_right_door_example.toml index 2aca0cd..05298a2 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_right_door_example.toml +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/front_right_door_example.toml @@ -2,10 +2,10 @@ Version = 100 [[Component]] -Path = "doors_vd_frontdoorright_rx.sdv" +Path = "frontdoors_vd_frontdoorright_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Right_Device" [[Component]] -Path = "doors_bs_frontdoorright_rx.sdv" +Path = "frontdoors_bs_frontdoorright_rx.sdv" Class = "Vehicle.Body.Door.Axle._01.Right_Service" diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_left_door_example.toml b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_left_door_example.toml index 23e6935..b4a5871 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_left_door_example.toml +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_left_door_example.toml @@ -2,11 +2,11 @@ Version = 100 [[Component]] -Path = "doors_vd_reardoorleft_rx.sdv" +Path = "reardoors_vd_reardoorleft_rx.sdv" Class = "Vehicle.Body.Door.Axle._02.Left_Device" [[Component]] -Path = "doors_bs_reardoorleft_rx.sdv" +Path = "reardoors_bs_reardoorleft_rx.sdv" Class = "Vehicle.Body.Door.Axle._02.Left_Service" diff --git a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_right_door_example.toml b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_right_door_example.toml index 4f2069e..64acf61 100644 --- a/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_right_door_example.toml +++ b/examples/door_demo_example/fmu_Doors4ExampleFMU/Doors4ExampleFMU/resources/rear_right_door_example.toml @@ -2,9 +2,9 @@ Version = 100 [[Component]] -Path = "doors_vd_reardoorright_rx.sdv" +Path = "reardoors_vd_reardoorright_rx.sdv" Class = "Vehicle.Body.Door.Axle._02.Right_Device" [[Component]] -Path = "doors_bs_reardoorright_rx.sdv" +Path = "reardoors_bs_reardoorright_rx.sdv" Class = "Vehicle.Body.Door.Axle._02.Right_Service" diff --git a/examples/door_demo_example/vss_doors_example.csv b/examples/door_demo_example/vss_doors_front_example.csv similarity index 53% rename from examples/door_demo_example/vss_doors_example.csv rename to examples/door_demo_example/vss_doors_front_example.csv index 005566f..f3e4e6e 100644 --- a/examples/door_demo_example/vss_doors_example.csv +++ b/examples/door_demo_example/vss_doors_front_example.csv @@ -2,17 +2,9 @@ ;;;;;;; VD;FrontDoorLeft;IsOpen;leftDoorIsOpen01;Vehicle.Body.Door.Axle.01.Left;RX;boolean;CAN_Input_L1.Door01LeftIsOpen VD;FrontDoorRight;IsOpen;rightDoorIsOpen01;Vehicle.Body.Door.Axle.01.Right;RX;boolean;CAN_Input_R1.Door01RightIsOpen -VD;RearDoorLeft;IsOpen;leftDoorIsOpen02;Vehicle.Body.Door.Axle.02.Left;RX;boolean;CAN_Input_L2.Door02LeftIsOpen -VD;RearDoorRight;IsOpen;rightDoorIsOpen02;Vehicle.Body.Door.Axle.02.Right;RX;boolean;CAN_Input_R2.Door02RightIsOpen VD;FrontDoorLeft;Lock;leftLatch01;Vehicle.Body.Door.Axle.01.Left;TX;boolean;CAN_Output.LockDoor01Left VD;FrontDoorRight;Lock;rightLatch01;Vehicle.Body.Door.Axle.01.Right;TX;boolean;CAN_Output.LockDoor01Right -VD;RearDoorLeft;Lock;leftLatch02;Vehicle.Body.Door.Axle.02.Left;TX;boolean;CAN_Output.LockDoor02Left -VD;RearDoorRight;Lock;rightLatch02;Vehicle.Body.Door.Axle.02.Right;TX;boolean;CAN_Output.LockDoor02Right BS;FrontDoorLeft;IsOpenL1;leftDoorIsOpen01;Vehicle.Body.Door.Axle.01.Left;RX;boolean;Vehicle.Body.Door.Axle.01.Left BS;FrontDoorRight;IsOpenR1;rightDoorIsOpen01;Vehicle.Body.Door.Axle.01.Right;RX;boolean;Vehicle.Body.Door.Axle.01.Right -BS;RearDoorLeft;IsOpenL2;leftDoorIsOpen02;Vehicle.Body.Door.Axle.02.Left;RX;boolean;Vehicle.Body.Door.Axle.02.Left -BS;RearDoorRight;IsOpenR2;rightDoorIsOpen02;Vehicle.Body.Door.Axle.02.Right;RX;boolean;Vehicle.Body.Door.Axle.02.Right BS;FrontDoorLeft;Lock;leftLatch01;Vehicle.Body.Door.Axle.01.Left;TX;boolean;Vehicle.Body.Door.Axle.01.Left BS;FrontDoorRight;Lock;rightLatch01;Vehicle.Body.Door.Axle.01.Right;TX;boolean;Vehicle.Body.Door.Axle.01.Right -BS;RearDoorLeft;Lock;leftLatch02;Vehicle.Body.Door.Axle.02.Left;TX;boolean;Vehicle.Body.Door.Axle.02.Left -BS;RearDoorRight;Lock;rightLatch02;Vehicle.Body.Door.Axle.02.Right;TX;boolean;Vehicle.Body.Door.Axle.02.Right diff --git a/examples/door_demo_example/vss_doors_rear_example.csv b/examples/door_demo_example/vss_doors_rear_example.csv new file mode 100644 index 0000000..285c0e5 --- /dev/null +++ b/examples/door_demo_example/vss_doors_rear_example.csv @@ -0,0 +1,10 @@ +;Class name;Function name;Signal name;vss;Signal direction;type;DBC CAN name includes CAN message name +;;;;;;; +VD;RearDoorLeft;IsOpen;leftDoorIsOpen02;Vehicle.Body.Door.Axle.02.Left;RX;boolean;CAN_Input_L2.Door02LeftIsOpen +VD;RearDoorRight;IsOpen;rightDoorIsOpen02;Vehicle.Body.Door.Axle.02.Right;RX;boolean;CAN_Input_R2.Door02RightIsOpen +VD;RearDoorLeft;Lock;leftLatch02;Vehicle.Body.Door.Axle.02.Left;TX;boolean;CAN_Output.LockDoor02Left +VD;RearDoorRight;Lock;rightLatch02;Vehicle.Body.Door.Axle.02.Right;TX;boolean;CAN_Output.LockDoor02Right +BS;RearDoorLeft;IsOpenL2;leftDoorIsOpen02;Vehicle.Body.Door.Axle.02.Left;RX;boolean;Vehicle.Body.Door.Axle.02.Left +BS;RearDoorRight;IsOpenR2;rightDoorIsOpen02;Vehicle.Body.Door.Axle.02.Right;RX;boolean;Vehicle.Body.Door.Axle.02.Right +BS;RearDoorLeft;Lock;leftLatch02;Vehicle.Body.Door.Axle.02.Left;TX;boolean;Vehicle.Body.Door.Axle.02.Left +BS;RearDoorRight;Lock;rightLatch02;Vehicle.Body.Door.Axle.02.Right;TX;boolean;Vehicle.Body.Door.Axle.02.Right diff --git a/examples/open_trunk_example/open_trunk_app/console.cpp b/examples/open_trunk_example/open_trunk_app/console.cpp index 1975242..612318b 100644 --- a/examples/open_trunk_example/open_trunk_app/console.cpp +++ b/examples/open_trunk_example/open_trunk_app/console.cpp @@ -34,9 +34,11 @@ const CConsole::SConsolePos g_sComment3{ 24, 1 }; const CConsole::SConsolePos g_sSeparator5{ 26, 1 }; const CConsole::SConsolePos g_sOpenViaDevice{ 28, 1 }; const CConsole::SConsolePos g_sOpenViaService{ 29, 1 }; -const CConsole::SConsolePos g_sSeparator6{ 31, 1 }; -const CConsole::SConsolePos g_sControlDescription{ 33, 1 }; -const CConsole::SConsolePos g_sCursor{ 34, 1 }; +const CConsole::SConsolePos g_sAutoCloseTrunk{ 30, 1 }; +const CConsole::SConsolePos g_sStatusTrunk{ 32, 1 }; +const CConsole::SConsolePos g_sSeparator6{ 34, 1 }; +const CConsole::SConsolePos g_sControlDescription{ 36, 1 }; +const CConsole::SConsolePos g_sCursor{ 37, 1 }; CConsole::CConsole() { @@ -92,6 +94,7 @@ void CConsole::PrintHeader(uint32_t uiInstance) { subTtitle = "Connected to core instance "; subTtitle.append(std::to_string(uiInstance)); + subTtitle.append(" (not all interfaces available)"); } // Print the titles PrintText(g_sTitle, "Open Trunk example: Open trunk when vehicle is not moving"); @@ -108,7 +111,7 @@ void CConsole::PrintHeader(uint32_t uiInstance) PrintText(g_sSeparator4, "============================================================================"); PrintText(g_sComment1, "The Open Trunk signal has a safety request."); PrintText(g_sComment2, "Therefore the speed value is required and must be checked."); - PrintText(g_sComment3, "If the car is moving, open is blocked and trunk is closed automatically."); + PrintText(g_sComment3, "If the vehicle is moving, open is blocked and trunk is closed automatically."); PrintText(g_sSeparator5, "============================================================================"); PrintText(g_sOpenViaDevice, "Vehicle Device Interface not available."); PrintText(g_sOpenViaService, "Basic Service Interface not available."); @@ -124,7 +127,7 @@ bool CConsole::PrepareDataConsumers() if (m_SignalSpeed) PrintValue(g_sDataLinkSpeed, "Vehicle Speed RX", m_SpeedDL, "m/s"); - // Registrate for the vehicle device & basic service of the speed. + // Register for the vehicle device & basic service of the speed. auto vehicleDevice = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface(); if (vehicleDevice) { @@ -153,6 +156,10 @@ bool CConsole::PrepareDataConsumers() else PrintText(g_sOpenViaDevice, "Trunk Device NOT available"); + m_SignalStatusTrunk = dispatch.RegisterTxSignal(trunk::dsTrunk, 0); + if (!m_SignalStatusTrunk) + PrintText(g_sStatusTrunk, "Trunk Status not available."); + return true; } @@ -172,6 +179,9 @@ void CConsole::ResetSignals() // Unregister the data link signals if (m_SignalSpeed) m_SignalSpeed.Reset(); + + if (m_SignalStatusTrunk) + m_SignalStatusTrunk.Reset(); } void CConsole::CallbackSpeed(sdv::any_t value) @@ -246,7 +256,13 @@ void CConsole::SetSpeed(float value) if (bIsMoving && !m_bWasMovingBS) { - PrintText(g_sOpenViaService, "Safety active: speed > 0, trunk close command sent."); + if (m_pTrunkService) + { + if (m_pTrunkService->SetOpen(false)) + PrintText(g_sAutoCloseTrunk, "Safety feature active: Trunk closed as the vehicle started moving."); + else + PrintText(g_sOpenViaService, "Close trunk automatically failed."); + } } m_bWasMovingBS = bIsMoving; } @@ -278,9 +294,24 @@ char CConsole::GetChar() void CConsole::RunUntilBreak() { bool bRunning = true; + bool bTrunkClosed = true; while (bRunning) { + if(m_SignalStatusTrunk) + { + auto value =m_SignalStatusTrunk.Read().get(); + if (bTrunkClosed != value) + { + bTrunkClosed = value; + if (bTrunkClosed) + PrintText(g_sStatusTrunk, "Status: Trunk is open."); + else + PrintText(g_sStatusTrunk, "Status: Trunk is closed."); + } + } + + // Check for a key if (!KeyHit()) { @@ -294,17 +325,19 @@ void CConsole::RunUntilBreak() { case 'c': case 'C': - PrintText(g_sOpenViaDevice, " "); - PrintText(g_sOpenViaService, " "); + PrintText(g_sOpenViaDevice, " "); + PrintText(g_sOpenViaService, " "); + PrintText(g_sAutoCloseTrunk, " "); break; case 'd': case 'D': if (m_pTrunkDevice) { + PrintText(g_sAutoCloseTrunk, " "); if (m_pTrunkDevice->WriteOpen(true)) { if (m_SpeedVD || m_SpeedBS) - PrintText(g_sOpenViaDevice, "Trunk opened directly although car was moving, not safe."); + PrintText(g_sOpenViaDevice, "Trunk opened directly although vehicle was moving, not safe."); else PrintText(g_sOpenViaDevice, "Trunk opened directly, not safe."); } @@ -316,10 +349,11 @@ void CConsole::RunUntilBreak() case 'O': if (m_pTrunkService) { + PrintText(g_sAutoCloseTrunk, " "); if (m_pTrunkService->SetOpen(true)) PrintText(g_sOpenViaService, "Trunk opened safely (auto-close if vehicle starts moving)."); else - PrintText(g_sOpenViaService, "Open trunk failed, car is moving."); + PrintText(g_sOpenViaService, "Open trunk failed, vehicle is moving."); } break; case 'x': diff --git a/examples/open_trunk_example/open_trunk_app/console.h b/examples/open_trunk_example/open_trunk_app/console.h index 326e833..0ce6338 100644 --- a/examples/open_trunk_example/open_trunk_app/console.h +++ b/examples/open_trunk_example/open_trunk_app/console.h @@ -157,6 +157,7 @@ private: mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message bool m_bRunning = false; ///< When set, the application is running. + sdv::core::CSignal m_SignalStatusTrunk; ///< Status of the trunk, only available in the standalone example sdv::core::CSignal m_SignalSpeed; ///< Speed float m_SpeedDL = 0.0; ///< Speed Data Link float m_SpeedVD = 0.0; ///< Speed Vehicle Device diff --git a/examples/system_demo_example/example_app/console.cpp b/examples/system_demo_example/example_app/console.cpp index f9b588e..114ec69 100644 --- a/examples/system_demo_example/example_app/console.cpp +++ b/examples/system_demo_example/example_app/console.cpp @@ -209,8 +209,7 @@ bool CConsole::PrepareDataConsumers() m_pCounterSteeringSvc = sdv::core::GetObject("Counter Steering Example Service").GetInterface(); if (!m_pCounterSteeringSvc) { - std::cerr << "Console ERROR: Could not get complex service interface 'ICounterSteeringService'" << std::endl; - return false; + PrintText(g_sCSActive, "Attention: Complex service 'CounterSteeringService' is not available."); } return true; diff --git a/examples/system_demo_example/example_app/control.cpp b/examples/system_demo_example/example_app/control.cpp index 5090b88..3eac499 100644 --- a/examples/system_demo_example/example_app/control.cpp +++ b/examples/system_demo_example/example_app/control.cpp @@ -103,7 +103,7 @@ void CExampleControl::StartTestRun() // Start the simulated datalink m_bRunning = true; - m_threadSimulateDatalink = std::thread(&CExampleControl::SimulateDatalinkThreadFunc, this); + m_threadSimulateDatalink = sdv::core::secure_thread(&CExampleControl::SimulateDatalinkThreadFunc, this); } void CExampleControl::StopTestRun() diff --git a/examples/system_demo_example/example_app/control.h b/examples/system_demo_example/example_app/control.h index c6d8e7e..5a343f6 100644 --- a/examples/system_demo_example/example_app/control.h +++ b/examples/system_demo_example/example_app/control.h @@ -163,20 +163,20 @@ private: verbose, ///< Extensive reporting by application control }; - bool m_bInitialized = false; ///< Set when initialized. - bool m_bCmdLnError = false; ///< Command line error occurred. - bool m_bCmdLnHelp = false; ///< Command line help provided. - std::atomic_bool m_bRunning = false; ///< When set, the application is running. - ERunAs m_eRunAs = ERunAs::standalone; ///< Application operation. - EAppControlReporting m_eReporting = EAppControlReporting::silent; ///< Application control reporting. - uint32_t m_uiInstance = 1000u; ///< Server instance to connect to. - std::filesystem::path m_pathFramework; ///< Path to the SDV V-API framework. - sdv::app::CAppControl m_appcontrol; ///< App-control of SDV V-API. - std::thread m_threadSimulateDatalink; ///< Simulation datalink thread. - sdv::core::CSignal m_signalSteeringWheel; ///< Steering wheel angle signal (input) - simulated datalink - sdv::core::CSignal m_signalSpeed; ///< Speed signal (input) - simulated datalink - sdv::core::CSignal m_signalRearAngle; ///< Rear angle signal (output) - simulated datalink - sdv::core::CSignal m_signalCounter; ///< Simple counter signal (output) - simulated datalink + bool m_bInitialized = false; ///< Set when initialized. + bool m_bCmdLnError = false; ///< Command line error occurred. + bool m_bCmdLnHelp = false; ///< Command line help provided. + std::atomic_bool m_bRunning = false; ///< When set, the application is running. + ERunAs m_eRunAs = ERunAs::standalone; ///< Application operation. + EAppControlReporting m_eReporting = EAppControlReporting::silent; ///< Application control reporting. + uint32_t m_uiInstance = 1000u; ///< Server instance to connect to. + std::filesystem::path m_pathFramework; ///< Path to the SDV V-API framework. + sdv::app::CAppControl m_appcontrol; ///< App-control of SDV V-API. + sdv::core::secure_thread m_threadSimulateDatalink; ///< Simulation datalink thread. + sdv::core::CSignal m_signalSteeringWheel; ///< Steering wheel angle signal (input) - simulated datalink + sdv::core::CSignal m_signalSpeed; ///< Speed signal (input) - simulated datalink + sdv::core::CSignal m_signalRearAngle; ///< Rear angle signal (output) - simulated datalink + sdv::core::CSignal m_signalCounter; ///< Simple counter signal (output) - simulated datalink }; template diff --git a/examples/system_demo_example/fmu/model.cpp b/examples/system_demo_example/fmu/model.cpp index 3983302..c75a484 100644 --- a/examples/system_demo_example/fmu/model.cpp +++ b/examples/system_demo_example/fmu/model.cpp @@ -103,7 +103,10 @@ bool CreateCoreServiceTomlFile(const std::string& resources) std::ofstream tomlFile("sdv_core_reloc.toml"); if (tomlFile.is_open()) { - tomlFile << "# Location of the SDV binaries and configuration files\ndirectory = \""; + tomlFile << R"toml(# Location of the SDV binaries and configuration files +[CoreLocation] +Version = 100 +Framework = ")toml"; tomlFile << resources; tomlFile << "\"\n"; tomlFile.close(); diff --git a/export/interfaces/app.idl b/export/interfaces/app.idl index 4efcaff..5357769 100644 --- a/export/interfaces/app.idl +++ b/export/interfaces/app.idl @@ -103,12 +103,6 @@ module sdv * @return The instance ID. */ uint32 GetInstanceID() const; - - /** - * @brief Return the number of retries to establish a connection. - * @return Number of retries. - */ - uint32 GetRetries() const; }; /** @@ -181,5 +175,96 @@ module sdv void RequestShutdown() raises(XAccessDenied, XInvalidState); }; + /** + * @brief Load and save the application settings. + * @remarks The interface is only available in maintenance mode. + */ + interface IAppSettingsPersist + { + /** + * @brief Load the application settings. + * @remarks When there is no settings file, this is not an error. Default settings will be assumed. + * @return Returns whether the loading was successful. + */ + boolean LoadSettings(); + + /** + * @brief Save the application settings file (or create when not existing yet). + * @return Returns whether the saving was successful. + */ + boolean SaveSettings(); + }; + + /** + * @brief Application connections + * @remarks The interface is only available in maintenance mode. + */ + interface IAppConnections + { + /** + * @brief Get a sequence with listener names. + * @return Sequence with listener name strings. + */ + sequence GetListeners() const; + + /** + * @brief Get the listener configuration. + * @param[in] ssName Name of the listener. + * @return String containing the listener configuration. + */ + u8string GetListenerConfig(in u8string ssName) const; + + /** + * @brief Add or update a listener configuration. + * @param[in] ssName Name of the listener configuration. + * @param[in] ssConfig The configuration string for the listener. + * @return Returns whether the listener could be added. + */ + boolean AddListenerConfig(in u8string ssName, in u8string ssConfig); + + /** + * @brief Remove a listener configuration with the provided name. + * @param[in] ssName Name of the listener configuration. + * @return Returns whether the removal was successful. + */ + boolean RemoveListenerConfig(in u8string ssName); + + /** + * @brief Get a sequence with connection names. + * @return Sequence with name strings. + */ + sequence GetConnections() const; + + /** + * @brief Get the connection configuration. + * @param[in] ssName Name of the connection. + * @return String containing the connection configuration. + */ + u8string GetConnectionConfig(in u8string ssName) const; + + /** + * @brief Add or update a connection configuration. + * @param[in] ssName Name of the connection configuration. + * @param[in] ssConfig The configuration string for the connection. + * @param[in] ssInsertBefore Reference to the string to connection to insert the the new connection before, or empty + * when the the new connection should be placed at the end. + * @return Returns whether the connection could be added (fails when the connection already exists). + */ + boolean AddConnectionConfig(in u8string ssName, in u8string ssConfig, in u8string ssInsertBefore); + + /** + * @brief Remove a connection configuration with the provided name. + * @param[in] ssName Name of the connection configuration. + * @return Returns whether the removal was successful. + */ + boolean RemoveConnectionConfig(in u8string ssName); + + /** + * @brief Return the number of retries to establish a connection. + * @return Number of retries. + */ + uint32 GetConnectRetries() const; + }; + }; // module app }; // module sdv diff --git a/export/interfaces/com.idl b/export/interfaces/com.idl index 2f2d392..e82fcd8 100644 --- a/export/interfaces/com.idl +++ b/export/interfaces/com.idl @@ -53,31 +53,56 @@ module sdv }; /** - * @brief Connect to a remote system. Provided by the ConnectClient utility. + * @brief Connect to a remote system. Provided by the Connection service. + * @details This interface is exposed by the connection client service. The configuration is provided during instantiation + * of the object and is of the form: + * @code + * # Provider to use for the connection + * [Provider] + * Name = "" + * + * # Additional channel information for the client + * [IpcChannel] + * xyz = "" + * @endcode + * + * For example for shared memory: + * @code + * [Provider] + * Name = "DefaultSharedMemory" + * [IpcChannel] + * Name = "LISTENER_1234" + * @endcode */ interface IClientConnect { /** - * @brief Connect to a remote system using the connection string to contact the system. - * @remarks After a successful connection, the ConnectClient utility is not needed any more. - * @param[in] ssConnectString Optional connection string to use for connection. If not provided, the connection will - * automatically get the connection ID from the app-control service (default). The connection string for a local - * connection can be of the form: - * @code - * [Client] - * Type = "local" - * Instance = 1234 # Optional: only use when connecting to a system with a different instance ID. - * @endcode - * And the following can be used for a remote connection: - * @code - * [Client] - * Type = "remote" - * Interface = "127.0.0.1" - * Port = 2000 - * @endcode - * @return Returns an interface to the repository of the remote system or a NULL pointer if not found. + * @brief Connect to a remote system. + * @return Returns whether connect was successful. */ - IInterfaceAccess Connect(in u8string ssConnectString) raises(XAccessDenied, XNotFound, XInvalidState, XTimeout); + boolean Connect() raises(XAccessDenied, XNotFound, XInvalidState, XTimeout); + + /** + * @brief Disconnect from a connected system. + * @return Returns whether disconnect was successful. + */ + boolean Disconnect() raises(XAccessDenied, XNotFound, XInvalidState, XTimeout); + + /** + * @brief State of the current connection. + * @return Returns whether an active connection exists. + */ + boolean IsConnected() const; + + /** + * @brief Get the remote repository that is available after connection. + * @remarks For main, isolated and external applications, the remote repository will be automatically linked to the + * local repository. Hence a requests for the repository is not needed. For all other applications, access must be + * explicitly acquired through this interface. + * @return Interface to the remote repository if a successful connection is established. The interface is valid until + * disconnect is called or the client connection service is terminated. + */ + IInterfaceAccess GetRemoteRepository(); }; /** diff --git a/export/interfaces/config.idl b/export/interfaces/config.idl index a598df2..6c44b9f 100644 --- a/export/interfaces/config.idl +++ b/export/interfaces/config.idl @@ -389,6 +389,17 @@ module sdv u8string ssInstallName; ///< Name of the installation. }; + /** + * @brief The package exceeds the maximum size. + */ + exception XPackageSizeExceeded : XSysExcept + { + /** Description */ + const char _description[] = "Maximum package size exceeded."; + + u8string ssFileName; ///< Name of the file which causes the exception. + }; + /** * @brief The installation doesn't contain a module. */ diff --git a/export/interfaces/core.idl b/export/interfaces/core.idl index 3685736..d537a35 100644 --- a/export/interfaces/core.idl +++ b/export/interfaces/core.idl @@ -57,11 +57,28 @@ module sdv template TInterface* GetInterface() { - return GetInterface(sdv::GetInterfaceId()).template get(); + try + { + return GetInterface(sdv::GetInterfaceId()).template get(); + } catch (const XSysExcept&) + { + return nullptr; + } } #verbatim_end }; + /** + * @brief Core features. + */ + module core + { + /** + * @brief Object ID. + */ + typedef uint64 TObjectID; + }; + /** * @brief Object type enumeration. */ @@ -163,13 +180,24 @@ module sdv /** * @brief Component operation mode. - */ + */ enum EOperationMode : uint32 { configuring = 20, ///< The component should switch to configuration mode. running = 30, ///< The component should switch to running mode. }; + /** + * @brief Object information supplied to the object initialization function. + */ + struct SObjectInfo + { + core::TObjectID tObjectID; ///< Object ID (local for the running process) + SClassInfo sClassInfo; ///< Object class information + u8string ssName; ///< Object name + u8string ssConfig; ///< Object configuration TOML + }; + /** * @brief Optional interface allowing for additional control of more complex SDVObjects. * To be implemented if the object in question calls other SDVobjects via running threads or callbacks, @@ -179,9 +207,9 @@ module sdv { /** * @brief Initialize the object. - * @param[in] ssObjectConfig Optional configuration string. + * @param[in] sObjectInfo The registration information of this object. */ - void Initialize(in u8string ssObjectConfig); + void Initialize(in SObjectInfo sObjectInfo); /** * @brief Get the current state of the object. @@ -247,50 +275,6 @@ module sdv */ uint32 GetCount() const; }; - - /** - * @brief Attribute flags. - */ - enum EAttributeFlags : uint32 - { - read_only = 0x100, ///< When set, the attribute is readonly. - }; - - /** - * @brief Attribute interface - */ - interface IAttributes - { - /** - * @brief Get a sequence with the available attribute names. - * @return The sequence of attribute names. - */ - sequence GetNames() const; - - /** - * @brief Get the attribute value. - * @param[in] ssAttribute Name of the attribute. - * @return The attribute value or an empty any-value if the attribute wasn't found or didn't have a value. - */ - any Get(in u8string ssAttribute) const; - - /** - * @brief Set the attribute value. - * @param[in] ssAttribute Name of the attribute. - * @param[in] anyAttribute Attribute value to set. - * @return Returns 'true' when setting the attribute was successful or 'false' when the attribute was not found or the - * attribute is read-only or another error occurred. - */ - boolean Set(in u8string ssAttribute, in any anyAttribute); - - /** - * @brief Get the attribute flags belonging to a certain attribute. - * @param[in] ssAttribute Name of the attribute. - * @return Returns the attribute flags (zero or more EAttributeFlags flags) or 0 when the attribute could not be found. - */ - uint32 GetFlags(in u8string ssAttribute) const; - }; - }; // module sdv #verbatim_begin diff --git a/export/interfaces/core_ps.idl b/export/interfaces/core_ps.idl index 04d4df3..c72a873 100644 --- a/export/interfaces/core_ps.idl +++ b/export/interfaces/core_ps.idl @@ -13,6 +13,7 @@ #include "core.idl" #include "process.idl" +#include "permission.idl" /** * @brief Software Defined Vehicle framework. @@ -149,6 +150,7 @@ module sdv TMarshallID tProxyID; ///< Proxy id to identify the proxy this packet is sending from or destined to. TMarshallID tStubID; ///< Stub id to identify the stub this packet is destined to or receiving from. uint64 uiCallIndex; ///< Call index to uniquely identify the call. + core::TPermissionID tPermissionID; ///< Permission ID used to elevate permission, or 0 for standard permission. }; /** diff --git a/export/interfaces/ipc.idl b/export/interfaces/ipc.idl index 75cd4a6..ab623c0 100644 --- a/export/interfaces/ipc.idl +++ b/export/interfaces/ipc.idl @@ -35,12 +35,15 @@ module sdv ///< identification must be similar to the following example: ///< @code ///< [Provider] - ///< Name = "DefaultSharedMemoryChannelControl" + ///< Name = "DefaultSharedMemory" ///< @endcode }; /** - * @brief Interface for creating an IPC connection, which other participants can connect to via IChannelAccess + * @brief Interface for creating an IPC connection, which other participants can connect to via IChannelAccess. + * @attention The channel control object should allow the creation of channels with a fixed configuration (e.g. a provided + * name or a port), which is necessary for the creation of a listener. It also should allow dynamic channel creation (e.g. + * automatic name or port-number) to create additional connections between components. */ interface ICreateEndpoint { diff --git a/export/interfaces/permission.idl b/export/interfaces/permission.idl new file mode 100644 index 0000000..f1a3fa3 --- /dev/null +++ b/export/interfaces/permission.idl @@ -0,0 +1,96 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include "core.idl" + +/** + * @brief Software Defined Vehicle framework. + */ +module sdv +{ + /** + * @brief Core features. + */ + module core + { + /// ID corresponding to a thread specific permission request. + typedef uint64 TPermissionID; + + /// ID identifying a permission transfer. + typedef uint64 TPermissionTransferID; + + /** + * @brief Access type restriction. The lower the access, the more restricted. + */ + enum EAccessPermission : int32 + { + not_set = -1001, ///< Permissions are not set; will be treated as restricted access. + restricted_access = -1000, ///< Restricted access (default when no other set). + remote_access = -200, ///< Access for remote execution. Access allowed to basic and complex services. + local_access = -100, ///< Access for isolated execution. Access allowed to system, basic and complex services. + full_access = 0, ///< Full access is allowed to complete system. + }; + + /** + * @brief Permission control interface. This interface is used for restricting permission on a thread and transferring + * permissions from one thread to another. + */ + interface IPermissionControl + { + /** + * @brief Restrict the access permission for the current thread. + * @remarks The access restriction will be assigned to the current thread and combined with previous and future + * permissions. The lowest assigned permission will determine the actual access permission for the current thread. + * @remarks The access restriction will stay in effect until it is released by the function ReleaseAccessPermission. + * @remarks A newly created thread has fully restricted access. This cannot be changed using this function. Use an + * access restriction transfer from one thread to this thread to set a higher level of access permission. + * @param[in] ePermission The permission to restrict to. + * @return The permission ID for this restriction or 0 when the access permission could not be set. Use the + * ReleaseAccessPermission to release the restriction again. + */ + TPermissionID RestrictAccessPermission(in EAccessPermission ePermission); + + /** + * @brief Release a previously set access restriction for the current thread. + * @param[in] tPermissionID The ID of the access restriction previously set for the current thread. + * @return Returns whether the restriction could be released successfully. + */ + boolean ReleaseAccessPermission(in TPermissionID tPermissionID); + + /** + * @brief Prepares a transfer of the access restriction from the current thread. + * @return The ID of the transfer object containing the current access permissions or 0 when the transfer preparation + * has failed. + */ + TPermissionTransferID TransferCurrentPermission(); + + /** + * @brief Set the access permission using a transfer object from one thread to another. + * @remarks An new thread has fully restricted access per default. Use this function to set the required access level. + * If a thread has already initialized with the proper access level, this function will set identical or lower access + * permissions for the current thread. + * @param[in] tTransferID The IS of the prepared access permission transfer. + * @return The permission ID for this restriction or 0 when the access permission could not be transferred. Use the + * ReleaseAccessPermission to release the restriction again. + */ + TPermissionID SetAccessPermission(in TPermissionTransferID tTransferID); + + /** + * @brief Get the access permission level for the current thread. This will be the lowest restriction set for the + * current thread. + * @return The current access permission level. + */ + EAccessPermission GetCurrentPermission() const; + }; + }; // module core +}; // module sdv diff --git a/export/interfaces/repository.idl b/export/interfaces/repository.idl index 6965a02..527293a 100644 --- a/export/interfaces/repository.idl +++ b/export/interfaces/repository.idl @@ -25,11 +25,6 @@ module sdv module core { - /** - * @brief Object ID. - */ - typedef uint64 TObjectID; - /** * @brief Interface used to access objects from other modules via the repository service */ @@ -229,6 +224,32 @@ module sdv TObjectID RegisterObject(in IInterfaceAccess pObjectIfc, in u8string ssObjectName); }; + /** + * @brief The object dependency can be used to state dependencies between two objects. This influence is used during + * shutdown to determine the termination order. + */ + interface IObjectDependency + { + /** + * @brief Add a dependency for an object. + * @param ssObjectName Name of the object. + * @param ssDependsOnObject Name of the object it depends on. + */ + void AddObjectDependency(in u8string ssObjectName, in u8string ssDependsOnObject); + + /** + * @brief Remove an object dependency. + * @param ssObjectName Name of the object. + * @param ssDependsOnObject Name of the object it depends on. + */ + void RemoveObjectDependency(in u8string ssObjectName, in u8string ssDependsOnObject); + }; + + /** + * @brief Link ID. + */ + typedef uint64 TLinkID; + /** * @brief Connect the core repository to the local repository to allow object access by the locally running components. * @remarks This interface is available only for isolated and external applications. @@ -238,13 +259,15 @@ module sdv /** * @brief Register the core repository. * @param[in] pCoreRepository Pointer to the proxy interface of the core repository. + * @return Returns a link ID to be used in the Unlink function. Or 0 on failure. */ - void LinkCoreRepository(in IInterfaceAccess pCoreRepository); + TLinkID LinkCoreRepository(in IInterfaceAccess pCoreRepository); /** * @brief Unlink a previously linked core repository. + * @param[in] tLinkID Link ID of the repository link to remove. */ - void UnlinkCoreRepository(); + void UnlinkCoreRepository(in TLinkID tLinkID); }; }; // module core diff --git a/export/interfaces/toml.idl b/export/interfaces/toml.idl index 0f94af6..98d6852 100644 --- a/export/interfaces/toml.idl +++ b/export/interfaces/toml.idl @@ -142,10 +142,10 @@ module sdv u8string GetComment(in ECommentType eType); /** - * @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments - * will not be changed. + * @brief Format the node automatically, remove redundant whitespace. + * @param[in] bRemoveComments When set, the comments are removed from the node. */ - void AutomaticFormat(); + void AutomaticFormat(in boolean bRemoveComments); /** * @brief Is the node inline? @@ -265,10 +265,21 @@ module sdv /** * @brief Convert the node to a standard node. + * @param[in] bIncludeChildren When set, applicable child nodes are made are converted to standard nodes as well (only + * tables and table-arrays can be defined as standard). * @return Returns whether the conversion was successful. Returns 'true' when the node was already defined as standard * node. */ - boolean MakeStandard(); + boolean MakeStandard(in boolean bIncludeChildren); + }; + + /** + * @brief Insertion preference for tables and table arrays, being standard or inline. + */ + enum EInsertPreference + { + prefer_standard = 0, ///< When the parent node is not inline, the node will be inserted as standard node. + prefer_inline = 1, ///< The node will be inserted as inline node. }; /** @@ -278,71 +289,67 @@ module sdv { /** * @brief Insert a value into the collection at the location before the supplied index. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Value nodes cannot be inserted behind external - * tables and table arrays. If the index is referencing a position behind an external table or a table array, the index - * is automatically corrected. - * @param[in] ssName Name of the node to insert. Will be ignored for an array collection. The name must adhere to the - * key names defined by the TOML specification. Defining the key multiple times is not allowed. Quotation of key names - * is done automatically; the parser decides itself whether the key is bare-key, a literal key or a quoted key. + * @remarks In TOML, inline nodes are located before standard nodes. Since values are presented as inline node, they + * will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. + * @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if + * not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty + * string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML + * specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the + * parser decides itself whether the key is bare-key, a literal key or a quoted key. * @param[in] anyValue The value of the node, being either an integer, floating point number, boolean value or a string. * Conversion is automatically done to int64, double float, bool or u8string. * @return On success the interface to the newly inserted node is returned or NULL otherwise. */ - IInterfaceAccess InsertValue(in uint32 uiIndex, in u8string ssName, in any anyValue); + IInterfaceAccess InsertValue(in u8string ssInsertBefore, in u8string ssName, in any anyValue); /** * @brief Insert an array into the collection at the location before the supplied index. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Array nodes cannot be inserted behind external - * tables and table arrays. If the index is referencing a position behind an external table or a table array, the index - * is automatically corrected. - * @param[in] ssName Name of the array node to insert. Will be ignored if the current node is also an array collection. - * The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not - * allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a - * literal key or a quoted key. + * @remarks In TOML, inline nodes are located before standard nodes. Since arrays are presented as inline node, they + * will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. + * @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if + * not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty + * string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML + * specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the + * parser decides itself whether the key is bare-key, a literal key or a quoted key. * @return On success the interface to the newly inserted node is returned or NULL otherwise. */ - IInterfaceAccess InsertArray(in uint32 uiIndex, in u8string ssName); - - /** - * @brief Insertion preference for tables and table arrays, being standard or inline. - */ - enum EInsertPreference - { - prefer_standard = 0, ///< When the parent node is not inline, the node will be inserted as standard node. - prefer_inline = 1, ///< The node will be inserted as inline node. - }; + IInterfaceAccess InsertArray(in u8string ssInsertBefore, in u8string ssName); /** * @brief Insert a table into the collection at the location before the supplied index. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Table nodes cannot be inserted before value nodes - * or arrays. If the index is referencing a position before a value node or an array, the index is automatically - * corrected. - * @param[in] ssName Name of the table node to insert. Will be ignored if the parent node is an array collection. - * The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not - * allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a - * literal key or a quoted key. + * @remarks In TOML, inline nodes are located before standard nodes. Tables can be inserted as inline node, in which + * case they will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. + * @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if + * not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty + * string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML + * specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the + * parser decides itself whether the key is bare-key, a literal key or a quoted key. * @param[in] ePreference The preferred form of the node to be inserted. * @return On success the interface to the newly inserted node is returned or NULL otherwise. */ - IInterfaceAccess InsertTable(in uint32 uiIndex, in u8string ssName, in EInsertPreference ePreference); + IInterfaceAccess InsertTable(in u8string ssInsertBefore, in u8string ssName, in EInsertPreference ePreference); /** * @brief Insert a table array into the collection at the location before the supplied index. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value - * nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically - * corrected. - * @param[in] ssName Name of the array node to insert. Will be ignored if the parent node is also an array collection. - * The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not - * allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a - * literal key or a quoted key. + * @remarks In TOML, inline nodes are located before standard nodes. Table arrays can be inserted as inline node, in + * which case they will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. + * @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if + * not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty + * string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML + * specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the + * parser decides itself whether the key is bare-key, a literal key or a quoted key. * @param[in] ePreference The preferred form of the node to be inserted. * @return On success the interface to the newly inserted node is returned or NULL otherwise. */ - IInterfaceAccess InsertTableArray(in uint32 uiIndex, in u8string ssName, in EInsertPreference ePreference); + IInterfaceAccess InsertTableArray(in u8string ssInsertBefore, in u8string ssName, in EInsertPreference ePreference); /** * @brief The result of the TOML string to insert. @@ -358,17 +365,15 @@ module sdv * @brief Insert a TOML string as a child of the current collection node. If the collection is a table, the TOML string * should contain values and inline/external/array-table nodes with names. If the collection is an array, the TOML * string should contain and inline table nodes without names. - * @attention Even though the TOML might be defining the node(s) in standard form, if the parent node is an inline node, - * the node will be inserted as inline node. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value - * nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically - * corrected. + * @remarks In TOML, inline nodes are located before standard nodes. Dependable on the nodes defined in the TOML they + * might be transferred to inline or they might be inserted at a different location. + * @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. * @param[in] ssTOML The TOML string to insert. This string can hold one or more nodes that should be inserted. * @param[in] bRollbackOnFailure If only part of the nodes could be inserted, no node will be inserted. * @return The result of the insertion. */ - EInsertResult InsertTOML(in uint32 uiIndex, in u8string ssTOML, in boolean bRollbackOnFailure); + EInsertResult InsertTOML(in u8string ssInsertBefore, in u8string ssTOML, in boolean bRollbackOnFailure); }; /** diff --git a/export/support/any.inl b/export/support/any.inl index 0442a91..23f0cdc 100644 --- a/export/support/any.inl +++ b/export/support/any.inl @@ -24,6 +24,9 @@ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 26495) +#else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif namespace sdv @@ -1738,6 +1741,8 @@ namespace sdv #ifdef _MSC_VER #pragma warning(pop) +#else + #pragma GCC diagnostic pop #endif #endif // !defined SDV_ANY_INL \ No newline at end of file diff --git a/export/support/app_control.h b/export/support/app_control.h index 77135ed..cdffaa0 100644 --- a/export/support/app_control.h +++ b/export/support/app_control.h @@ -65,6 +65,7 @@ namespace sdv bool Startup(const std::string& rssConfig) { IAppControl* pAppControl = core::GetCore() ? core::GetCore() : nullptr; + if (!pAppControl) return false; if (m_eState != EAppOperationState::not_started) return false; try @@ -78,45 +79,6 @@ namespace sdv { m_eContext = pAppContext->GetContextType(); m_uiInstanceID = pAppContext->GetInstanceID(); - m_uiRetries = pAppContext->GetRetries(); - } - - // Automatically connect to the server - if (m_eContext == EAppContext::external) - { - // Try to connect - m_ptrServerRepository = sdv::com::ConnectToLocalServerRepository(m_uiInstanceID, m_uiRetries); - if (!m_ptrServerRepository) - { - if (!ConsoleIsSilent()) - std::cerr << "ERROR: Failed to connect to the server repository." << std::endl; - Shutdown(); - return false; - } - - // Get access to the module control service - sdv::core::IObjectAccess* pObjectAccess = m_ptrServerRepository.GetInterface(); - const sdv::core::IRepositoryControl* pRepoControl = nullptr; - if (pObjectAccess) - pRepoControl = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("RepositoryService")). - GetInterface(); - if (!pRepoControl) - { - if (!ConsoleIsSilent()) - std::cerr << "ERROR: Failed to access the server repository." << std::endl; - return false; - } - - // Link the local repository to the server repository. - sdv::core::ILinkCoreRepository* pLinkCoreRepo = - sdv::core::GetObject("RepositoryService"); - if (!pLinkCoreRepo) - { - if (!ConsoleIsSilent()) - std::cerr << "ERROR: Cannot link local and server repositories." << std::endl; - return false; - } - pLinkCoreRepo->LinkCoreRepository(m_ptrServerRepository); } return bRet; @@ -154,23 +116,6 @@ namespace sdv */ void Shutdown() { - // Disconnect local and remote repositories. - if (m_ptrServerRepository) - { - // Link the local repository to the server repository. - sdv::core::ILinkCoreRepository* pLinkCoreRepo = - sdv::core::GetObject("RepositoryService"); - if (!pLinkCoreRepo) - { - if (!ConsoleIsSilent()) - std::cerr << "ERROR: Cannot unlink local and server repositories." << std::endl; - } else - pLinkCoreRepo->UnlinkCoreRepository(); - } - - // Disconnect from the server (if connected at all). - m_ptrServerRepository.Clear(); - // Shutdown. IAppControl* pAppControl = core::GetCore() ? core::GetCore() : nullptr; try @@ -194,28 +139,86 @@ namespace sdv return m_eState == EAppOperationState::running; } + /** + * @brief Get the absolute path to the currently run executable + * @return Absolute std::filesystem::path to the currently run executable + */ + static std::filesystem::path GetAppDirectory() + { + static std::filesystem::path pathExeDir; + if (!pathExeDir.empty()) + return pathExeDir; +#ifdef _WIN32 + // Windows specific + std::wstring ssPath(32768, '\0'); + GetModuleFileNameW(NULL, ssPath.data(), static_cast(ssPath.size() - 1)); +#elif defined __linux__ + // Linux specific + std::string ssPath(PATH_MAX + 1, '\0'); + const ssize_t nCount = readlink("/proc/self/exe", ssPath.data(), PATH_MAX); + if (nCount < 0 || nCount >= PATH_MAX) + return pathExeDir; // some error + ssPath.at(nCount) = '\0'; +#else + #error OS is not supported! +#endif + pathExeDir = std::filesystem::path{ssPath.c_str()}.parent_path() / ""; // To finish the folder path with (back)slash + return pathExeDir; + } + + /** + * @brief Get the filename of the currently run executable + * @return The filename to the currently run executable + */ + static std::filesystem::path GetAppFilename() + { + static std::filesystem::path pathExeFilename; + if (!pathExeFilename.empty()) + return pathExeFilename; +#ifdef _WIN32 + // Windows specific + std::wstring ssPath(32768, '\0'); + GetModuleFileNameW(NULL, ssPath.data(), static_cast(ssPath.size() - 1)); +#elif defined __linux__ + // Linux specific + std::string ssPath(PATH_MAX + 1, '\0'); + const ssize_t nCount = readlink("/proc/self/exe", ssPath.data(), PATH_MAX); + if (nCount < 0 || nCount >= PATH_MAX) + return pathExeFilename; // some error + ssPath.at(nCount) = '\0'; +#else + #error OS is not supported! +#endif + pathExeFilename = std::filesystem::path{ssPath.c_str()}.filename(); + return pathExeFilename; + } + + /** * @brief Get the SDV_FRAMEWORK_RUNTIME environment variable for this application. - * @return Path directing to the SDV V-API Framework directory if available or an empty path if not. + * @remarks If the environment variable is empty or if the variable contains a relative path, the path will be enhanced + * with the application path. + * @return Absolute path directing to the SDV Vehicle API Framework directory. */ static std::filesystem::path GetFrameworkRuntimeDirectory() { + std::filesystem::path path; #ifdef _WIN32 const wchar_t* szFrameworkDir = _wgetenv(L"SDV_FRAMEWORK_RUNTIME"); - if (!szFrameworkDir) return {}; - return szFrameworkDir; + if (szFrameworkDir) path = szFrameworkDir; #elif defined __unix__ const char* szFrameworkDir = getenv("SDV_FRAMEWORK_RUNTIME"); - if (!szFrameworkDir) return {}; - return szFrameworkDir; + if (szFrameworkDir) path = szFrameworkDir; #else #error The OS is not supported! #endif + if (path.empty() || path.is_relative()) path = GetAppDirectory() / path; + return path.lexically_normal(); } /** * @brief Set or overwrite the SDV_FRAMEWORK_RUNTIME environment variable for this application. - * @param[in] rpathDir Reference of the path directing to the SDV V-API Framework directory. + * @param[in] rpathDir Reference of the path directing to the SDV Vehicle API Framework directory. */ static void SetFrameworkRuntimeDirectory(const std::filesystem::path& rpathDir) { @@ -232,26 +235,30 @@ namespace sdv /** * @brief Get the SDV_COMPONENT_INSTALL environment variable for this application. - * @return Path directing to the SDV V-API component installation directory if available or an empty path if not. + * @remarks If the environment variable is empty, the runtime directory is taken. If the variable contains a relative + * path, the path will be enhanced with the application path. + * @return Absolute path directing to the SDV Vehicle API component installation directory. */ static std::filesystem::path GetComponentInstallDirectory() { + std::filesystem::path path; #ifdef _WIN32 const wchar_t* szComponentDir = _wgetenv(L"SDV_COMPONENT_INSTALL"); - if (!szComponentDir) return {}; - return szComponentDir; + if (szComponentDir) path = szComponentDir; #elif defined __unix__ const char* szComponentDir = getenv("SDV_COMPONENT_INSTALL"); - if (!szComponentDir) return {}; - return szComponentDir; + if (szComponentDir) path = szComponentDir; #else #error The OS is not supported! #endif + if (path.empty()) return GetFrameworkRuntimeDirectory(); + if (path.is_relative()) path = GetAppDirectory() / path; + return path.lexically_normal(); } /** * @brief Set or overwrite the SDV_COMPONENT_INSTALL environment variable for this application. - * @param[in] rpathDir Reference of the path directing to the SDV V-API component installation directory. + * @param[in] rpathDir Reference of the path directing to the SDV Vehicle API component installation directory. */ static void SetComponentInstallDirectory(const std::filesystem::path& rpathDir) { @@ -527,8 +534,7 @@ namespace sdv EAppOperationState m_eState = EAppOperationState::not_started; ///< Application state. EAppContext m_eContext = EAppContext::no_context; ///< Application context. uint32_t m_uiInstanceID = 0u; ///< Core instance. - uint32_t m_uiRetries = 0u; ///< Number of retries to establish a connection. - sdv::TObjectPtr m_ptrServerRepository; ///< Server repository interface. + uint32_t m_uiConnectRetries = 0u; ///< Number of retries to establish a connection. }; } // namespace app } // namespace sdv diff --git a/export/support/component_impl.h b/export/support/component_impl.h index 3a39a92..065b8ad 100644 --- a/export/support/component_impl.h +++ b/export/support/component_impl.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "../interfaces/core_types.h" #include "../interfaces/core.h" @@ -683,9 +685,9 @@ namespace sdv /** * @brief Initialize the object. Overload of IObjectControl::Initialize. - * @param[in] ssObjectConfig Optional configuration string. + * @param[in] sObjectInfo The registration information of this object. */ - virtual void Initialize(/*in*/ const u8string& ssObjectConfig) override + virtual void Initialize(/*in*/ const SObjectInfo& sObjectInfo) override { // Not started before or ended completely? if (GetObjectState() != EObjectState::initialization_pending && GetObjectState() != EObjectState::destruction_pending) @@ -700,19 +702,19 @@ namespace sdv // Initialize the parameter map. InitParamMap(); - // Copy the configuration - m_ssObjectConfig = ssObjectConfig; + // Copy the object information + m_sSelf = sObjectInfo; // Prepare initialization OnPreInitialize(); // Parse the object configuration if there is any. - if (!ssObjectConfig.empty()) + if (!m_sSelf.ssConfig.empty()) { try { // Parse the config TOML. - toml::CTOMLParser parser(ssObjectConfig); + toml::CTOMLParser parser(m_sSelf.ssConfig); if (!parser.IsValid()) { m_eObjectState = EObjectState::config_error; @@ -781,6 +783,15 @@ namespace sdv return m_eObjectState; } + /** + * @brief Get information about ourself. + * @return The object information about ourself. + */ + const SObjectInfo& Self() const + { + return m_sSelf; + } + /** * @brief Set the component operation mode. Overload of IObjectControl::SetOperationMode. * @param[in] eMode The operation mode, the component should run in. @@ -831,15 +842,11 @@ namespace sdv } /** - * @brief Get the object configuration for persistence. + * @brief Build the object configuration from the parameter map and the stored initial configuration. * @return The object configuration as TOML string. */ - virtual u8string GetObjectConfig() const override + virtual u8string BuildObjectConfig() const { - // During the initialization, return the stored object configuration. - if (m_eObjectState == EObjectState::initializing || m_eObjectState == EObjectState::initialization_pending) - return m_ssObjectConfig; - // Split path function (splits the group from the parameter names) auto fnSplitPath = [](const u8string& rssPath) -> std::pair { @@ -849,13 +856,10 @@ namespace sdv return std::make_pair(rssPath.substr(0, nPos), rssPath.substr(nPos + 1)); }; - // Create a new configuration string from the parameters. - auto seqParameters = GetParamPaths(); - if (seqParameters.empty()) return {}; - // Iterate through the list of parameter names and create the TOML entries for it. + auto seqParameters = GetParamPaths(); u8string ssGroup; - toml::CTOMLParser parser(""); + toml::CTOMLParser parser(m_sSelf.ssConfig); toml::CNodeCollection table(parser); for (auto ssParamPath : seqParameters) { @@ -863,7 +867,7 @@ namespace sdv auto ptrParam = FindParamObject(ssParamPath); // Read only and temporary parameters are not stored - if ((!ptrParam->Locked() && ptrParam->ReadOnly()) || !ptrParam->Temporary()) + if ((!ptrParam->Locked() && ptrParam->ReadOnly()) || ptrParam->Temporary()) continue; // Get the value @@ -877,7 +881,9 @@ namespace sdv // Need to add a group? if (prParam.first != ssGroup) { - table = parser.AddTable(prParam.first); + table = parser.GetDirect(prParam.first); + if (!table) + table = parser.AddTable(prParam.first); ssGroup = prParam.first; } @@ -888,6 +894,20 @@ namespace sdv return parser.GetTOML(); } + /** + * @brief Get the object configuration for persistence. Overload of IObjectControl::GetObjectConfig. + * @return The object configuration as TOML string. + */ + virtual u8string GetObjectConfig() const override + { + // During the initialization, return the stored object configuration. + if (m_eObjectState == EObjectState::initializing || m_eObjectState == EObjectState::initialization_pending) + return m_sSelf.ssConfig; + + + return BuildObjectConfig(); + } + /** * @brief Shutdown called before the object is destroyed. Overload of IObjectControl::Shutdown. * @attention Implement calls to other SDV objects here as this is no longer considered safe during the destructor of the @@ -930,7 +950,7 @@ namespace sdv * @post Reset by switching to configuration mode. */ void SetObjectIntoRuntimeErrorState() - { + { if (m_eObjectState == EObjectState::running) m_eObjectState = EObjectState::runtime_error; } @@ -992,6 +1012,12 @@ namespace sdv */ virtual void OnShutdown() {} + /** + * @brief Last function called before destruction. The object integrity is still in tact. It can be assumed that all + * dependencies to the object have been released. + */ + virtual void OnDestroy() {} + /** * @brief Interface map */ @@ -1002,7 +1028,8 @@ namespace sdv private: std::atomic m_eObjectState = EObjectState::initialization_pending; ///< Object state - std::string m_ssObjectConfig; ///< Copy of the configuration TOML. + CLifetimeCookie m_lifetime = CreateLifetimeCookie(); ///< Manage module lifetime. + SObjectInfo m_sSelf; ///< Information about ourself. }; /** @@ -1022,6 +1049,206 @@ namespace sdv END_SDV_INTERFACE_MAP() }; + /** + * @brief Lifetime control implementation class. + */ + class CLifetimeControlImpl : public virtual IInterfaceAccess, public IObjectLifetime, protected IObjectDestroy + { + public: + /** + * @brief Default constructor + */ + CLifetimeControlImpl() + {} + + /** + * @brief Deleted copy constructor. + * @param[in] rClass Reference to the class to copy from. + */ + CLifetimeControlImpl(const CLifetimeControlImpl& rClass) = delete; + + /** + * @brief Deleted move constructor. + * @param[in] rClass Reference to the class to copy from. + */ + CLifetimeControlImpl(CLifetimeControlImpl&& rClass) = delete; + + /** + * @brief Destructor. + */ + virtual ~CLifetimeControlImpl() + {} + + // Interface map (IObjectDestroy is not exposed) + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(IObjectLifetime) + END_SDV_INTERFACE_MAP() + + /** + * @brief Deleted copy assignment constructor. + * @param[in] rClass Reference to the class to copy from. + * @return Reference to this class. + */ + CLifetimeControlImpl& operator=(const CLifetimeControlImpl& rClass) = delete; + + /** + * @brief Deleted move assignment constructor. + * @param[in] rClass Reference to the class to copy from. + * @return Reference to this class. + */ + CLifetimeControlImpl& operator=(CLifetimeControlImpl&& rClass) = delete; + + /** + * @brief Increment the lifetime. Needs to be balanced by a call to Decrement. Overload of IObjectLifetime::Increment. + */ + virtual void Increment() override + { + m_uiLifetimeCnt++; + } + + /** + * @brief Decrement the lifetime. If the lifetime reaches zero, the object will be destroyed (through the exposed + * IObjectDestroy interface). Overload of IObjectLifetime::Decrement. + * @return Returns 'true' if the object was destroyed, false if not. + */ + virtual bool Decrement() override + { + if (m_uiLifetimeCnt && !(--m_uiLifetimeCnt)) + { + DestroyObject(); + return true; + } + return false; + } + + /** + * @brief Get the current lifetime count. Overload of IObjectLifetime::GetCount. + * @remarks The GetCount function returns a momentary value, which can be changed at any moment. + * @return Returns the current counter value. + */ + virtual uint32_t GetCount() const override + { + return m_uiLifetimeCnt; + } + + protected: + /** + * @brief Destroy the object. Default implementation deletes 'this'. Overload of IObjectDestroy::DestroyObject. + * @attention After a call of this function, all exposed interfaces render invalid and should not be used any more. + */ + virtual void DestroyObject() override + { + delete this; + } + + private: + CLifetimeCookie m_lifetime = CreateLifetimeCookie(); ///< Module lifetime + std::atomic_uint32_t m_uiLifetimeCnt = 1; ///< Lifetime reference counter + }; + + /** + * @brief Lifetime control implementation class based on shared pointer. + * @tparam TClass The type of class deriving CSharedLifetimeControlImpl class. + */ + template + class CSharedLifetimeControlImpl : public std::enable_shared_from_this, public CLifetimeControlImpl + { + public: + /** + * @brief Default constructor + */ + CSharedLifetimeControlImpl() + {} + + /** + * @brief Deleted copy constructor. + * @param[in] rClass Reference to the class to copy from. + */ + CSharedLifetimeControlImpl(const CSharedLifetimeControlImpl& rClass) = delete; + + /** + * @brief Deleted move constructor. + * @param[in] rClass Reference to the class to copy from. + */ + CSharedLifetimeControlImpl(CSharedLifetimeControlImpl&& rClass) = delete; + + /** + * @brief Destructor. + */ + virtual ~CSharedLifetimeControlImpl() + { + } + + // Interface map (IObjectDestroy is not exposed) + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(IObjectLifetime) + END_SDV_INTERFACE_MAP() + + /** + * @brief Deleted copy assignment constructor. + * @param[in] rClass Reference to the class to copy from. + * @return Reference to this class. + */ + CSharedLifetimeControlImpl& operator=(const CSharedLifetimeControlImpl& rClass) = delete; + + /** + * @brief Deleted move assignment constructor. + * @param[in] rClass Reference to the class to copy from. + * @return Reference to this class. + */ + CSharedLifetimeControlImpl& operator=(CSharedLifetimeControlImpl&& rClass) = delete; + + /** + * @brief Increment the lifetime. Needs to be balanced by a call to Decrement. Overload of IObjectLifetime::Increment. + */ + virtual void Increment() override + { + if (!m_uiLifetimeCnt++) + m_ptrLocking = std::enable_shared_from_this::shared_from_this(); + } + + /** + * @brief Decrement the lifetime. If the lifetime reaches zero, the object will be destroyed (through the exposed + * IObjectDestroy interface). Overload of IObjectLifetime::Decrement. + * @return Returns 'true' if the object was destroyed, false if not. + */ + virtual bool Decrement() override + { + if (m_uiLifetimeCnt && !(--m_uiLifetimeCnt)) + { + DestroyObject(); + return true; + } + return false; + } + + /** + * @brief Get the current lifetime count. Overload of IObjectLifetime::GetCount. + * @remarks The GetCount function returns a momentary value, which can be changed at any moment. + * @return Returns the current counter value. + */ + virtual uint32_t GetCount() const override + { + return m_uiLifetimeCnt; + } + + protected: + /** + * @brief Destroy the object. Default implementation deletes 'this'. Overload of IObjectDestroy::DestroyObject. + * @attention After a call of this function, all exposed interfaces render invalid and should not be used any more. + */ + virtual void DestroyObject() override + { + // Reset the locking; this might delete the class if no other references are present. + m_ptrLocking.reset(); + } + + private: + std::shared_ptr m_ptrLocking; ///< Holder of the instance of the class during lifetime. + CLifetimeCookie m_lifetime = CreateLifetimeCookie(); ///< Module lifetime + std::atomic_uint32_t m_uiLifetimeCnt = 0; ///< Lifetime reference counter + }; + ///////////////////////////////////////// // Object control class implementation // ///////////////////////////////////////// @@ -1114,6 +1341,7 @@ namespace sdv auto objectPtr = std::move(*iter); m_vecActiveObjects.erase(iter); lockObjects.unlock(); + objectPtr->OnDestroy(); objectPtr = nullptr; if (m_uiActiveObjectCount) --m_uiActiveObjectCount; return; diff --git a/export/support/interface_ptr.h b/export/support/interface_ptr.h index e10b40b..9249a2f 100644 --- a/export/support/interface_ptr.h +++ b/export/support/interface_ptr.h @@ -389,7 +389,13 @@ namespace sdv template TIfc* GetInterface() const { - return m_pInterface ? m_pInterface.load()->GetInterface(GetInterfaceId()).template get() : nullptr; + try + { + return m_pInterface ? m_pInterface.load()->GetInterface(GetInterfaceId()).template get() : nullptr; + } catch (const XSysExcept&) + { + return nullptr; + } } private: diff --git a/export/support/local_service_access.h b/export/support/local_service_access.h index 3375315..971a4e6 100644 --- a/export/support/local_service_access.h +++ b/export/support/local_service_access.h @@ -18,10 +18,17 @@ #include "../interfaces/log.h" #include "../interfaces/repository.h" #include "../interfaces/com.h" +#include "../interfaces/app.h" +#include "../interfaces/param.h" +#include "../interfaces/permission.h" #include "interface_ptr.h" +#include +#include +#include #include #include - +#include +#include #ifdef __GNUC__ // Needed for getpid() #include @@ -49,9 +56,11 @@ namespace sdv */ inline TInterfaceAccessPtr GetObject(const std::string& rssObjectName) { - if (!GetCore()) return nullptr; + if (!GetCore()) + return nullptr; IObjectAccess* pRepository = GetCore(); - if (!pRepository) return nullptr; + if (!pRepository) + return nullptr; return pRepository->GetObject(rssObjectName); } @@ -63,9 +72,11 @@ namespace sdv */ inline TInterfaceAccessPtr GetObject(TObjectID tObjectID) { - if (!GetCore()) return nullptr; + if (!GetCore()) + return nullptr; IObjectAccess* pRepository = GetCore(); - if (!pRepository) return nullptr; + if (!pRepository) + return nullptr; return pRepository->GetObjectByID(tObjectID); } #else @@ -113,21 +124,23 @@ namespace sdv } /** - * @brief Log function enables logging for SDV. - * @param[in] eSeverity Severity level of the log message which will be logged, e.g. Info, Warning, Error etc. - * @param[in] rssSrcFile Name of the file from which the message is logged. - * @param[in] uiSrcLine Line of the file from which the message is logged. - * @param[in] rssMessage Reference to the log message to be logged. - */ + * @brief Log function enables logging for SDV. + * @param[in] eSeverity Severity level of the log message which will be logged, e.g. Info, Warning, Error etc. + * @param[in] rssSrcFile Name of the file from which the message is logged. + * @param[in] uiSrcLine Line of the file from which the message is logged. + * @param[in] rssMessage Reference to the log message to be logged. + */ inline void Log(ELogSeverity eSeverity, const u8string& rssSrcFile, uint32_t uiSrcLine, const u8string& rssMessage) { ILogger* pLogger = GetCore() ? GetCore() : nullptr; #ifdef _WIN32 - if (pLogger) pLogger->Log(eSeverity, rssSrcFile, uiSrcLine, _getpid(), "", rssMessage); + if (pLogger) + pLogger->Log(eSeverity, rssSrcFile, uiSrcLine, _getpid(), "", rssMessage); #elif defined __unix__ - if (pLogger) pLogger->Log(eSeverity, rssSrcFile, uiSrcLine, getpid(), "", rssMessage); + if (pLogger) + pLogger->Log(eSeverity, rssSrcFile, uiSrcLine, getpid(), "", rssMessage); #else -#error The OS is currently not supported! + #error The OS is currently not supported! #endif } @@ -137,34 +150,34 @@ namespace sdv */ #define SDV_LOG(severity, ...) sdv::core::internal::CSDVLogImpl(severity, __FILE__, __LINE__, __VA_ARGS__) - /** + /** * @brief Log a trace message with line and position. */ #define SDV_LOG_TRACE(...) sdv::core::internal::CSDVLogImpl(sdv::core::ELogSeverity::trace, __FILE__, __LINE__, __VA_ARGS__) - /** - * @brief Log a debug message with line and position. - */ + /** + * @brief Log a debug message with line and position. + */ #define SDV_LOG_DEBUG(...) sdv::core::internal::CSDVLogImpl(sdv::core::ELogSeverity::debug, __FILE__, __LINE__, __VA_ARGS__) - /** - * @brief Log an information message with line and position. - */ + /** + * @brief Log an information message with line and position. + */ #define SDV_LOG_INFO(...) sdv::core::internal::CSDVLogImpl(sdv::core::ELogSeverity::info, __FILE__, __LINE__, __VA_ARGS__) - /** - * @brief Log a warning message with line and position. - */ + /** + * @brief Log a warning message with line and position. + */ #define SDV_LOG_WARNING(...) sdv::core::internal::CSDVLogImpl(sdv::core::ELogSeverity::warning, __FILE__, __LINE__, __VA_ARGS__) - /** - * @brief Log an error message with line and position. - */ + /** + * @brief Log an error message with line and position. + */ #define SDV_LOG_ERROR(...) sdv::core::internal::CSDVLogImpl(sdv::core::ELogSeverity::error, __FILE__, __LINE__, __VA_ARGS__) - /** - * @brief Log a fatal message with line and position. - */ + /** + * @brief Log a fatal message with line and position. + */ #define SDV_LOG_FATAL(...) sdv::core::internal::CSDVLogImpl(sdv::core::ELogSeverity::fatal, __FILE__, __LINE__, __VA_ARGS__) namespace internal @@ -176,22 +189,22 @@ namespace sdv * @param[in] uiSrcLine Line of the file from which the message is logged. Specified by C++ standard. * @param[in] ...args identifier specified by C++ standard that uses the ellipsis notation in the parameters. */ - template - inline void CSDVLogImpl(ELogSeverity eSeverity, const char* szSrcFile, uint32_t uiSrcLine, Args&& ...args) + template + inline void CSDVLogImpl(ELogSeverity eSeverity, const char* szSrcFile, uint32_t uiSrcLine, Args&&... args) { std::ostringstream stream; (stream << ... << std::forward(args)); Log(eSeverity, szSrcFile ? szSrcFile : "", uiSrcLine, stream.str().c_str()); } - } + } // namespace internal /** * @brief Create a utility * @param[in] rssUtilityName Reference to the utility name. * @param[in] rssUtilityConfig Optional reference to the utility configuration. * @return Smart pointer to the utility or NULL when the utility could not be found. - */ + */ inline TObjectPtr CreateUtility(const std::string& rssUtilityName, const std::string& rssUtilityConfig = std::string()) { TInterfaceAccessPtr ptrRepository = GetObject("RepositoryService"); @@ -199,8 +212,529 @@ namespace sdv if (!pUtilityCreate) return nullptr; return pUtilityCreate->CreateUtility(rssUtilityName, rssUtilityConfig); } - } // namespace core + /** + * @brief Get a parameter from the object smart pointer. + * @param[in] pParameters Pointer to the parameter map interface of an object. + * @param[in] rssParamName Name of the parameter (incl. groups preceding the name and separated with a dot). + * @param[in] bNoExcept When set, do not trigger an std::runtime exception when the object or the parameter could not be + * found. + * @return Returns the value of the parameter or an empty any_t when the object or the parameter could not be found and + * bNoExcept was set. + */ + inline any_t GetParameter(const IParameters* pParameters, const std::string& rssParamName, bool bNoExcept = true) + { + if (!pParameters) + { + if (bNoExcept) return {}; + throw std::runtime_error("The object doesn't expose IParameters interface."); + } + any_t any = pParameters->GetParam(rssParamName); + if (any.empty()) + { + if (bNoExcept) return {}; + throw std::runtime_error("The parameter could not be found."); + } + return any; + } + + /** + * @brief Get a parameter from the parameter interface. + * @param[in] rptrObject Reference to object smart pointer to the the parameter of. + * @param[in] rssParamName Name of the parameter (incl. groups preceding the name and separated with a dot). + * @param[in] bNoExcept When set, do not trigger an std::runtime exception when the object or the parameter could not be + * found. + * @return Returns the value of the parameter or an empty any_t when the object or the parameter could not be found and + * bNoExcept was set. + */ + inline any_t GetParameter(const TInterfaceAccessPtr& rptrObject, const std::string& rssParamName, bool bNoExcept = true) + { + const IParameters* pParameters = rptrObject.GetInterface(); + if (!pParameters) + { + if (bNoExcept) return {}; + throw std::runtime_error("The object doesn't expose IParameters interface."); + } + return GetParameter(pParameters, rssParamName, bNoExcept); + } + + /** + * @brief Get a parameter from the object with the supplied name. + * @param[in] rssObjectName Reference to the string containing the name of the object or service. + * @param[in] rssParamName Name of the parameter (incl. groups preceding the name and separated with a dot). + * @param[in] bNoExcept When set, do not trigger an std::runtime exception when the object or the parameter could not be + * found. + * @return Returns the value of the parameter or an empty any_t when the object or the parameter could not be found and + * bNoExcept was set. + */ + inline any_t GetParameter(const std::string& rssObjectName, const std::string& rssParamName, bool bNoExcept = true) + { + TInterfaceAccessPtr ptrObject = GetObject(rssObjectName); + if (!ptrObject) + { + if (bNoExcept) return {}; + throw std::runtime_error("An object with the name '" + rssObjectName + "' could not be found."); + } + return GetParameter(ptrObject, rssParamName, bNoExcept); + } + + /** + * @brief Get a parameter from the object with the supplied name. Resolve enums and bitmask objects in the returned + * parameter string. + * @param[in] rssObjectName Reference to the string containing the name of the object or service. + * @param[in] rssParamName Name of the parameter (incl. groups preceding the name and separated with a dot). + * @param[in] bNoExcept When set, do not trigger an std::runtime exception when the object or the parameter could not be + * found. + * @return Returns the value of the parameter as a text string or an empty string when the object or the parameter could + * not be found and bNoExcept was set. + */ + inline std::string GetParameterExpand(const std::string& rssObjectName, const std::string& rssParamName, bool bNoExcept = true) + { + TInterfaceAccessPtr ptrObject = GetObject(rssObjectName); + if (!ptrObject) + { + if (bNoExcept) return {}; + throw std::runtime_error("An object with the name '" + rssObjectName + "' could not be found."); + } + + const IParameters* pParameters = ptrObject.GetInterface(); + if (!pParameters) + { + if (bNoExcept) return {}; + throw std::runtime_error("The object doesn't expose IParameters interface."); + } + any_t any = pParameters->GetParam(rssParamName); + if (any.empty()) + { + if (bNoExcept) return {}; + throw std::runtime_error("The parameter could not be found."); + } + + SParamInfo sParamInfo = pParameters->GetParamInfo(rssParamName); + if (sParamInfo.get_switch() == EParamType::enum_param) + { + for (const SLabelInfo::SLabel& rsLabel : sParamInfo.uExtInfo.sEnumInfo.seqLabels) + { + if (rsLabel.anyValue == any) + return rsLabel.ssLabel; + } + } + if (sParamInfo.get_switch() == EParamType::bitmask_param) + { + uint64_t uiValue = static_cast(any); + std::stringstream sstreamValue; + for (const SLabelInfo::SLabel& rsLabel : sParamInfo.uExtInfo.sBitmaskInfo.seqLabels) + { + uint64_t uiValueLabel = static_cast(rsLabel.anyValue); + if (uiValue & uiValueLabel) + { + if (sstreamValue.rdbuf()->in_avail()) // Has characters + sstreamValue << "|"; + sstreamValue << rsLabel.ssLabel; + uiValue = uiValue & ~uiValueLabel; + } + } + if (uiValue) // Not all bits have labels + { + if (sstreamValue.rdbuf()->in_avail()) // Has characters + sstreamValue << "|"; + sstreamValue << uiValue; + } + return sstreamValue.str(); + } + return any; + } + + /// Internal namespace + namespace internal + { + /** + * @brief Trim whitespace at the begin and end of the text. + * @param[in] rssText Reference to the text to trim whitespace for. + * @return The trimmed text. + */ + inline std::string TrimWhitespace(const std::string& rssText) + { + size_t nFirst = rssText.find_first_not_of(" \t\r\n"); + if (nFirst == std::string::npos) return ""; + size_t nLast = rssText.find_last_not_of(" \t\r\n"); + return rssText.substr(nFirst, (nLast - nFirst + 1)); + } + + /** + * @brief Helper function to safely check if a string contains a valid, unescaped $(...) sequence. + * @param[in] rssText The text to check. + * @return Returns whether the text represents a valid variable. + */ + inline bool ContainsValidVariable(const std::string& rssText) + { + size_t nLength = rssText.length(); + size_t nIndex = 0; + + while (nIndex < nLength) + { + if (rssText[nIndex] == '\\') + { + // Skip the escape and the escaped character + nIndex += 2; + } + else if (rssText[nIndex] == '$') + { + // Check if it forms the start of a token sequence '$( ' + if (nIndex + 1 < nLength && rssText[nIndex + 1] == '(') + { + size_t nClose = rssText.find(')', nIndex + 2); + if (nClose != std::string::npos) + { + // Found a valid structural candidate + return true; + } + } + nIndex++; + } + else + { + nIndex++; + } + } + return false; + } + } // namespace internal + + /** + * @brief Resolves embedded object parameter variables formatted as $(object:param) within a text string. + * @param[in] rssText The source text containing potential variables and backslash escapes. + * @param[in] bRecursive If true, continues resolving until no more unescaped variables remain. + * @param[in] bNoExcept If true, returns an empty string for errors; if false, throws std::runtime_error. + * @return The fully resolved text string. + */ + inline std::string ResolveText(const std::string& rssText, bool bRecursive = true, bool bNoExcept = true) + { + std::string ssCurrentText = rssText; + bool bMayHaveMoreVariables = true; + + while (bMayHaveMoreVariables) + { + bMayHaveMoreVariables = false; + std::string ssResult = ""; + ssResult.reserve(ssCurrentText.length()); // Pre-allocate memory for performance + + size_t nLength = ssCurrentText.length(); + size_t nIndex = 0; + + while (nIndex < nLength) + { + char cCurrent = ssCurrentText[nIndex]; + + // Handle backslash escape sequences + if (cCurrent == '\\') + { + // Check if the next character is a '$' + if (nIndex + 1 < nLength && ssCurrentText[nIndex + 1] == '$') + { + ssResult.push_back('$'); // Strip the escape character, keep the literal '$' + nIndex += 2; + } + else + { + ssResult.push_back('\\'); // Preserve other backslashes for paths + nIndex++; + } + } + // Identify potential variables + else if (cCurrent == '$') + { + // extension 2: Only throw an error or treat as a variable if it is + // explicitly part of a dynamic sequence tracking towards a '$(...)' match. + if (nIndex + 1 >= nLength || ssCurrentText[nIndex + 1] != '(') + { + // It is a loose literal '$' (like currency). Keep it and do not throw. + ssResult.push_back('$'); + nIndex++; + continue; + } + + size_t nVarStart = nIndex + 2; // Position right after "$(" + size_t nVarEnd = ssCurrentText.find(')', nVarStart); + + // Validate that a matching closing parenthesis exists + if (nVarEnd == std::string::npos) + { + if (!bNoExcept) + throw std::runtime_error( + "Format error: Missing closing parenthesis ')' for variable starting at index " + + std::to_string(nIndex)); + return ""; + } + + // Extract the inner payload block + std::string sVariableContent = ssCurrentText.substr(nVarStart, nVarEnd - nVarStart); + size_t nColonPos = sVariableContent.find(':'); + + // Validate that the delimiter ':' separates object and parameter names + if (nColonPos == std::string::npos) + { + if (!bNoExcept) + throw std::runtime_error("Format error: Variable content '" + sVariableContent + + "' lacks an 'object:param' colon separator"); + return ""; + } + + // Extract and clean whitespace before/after tokens, preserving inner spaces + std::string sObjName = internal::TrimWhitespace(sVariableContent.substr(0, nColonPos)); + std::string sParamName = internal::TrimWhitespace(sVariableContent.substr(nColonPos + 1)); + + // Request the parameter. + std::string ssValue = GetParameterExpand(sObjName, sParamName, bNoExcept); + + // Add the parameter to the string + ssResult.append(ssValue); + + nIndex = nVarEnd + 1; // Move parsing index past the ')' + } + else + { + ssResult.push_back(cCurrent); + nIndex++; + } + } + + ssCurrentText = ssResult; + + // Exit immediately if recursive resolution was disabled + if (!bRecursive) break; + + // extension 2: Intelligently analyze if a recursive processing loop is required + if (internal::ContainsValidVariable(ssCurrentText)) + bMayHaveMoreVariables = true; + } + + return ssCurrentText; + } + + /** + * @brief Get the current access permission. + * @return The current access permission. + */ + inline EAccessPermission GetCurrentAccessPermission() + { + IPermissionControl* pPermissionControl = GetCore(); + if (!pPermissionControl) + return EAccessPermission::not_set; + return pPermissionControl->GetCurrentPermission(); + } + + /** + * @brief Get the current transmission from this thread and prepare a transfer to another thread. + * @return Transfer ID identifying the permission of this thread. + */ + inline TPermissionTransferID TransferCurrentPermission() + { + IPermissionControl* pPermissionControl = GetCore(); + if (!pPermissionControl) return 0u; + return pPermissionControl->TransferCurrentPermission(); + } + + /** + * @brief Class managing the lifetime of an access permission. + */ + class CAccessPermission + { + // Friend functions + friend CAccessPermission RestrictAccessPermission(EAccessPermission); + friend CAccessPermission SetAccessPermission(TPermissionTransferID); + + public: + /** + * @brief Default constructor + */ + CAccessPermission() = default; + + private: + /** + * @brief Constructor + * @param[in] tPermissionID The access permission to manage. + */ + CAccessPermission(TPermissionID tPermissionID) : m_tPermissionID(tPermissionID) + {} + + public: + /** + * @brief Copy constructor is deleted. + * @param[in] rPermission Reference to the permission to copy from. + */ + CAccessPermission(const CAccessPermission& rPermission) = delete; + + /** + * @brief Move constructor. + * @param[in] rPermission Reference to the permission to move from. + */ + CAccessPermission(CAccessPermission&& rPermission) : m_tPermissionID(rPermission.m_tPermissionID) + { + rPermission.m_tPermissionID = 0u; + } + + /** + * @brief Destructor + */ + ~CAccessPermission() + { + Release(); + } + + /** + * @brief Copy assignment operator is deleted. + * @param[in] rPermission Reference to the permission to copy from. + * @return Reference to this class. + */ + CAccessPermission& operator=(const CAccessPermission& rPermission) = delete; + + /** + * @brief Move assignment operator. + * @param[in] rPermission Reference to the permission to move from. + * @return Reference to this class. + */ + CAccessPermission& operator=(CAccessPermission&& rPermission) + { + m_tPermissionID = rPermission.m_tPermissionID; + rPermission.m_tPermissionID = 0u; + return *this; + } + + /** + * @brief Boolean operator. + * @return Returns whether a valid permission ID is assigned to this class. + */ + operator bool() const + { + return m_tPermissionID ? true : false; + } + + /** + * @brief Does the access permission class contain a valid permission ID. + * @return Returns whether a valid permission ID is assigned to this class. + */ + bool IsValid() const + { + return m_tPermissionID ? true : false; + } + + /** + * @brief Release the permission. + */ + void Release() + { + if (!m_tPermissionID) return; + IPermissionControl* pPermissionControl = GetCore(); + if (!pPermissionControl) return; + pPermissionControl->ReleaseAccessPermission(m_tPermissionID); + m_tPermissionID = 0u; + } + + private: + TPermissionID m_tPermissionID = 0u; ///< Permission ID to be managed by this class. + }; + + /** + * @brief Restrict the current access permission. + * @param[in] ePermission The permission the access should be restricted to. + * @return The access permission connected to this restriction. The lifetime of the access permission is managed by the + * returned class. + */ + inline CAccessPermission RestrictAccessPermission(EAccessPermission ePermission) + { + IPermissionControl* pPermissionControl = GetCore(); + if (!pPermissionControl) return {}; + return pPermissionControl->RestrictAccessPermission(ePermission); + } + + /** + * @brief Set the access permission from a transfer ID that defined the access permission of a different thread. + * @param[in] tTransferID The ID of the transferred access permission. + * @return The access permission for the current thread. The lifetime of the access permission is managed by the returned + * class. + */ + inline CAccessPermission SetAccessPermission(TPermissionTransferID tTransferID) + { + IPermissionControl* pPermissionControl = GetCore(); + if (!pPermissionControl) return {}; + return pPermissionControl->SetAccessPermission(tTransferID); + } + + /** + * @brief Secure thread implementation based on std::thread transferring the permissions from the creation thread to the + * execution thread. + */ + class secure_thread : public std::thread + { + public: + /** + * @brief Default constructor + */ + secure_thread() = default; + + /** + * @brief Copy constructor is deleted. + * @param[in] rthread Reference to the thread object. + */ + secure_thread(const secure_thread& rthread) = delete; + + /** + * @brief Move constructor. + * @param[in] rthread Reference to the thread object. + */ + secure_thread(secure_thread&& rthread) : std::thread(static_cast(rthread)) + {} + + /** + * @brief Assignment constructor for thread execution. + * @tparam F The function to execute. + * @tparam Args The argument types of the function to execute. + * @param[in] f Reference to the function. + * @param[in] args Reference to zero or more arguments. + */ + template + explicit secure_thread(F&& f, Args&&... args) + { + static_cast(*this) = std::thread( + [](TPermissionTransferID tTransferID, auto&& function, auto&&... arguments) + { + CAccessPermission permission = SetAccessPermission(tTransferID); + std::invoke(std::forward(function), std::forward(arguments)...); + }, TransferCurrentPermission(), std::forward(f), std::forward(args)...); + } + + /** + * @brief Move assignment operator. + * @param[in] rthread Reference to the thread object. + * @return Reference to this object. + */ + secure_thread& operator=(secure_thread&& rthread) + { + static_cast(*this) = static_cast(rthread); + return *this; + } + }; + } // namespace core +}// namespace sdv + +/** + * @{ + * @brief Comparison operators + * @param[in] e1 First permission + * @param[in] e2 Second permission + * @return Result of the comparison + */ +inline bool operator<(sdv::core::EAccessPermission e1, sdv::core::EAccessPermission e2) { return static_cast(e1) < static_cast(e2); } +inline bool operator<=(sdv::core::EAccessPermission e1, sdv::core::EAccessPermission e2) { return static_cast(e1) <= static_cast(e2); } +inline bool operator>(sdv::core::EAccessPermission e1, sdv::core::EAccessPermission e2) { return static_cast(e1) > static_cast(e2); } +inline bool operator>=(sdv::core::EAccessPermission e1, sdv::core::EAccessPermission e2) { return static_cast(e1) >= static_cast(e2); } +/** + * @} + */ + +namespace sdv +{ namespace app { /** @@ -208,11 +742,11 @@ namespace sdv * @param[in] rssAttribute Name of the attribute. * @return The attribute value or an empty any-value if the attribute wasn't found or didn't have a value. */ - inline any_t GetAppAttribute(const std::string& rssAttribute) + inline any_t GetAppSettingsAttribute(const std::string& rssAttribute) { - const IAttributes* ptrAppAttributes = core::GetObject("AppControlService"); - if (!ptrAppAttributes) return {}; - return ptrAppAttributes->Get(rssAttribute); + const IParameters* pParameters = core::GetObject("AppSettingsService"); + if (!pParameters) return {}; + return pParameters->GetParam(rssAttribute); } /** @@ -221,7 +755,7 @@ namespace sdv */ inline bool ConsoleIsSilent() { - return GetAppAttribute("console.info_level") == "silent"; + return GetAppSettingsAttribute("Console.Reporting") == "Silent"; } /** @@ -230,7 +764,7 @@ namespace sdv */ inline bool ConsoleIsVerbose() { - return GetAppAttribute("console.info_level") == "verbose"; + return GetAppSettingsAttribute("Console.Reporting") == "Verbose"; } /** @@ -239,7 +773,7 @@ namespace sdv */ inline uint32_t GetAppInstanceID() { - return GetAppAttribute("app.instance_id"); + return GetAppSettingsAttribute("Application.Instance"); } } // namespace app @@ -247,64 +781,81 @@ namespace sdv { /** * @brief Create a repository connection to a local server. - * @param[in] uiInstanceID Optionally the instance ID of the target system to connect to or 0 (default) to connect to the - * instance identified by app-control. * @param[in] nRetries Number of retries to connect (optional, default 30, minimum 3). * @return Returns a smart pointer to the repository proxy. Disconnection takes place when IObjectDestroy::DestroyObject is * called. */ - inline TObjectPtr ConnectToLocalServerRepository(uint32_t uiInstanceID = 0, size_t nRetries = 30) + inline TObjectPtr ConnectToLocalServerRepository(size_t nRetries = 30) { - com::IClientConnect* pClientConnect = core::GetObject("ConnectionService"); - if (!pClientConnect) + // This function works with main, external and maintenance applications. + const app::IAppContext* pAppContext = core::GetCore(); + if (!pAppContext) return {}; + switch (pAppContext->GetContextType()) { - if (!app::ConsoleIsSilent()) - std::cerr << "ERROR: Could not access the connection service." << std::endl; + case app::EAppContext::main: + case app::EAppContext::external: + case app::EAppContext::maintenance: + break; + default: + return {}; + } + + const sdv::app::IAppConnections* pConnections = core::GetObject("AppSettingsService"); + if (!pConnections) return {}; + std::string ssConnectionConfig = pConnections->GetConnectionConfig("Default"); + if (ssConnectionConfig.empty()) return {}; + + sdv::core::IRepositoryControl* pRepository = core::GetObject("RepositoryService"); + if (!pRepository) return {}; + auto tConnectSvcID = pRepository->CreateObject("ClientConnectService", "ClientDefault", ssConnectionConfig); + if (!tConnectSvcID) return {}; + + sdv::TInterfaceAccessPtr ptrConnectSvc = core::GetObject(tConnectSvcID); + sdv::com::IClientConnect* pClientConnect = ptrConnectSvc.GetInterface(); + if (!pClientConnect) + { + pRepository->DestroyObject("ClientDefault"); return {}; } - // Connect the client to the server and return the server repository interface. - std::string ssConnectString = R"code([Client] -Type = "Local" -)code"; - if (uiInstanceID) - ssConnectString += "Instance = " + std::to_string(uiInstanceID) + R"code( -)code"; try { // Try to connect (30 times with 1 second in between). size_t nCnt = 0; - sdv::TObjectPtr ptrRepository; - while (!ptrRepository && nCnt < nRetries) + sdv::TObjectPtr ptrRemoteRepo; + while (!ptrRemoteRepo && nCnt < std::max(nRetries, static_cast(3u))) { nCnt++; - ptrRepository = pClientConnect->Connect(ssConnectString); - if (!ptrRepository) - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + if (pClientConnect->IsConnected() || pClientConnect->Connect()) + { + ptrRemoteRepo = pClientConnect->GetRemoteRepository(); + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } // Return the result - return ptrRepository; + return ptrRemoteRepo; } catch (const XAccessDenied& /*rExcept*/) { if (!app::ConsoleIsSilent()) std::cout << "Access denied trying to connect to a local repository with server instance ID#" << - (uiInstanceID?uiInstanceID : app::GetAppInstanceID()) << "." << std::endl; + app::GetAppInstanceID() << "." << std::endl; return {}; } catch (const XNotFound& /*rExcept*/) { if (!app::ConsoleIsSilent()) std::cout << "Local repository with server instance ID#" << - (uiInstanceID?uiInstanceID : app::GetAppInstanceID()) << " not found." << std::endl; + app::GetAppInstanceID() << " not found." << std::endl; return {}; } catch (const XInvalidState& rExcept) { if (!app::ConsoleIsSilent()) std::cout << "The local repository with server instance ID#" << - (uiInstanceID?uiInstanceID : app::GetAppInstanceID()) << " is in an invalid state: " << rExcept.what() << + app::GetAppInstanceID() << " is in an invalid state: " << rExcept.what() << std::endl; return {}; } @@ -312,12 +863,11 @@ Type = "Local" { if (!app::ConsoleIsSilent()) std::cout << "Timeout occurred trying to connect to a local repository with server instance ID#" << - (uiInstanceID?uiInstanceID : app::GetAppInstanceID()) << "." << std::endl; + app::GetAppInstanceID() << "." << std::endl; return {}; } } - - } // namespace core + } // namespace com } // namespace sdv #endif // !defined LOCAL_SERVICE_ACCESS_H \ No newline at end of file diff --git a/export/support/param_impl.h b/export/support/param_impl.h index 7344e5a..88f0ab4 100644 --- a/export/support/param_impl.h +++ b/export/support/param_impl.h @@ -435,12 +435,14 @@ namespace sdv * @tparam TInfoConstruct The type of the variables that are provided to the constructor function of the parameter * information. * @param[in] rtVar Reference to the parameter variable. + * @param[in] bReadOnly When set, the parameter is read-only (even when writable) and will not be initialized. * @param[in] bLockable When set, the parameter is lockable. Only use with writable parameters. - * @param[in] bAutoDirty When set, the parameter dirty flag is detected automatically. Only use with writable parameters. + * @param[in] bAutoDirty When set, the parameter dirty flag is detected automatically. Only use with writable + * parameters. * @param[in] tConstruct The construct function arguments. */ template - CParamValue(TVar& rtVar, bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct); + CParamValue(TVar& rtVar, bool bReadOnly, bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct); /** * @brief Set a value. Overload of CParamGuardian::Set. @@ -993,7 +995,7 @@ namespace sdv } \ std::vector> vecParamInfo; \ [[maybe_unused]] uint32_t uiFlags = 0; \ - [[maybe_unused]] std::string ssGroup; \ + [[maybe_unused]] std::string ssGroup; \ [[maybe_unused]] bool bLockable = false; /** @@ -1107,8 +1109,9 @@ namespace sdv } \ else \ { \ - std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, bLockable, true, name_string, \ - default_val, unit_string, ssGroup, description_string, uiFlags); \ + std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, \ + uiFlags & static_cast(::sdv::EParamFlags::read_only), bLockable, true, name_string, default_val, \ + unit_string, ssGroup, description_string, uiFlags); \ if (ptrParamInfo) vecParamInfo.push_back(std::move(ptrParamInfo)); \ } @@ -1126,7 +1129,7 @@ namespace sdv * @param description_string The description of the parameter. */ #define SDV_PARAM_NUMBER_ENTRY(var, name_string, default_val, low_limit, high_limit, unit_string, description_string) \ - { \ + { \ auto prLowLimit = sdv::internal::SLowerLimit() low_limit; \ sdv::any_t anyLower; \ if (prLowLimit.second != sdv::internal::ELimitType::no_limit) anyLower = prLowLimit.first; \ @@ -1144,8 +1147,9 @@ namespace sdv } \ else \ { \ - std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, bLockable, true, \ - name_string, default_val, anyLower, prLowLimit.second != sdv::internal::ELimitType::up_to_limit, anyUpper, \ + std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, \ + uiFlags & static_cast(::sdv::EParamFlags::read_only), bLockable, true, name_string, default_val, \ + anyLower, prLowLimit.second != sdv::internal::ELimitType::up_to_limit, anyUpper, \ prHighLimit.second != sdv::internal::ELimitType::up_to_limit, unit_string, ssGroup, description_string, \ uiFlags); \ if (ptrParamInfo)vecParamInfo.push_back(std::move(ptrParamInfo)); \ @@ -1181,8 +1185,9 @@ namespace sdv } \ else \ { \ - std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, bLockable, true, name_string, \ - default_val, pattern_string, unit_string, ssGroup, description_string, uiFlags); \ + std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, \ + uiFlags & static_cast(::sdv::EParamFlags::read_only), bLockable, true, name_string, default_val, \ + pattern_string, unit_string, ssGroup, description_string, uiFlags); \ if (ptrParamInfo) vecParamInfo.push_back(std::move(ptrParamInfo)); \ } @@ -1213,8 +1218,9 @@ namespace sdv } \ else \ { \ - std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, bLockable, true, name_string, \ - default_val, sdv::internal::GetLabelMapHelper().GetLabelMap(), ssGroup, description_string, uiFlags); \ + std::shared_ptr ptrParamInfo = pObject->RegisterParameter(pObject->var, \ + uiFlags & static_cast(::sdv::EParamFlags::read_only), bLockable, true, name_string, default_val, \ + sdv::internal::GetLabelMapHelper().GetLabelMap(), ssGroup, description_string, uiFlags); \ if (ptrParamInfo) vecParamInfo.push_back(std::move(ptrParamInfo)); \ } @@ -1238,11 +1244,12 @@ namespace sdv std::vector> vecParamInfoMember; \ if constexpr (bStatic) \ { \ - vecParamInfoMember = sdv::internal::SMemberMap::BuildStatic(); \ + vecParamInfoMember = sdv::internal::SMemberMap>::BuildStatic(); \ } \ else \ { \ - auto ptrMemberMap = std::make_shared>(pObject->member, pObject); \ + auto ptrMemberMap = \ + std::make_shared>>(pObject->member, pObject); \ if (ptrMemberMap) \ { \ ptrMemberMap->BuildMap(); \ @@ -1388,13 +1395,15 @@ namespace sdv * @tparam TVar Type of the variable. * @tparam TConstruct The arguments for the parameter info construct function. * @param[in] rtVar Reference to the parameter. + * @param[in] bReadOnly When set, the parameter is read only and will not be initialized. * @param[in] bLockable When set, the parameter is lockable. Only use with writable parameters. * @param[in] bAutoDirty When set, the parameter dirty flag is detected automatically. Only use with writable parameters. * @param[in] tConstruct The construct function arguments. * @return Smart pointer to the parameter information structure. */ template - std::shared_ptr RegisterParameter(TVar& rtVar, bool bLockable, bool bAutoDirty, TConstruct... tConstruct); + std::shared_ptr RegisterParameter(TVar& rtVar, bool bReadOnly, bool bLockable, bool bAutoDirty, + TConstruct... tConstruct); /** * @brief Register a member parameter map into this parameter map. diff --git a/export/support/param_impl.inl b/export/support/param_impl.inl index 83a6d67..9da6136 100644 --- a/export/support/param_impl.inl +++ b/export/support/param_impl.inl @@ -412,13 +412,14 @@ namespace sdv template template - inline CParamValue::CParamValue(TVar& rtVar, bool bLockable, bool bAutoDirty, TInfoConstruct... tConstruct) : + inline CParamValue::CParamValue(TVar& rtVar, bool bReadOnly, bool bLockable, bool bAutoDirty, + TInfoConstruct... tConstruct) : CParamGuardian(bLockable, bAutoDirty, rtVar, tConstruct...), m_rtVar(rtVar) { // Assign the default value if constexpr (!CSdvParamInfo::TypeIsReadOnly()) { - m_rtVar = DefaultVal().get(); + if (!bReadOnly) m_rtVar = DefaultVal().get(); UpdateDirty(DefaultVal()); ResetDirty(); } @@ -678,10 +679,10 @@ namespace sdv } template - inline std::shared_ptr CSdvParamMap::RegisterParameter(TVar& rtVar, bool bLockable, bool bAutoDirty, - TConstruct... tConstruct) + inline std::shared_ptr CSdvParamMap::RegisterParameter(TVar& rtVar, bool bReadOnly, bool bLockable, + bool bAutoDirty, TConstruct... tConstruct) { - auto ptrParam = std::make_shared>(rtVar, bLockable, bAutoDirty, tConstruct...); + auto ptrParam = std::make_shared>(rtVar, bReadOnly, bLockable, bAutoDirty, tConstruct...); m_vecParamMapRegistration.push_back(SParamRegistration(ptrParam)); return ptrParam; } diff --git a/export/support/pssup.inl b/export/support/pssup.inl index ded58f0..997e76c 100644 --- a/export/support/pssup.inl +++ b/export/support/pssup.inl @@ -32,6 +32,7 @@ #include #include #include +#include namespace serdes { @@ -378,7 +379,25 @@ namespace sdv inline CRawDataBypass& GetRawDataBypass() { +#if defined __GNUC__ && defined _WIN32 + // https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/610009 + // In MINGW GCC implementation of thread_local, there is an issue with complex classes being used after the destruction. + // This is caused by an architectural bug (https://github.com/msys2/MINGW-packages/issues/2519) causing the emutls (GCC + // Emulated TLS) managing the memory allocation to be executed before the __cxa_thread_exit, the thread cleanup callback + // of Windows. The solution is to use trivial data only (pointer, reference, integers, etc.). A workaround is + // implemented to use a reference of the permission list and map instead of an instance to the list and map. + static auto fnCreateBypass = []() -> CRawDataBypass& + { + static std::mutex mtx; + static std::list lstRawDataBypasses; + std::unique_lock lock(mtx); + lstRawDataBypasses.resize(lstRawDataBypasses.size() + 1); + return lstRawDataBypasses.back(); + }; + thread_local static CRawDataBypass& bypass = fnCreateBypass(); +#else thread_local static CRawDataBypass bypass; +#endif return bypass; } diff --git a/export/support/sdv_core.h b/export/support/sdv_core.h index 470f517..d285bac 100644 --- a/export/support/sdv_core.h +++ b/export/support/sdv_core.h @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef _WIN32 // Resolve conflict @@ -59,6 +60,8 @@ #error OS is not supported! #endif +#include "simple_toml.h" + namespace sdv { /** @@ -148,7 +151,36 @@ namespace sdv{ { /** * @brief The SDV core loader - */ + * @details The SDV core loader class searches for the core library and does the first startup. The core library uses + * the following environment variables to identify its location: + * - SDV_FRAMEWORK_RUNTIME directs to the Vehicle API core location. + * - SDV_COMPONENT_INSTALL directs to the location of the component installations. + * - SDV_FRAMEWORK_DEV_TOOLS directs to the location of the development tools used during the build process of SDV + * components. + * - SDV_FRAMEWORK_DEV_INCLUDE directs to the header file location to allow building components for use with the + * Vehicle API framework. + * + * The SDV core loader supports different core stacks to coexist. For this there is a location procedure for the core + * systems: + * 1. Check for a "sdv_core_reloc.toml" file that contain one or more paths to the core system. The paths defined in the + * file override the global environment variables for this application and all child applications. + * 2. Check for the environment variables. If the SDV_FRAMEWORK_RUNTIM variable is set, it is used to extract all other + * variables as well, if not set. + * 3. If no variable is set, use the location of the EXE as core location. This might work in some situations if the + * path variable has been set properly. + * + * If none of the above strategies work, the core cannot be loaded. + * + * The "sdv_core_reloc.toml" file has the following format: + * @code + * [CoreLocation] + * Version = 100 # Version; currently supported 100 for version 1.0 + * Runtime = "" + * Install = "" # Typically identical to runtime directory + * DevTools = "" # Typically identical to runtime directory + * Include = "" # Typically in the include subdirectory + * @endcode + */ class CSDVCoreLoader { public: @@ -189,77 +221,50 @@ namespace sdv{ { if (m_bInit) return; // Prevent trying to load again. m_bInit = true; - bool bRelocFileError = false; - std::string ssRelocFileInfo = ""; std::string ssEnvironmentInfo = ""; - // Check for the executable directory - std::filesystem::path pathCoreLib; - if (std::filesystem::exists(GetExecDirectory() / "core_services.sdv")) - pathCoreLib = GetExecDirectory() / "core_services.sdv"; + // Step 1: check for the "sdv_core_reloc.toml" + ProcessRelocationFile(); - if (pathCoreLib.empty()) - { - if (std::filesystem::exists(GetExecDirectory() / "sdv_core_reloc.toml")) - { - // Check for the library in the relocation directory - auto relocFolder = GetRelocationPath(); - if (!relocFolder.empty()) - { - auto coreLibFolder = std::filesystem::path(relocFolder) / "core_services.sdv"; - if (coreLibFolder.is_relative()) - coreLibFolder = (GetExecDirectory() / coreLibFolder).lexically_normal(); - - if (std::filesystem::exists(coreLibFolder)) - pathCoreLib = coreLibFolder; - } - if (pathCoreLib.empty()) - { - bRelocFileError = true; // we found the file but not the core library, we must run into an error - ssRelocFileInfo = "Error: Invalid \"sdv_core_reloc.toml\" file found (but no core library), it contains: " + relocFolder; - } - } - } - - if (pathCoreLib.empty()) - { + // Step 2: check for environment variables (either set globally or overwritten by the sdv_core_reloc.toml). #ifdef _WIN32 - std::wstring ssPathCoreTemp(32768, '\0'); - GetEnvironmentVariable(L"SDV_FRAMEWORK_RUNTIME", ssPathCoreTemp.data(), static_cast(ssPathCoreTemp.size())); - ssPathCoreTemp.resize(wcsnlen(ssPathCoreTemp.c_str(), ssPathCoreTemp.size())); - if (!ssPathCoreTemp.empty()) - { - pathCoreLib = std::filesystem::path(ssPathCoreTemp) / "core_services.sdv"; + std::wstring ssPathCoreTemp(32768, '\0'); + GetEnvironmentVariable( + L"SDV_FRAMEWORK_RUNTIME", ssPathCoreTemp.data(), static_cast(ssPathCoreTemp.size())); + ssPathCoreTemp.resize(wcsnlen(ssPathCoreTemp.c_str(), ssPathCoreTemp.size())); + if (!ssPathCoreTemp.empty()) + { + m_pathCoreLib = std::filesystem::path(ssPathCoreTemp) / "core_services.sdv"; #else - std::string ssPathCoreTemp = std::getenv("SDV_FRAMEWORK_RUNTIME") ? std::getenv("SDV_FRAMEWORK_RUNTIME") : ""; - if (!ssPathCoreTemp.empty()) - { - pathCoreLib = std::filesystem::path(ssPathCoreTemp) / "core_services.sdv"; + std::string ssPathCoreTemp = std::getenv("SDV_FRAMEWORK_RUNTIME") ? std::getenv("SDV_FRAMEWORK_RUNTIME") : ""; + if (!ssPathCoreTemp.empty()) + { + m_pathCoreLib = std::filesystem::path(ssPathCoreTemp) / "core_services.sdv"; #endif - if (pathCoreLib.is_relative()) - pathCoreLib = (GetExecDirectory() / pathCoreLib).lexically_normal(); + if (m_pathCoreLib.is_relative()) + m_pathCoreLib = (GetExecDirectory() / m_pathCoreLib).lexically_normal(); - if (!pathCoreLib.empty()) - ssEnvironmentInfo = "System environment path: " + pathCoreLib.generic_string(); - } } - // Depend on system path to find the library - if (pathCoreLib.empty()) - pathCoreLib = "core_services.sdv"; - - // Open the library only if there is no or a valid 'sdv_core_reloc.toml' file - if (!bRelocFileError) + // Step 3: check for the executable directory or otherwise check globally + if (m_pathCoreLib.empty()) { + if (std::filesystem::exists(GetExecDirectory() / "core_services.sdv")) + m_pathCoreLib = GetExecDirectory() / "core_services.sdv"; + else + m_pathCoreLib = "core_services.sdv"; + } + + ssEnvironmentInfo = "System environment path: " + m_pathCoreLib.generic_string(); + #ifdef _WIN32 - SetErrorMode(SEM_FAILCRITICALERRORS); - m_tModule = reinterpret_cast(LoadLibraryW(pathCoreLib.native().c_str())); + SetErrorMode(SEM_FAILCRITICALERRORS); + m_tModule = reinterpret_cast(LoadLibraryW(m_pathCoreLib.native().c_str())); #elif defined __unix__ - m_tModule = reinterpret_cast(dlopen(pathCoreLib.native().c_str(), RTLD_LAZY)); + m_tModule = reinterpret_cast(dlopen(m_pathCoreLib.native().c_str(), RTLD_LAZY)); #else #error OS is not supported! #endif - } if (!m_tModule) { @@ -275,16 +280,12 @@ namespace sdv{ #else #error OS is not supported! #endif - if (ssRelocFileInfo.empty() && ssEnvironmentInfo.empty()) - std::cerr << "No environment variable set and no realocation file found." << std::endl; - if (!ssRelocFileInfo.empty()) - std::cerr << ssRelocFileInfo << std::endl; if (!ssEnvironmentInfo.empty()) std::cerr << ssEnvironmentInfo << std::endl; - - std::cerr << "Could not load \"core_services.sdv\" library"; - if (!ssError.empty()) std::cerr << ": " << ssError; - std::cerr << std::endl; + m_ssErrMsg = "Could not load \"core_services.sdv\" library"; + if (!ssError.empty()) + m_ssErrMsg += ": " + ssError; + std::cerr << m_ssErrMsg << std::endl; return; } @@ -301,7 +302,8 @@ namespace sdv{ #endif if (!fnSDVCore) { - std::cerr << "The library \"core_services.sdv\" doesn't expose the SDVCore function." << std::endl; + m_ssErrMsg = "The library \"core_services.sdv\" doesn't expose the SDVCore function."; + std::cerr << m_ssErrMsg << std::endl; return; } @@ -311,7 +313,8 @@ namespace sdv{ m_pCore = fnSDVCore(); if (!m_pCore) { - std::cerr << "The library \"core_services.sdv\" doesn't provide a valid interface." << std::endl; + m_ssErrMsg = "The library \"core_services.sdv\" doesn't provide a valid interface."; + std::cerr << m_ssErrMsg << std::endl; return; } } @@ -321,7 +324,6 @@ namespace sdv{ */ operator TInterfaceAccessPtr() const { return m_pCore; } - private: /** * @brief Get the directory of the executable. * @return Path to the directory. @@ -346,86 +348,170 @@ namespace sdv{ } /** - * @brief Get the folder path in the file 'sdv_core_reloc.toml'. - * @return Path content, empty string if not found. - */ - static std::string GetRelocationPath() + * @brief Get the path to the core library. + * @return Returns a reference to the path to the core library. + */ + const std::filesystem::path& GetCoreLibPath() const { - if (std::filesystem::exists(GetExecDirectory() / "sdv_core_reloc.toml")) - { - std::ifstream fstream(GetExecDirectory() / "sdv_core_reloc.toml"); - std::string ssLine; - while (std::getline(fstream, ssLine)) - { - size_t nPos = 0; - auto fnSkipWhitespace = [&]() { while (std::isspace(ssLine[nPos])) nPos++; }; - fnSkipWhitespace(); - if (ssLine[nPos] == '#') continue; // Rest of the line is comments - if (ssLine.substr(nPos, 9) != "directory") continue; // not the keq of interest: skip line - nPos += 9; - fnSkipWhitespace(); - if (ssLine[nPos] != '=') - { - std::cout << "Error in \"sdv_core_reloc.toml\": expecting assignment character '=' following" - " keyword 'directory'." << std::endl; - break; - } - nPos++; - fnSkipWhitespace(); - if (ssLine[nPos] != '\"') - { - std::cout << "Error in \"sdv_core_reloc.toml\": expecting double quote character '\"' indicating" - " a string begin'." << std::endl; - break; - } - nPos++; - size_t nStart = nPos; - while (nPos < ssLine.length() && ssLine[nPos] != '\"') - { - // Check for escape character - if (ssLine[nPos] == '\\') nPos++; - - // Skip character - nPos++; - } - if (nPos >= ssLine.length() || ssLine[nPos] != '\"') - { - std::cout << "Error in \"sdv_core_reloc.toml\": expecting double quote character '\"' indicating" - " a string end'." << std::endl; - break; - } - std::string ssDirectory = ssLine.substr(nStart, nPos - nStart); - while (ssDirectory.empty()) - { - std::cout << "Error in \"sdv_core_reloc.toml\": expecting a valid value following the assignment" - " of the 'directory' key." << std::endl; - break; - } - - return ssDirectory; - } - } - return ""; + return m_pathCoreLib; } - bool m_bInit = false; ///< Is the loader initialized? - core::TModuleID m_tModule = 0; ///< Module ID - IInterfaceAccess* m_pCore = nullptr; ///< Pointer to the core services. + /** + * @brief Has loaded successfully? + * @return Returns whether loading was successful. + */ + bool HasLoaded() const + { + return m_bInit && m_tModule && m_pCore; + } + + /** + * @brief Get the error message if loading was unsuccessful. + * @return Returns the a reference to the string containing the error message or an empty string if not available. + */ + const std::string& GetErrorMsg() const + { + return m_ssErrMsg; + } + + private: + /** + * @brief Check for the 'sdv_core_reloc.toml' file containing the information about core relocation. + * @details Set the environment variables following the information from the core relocation file. + */ + static void ProcessRelocationFile() + { + if (!std::filesystem::exists(GetExecDirectory() / "sdv_core_reloc.toml")) return; + + std::ifstream fstream(GetExecDirectory() / "sdv_core_reloc.toml"); + if (!fstream.is_open()) return; + + std::stringstream sstreamContent; + sstreamContent << fstream.rdbuf(); + fstream.close(); + + try + { + sdv::toml::simple_parser::CParser parser(sstreamContent.str()); + if (parser.Root().GetDirect("CoreLocation.Version").GetValue() != SDVFrameworkInterfaceVersion) + { + std::cerr << "Invalid version in sdv_core_reloc.toml" << std::endl; + return; // Version not supported + } + + const auto& sRuntimeNode = parser.Root().GetDirect("CoreLocation.Runtime"); + if (sRuntimeNode) + { + // Note: since the system might load multiple executables and the path is relative to this executable, + // but maybe not to another executable, create an absolute path before assigning the environment + // variable. + std::filesystem::path pathRuntime = sRuntimeNode.GetValue(); + if (pathRuntime.is_relative()) + pathRuntime = (GetExecDirectory() / pathRuntime).lexically_normal(); +#ifdef _WIN32 + // NOTE: In windows there are two environment variables which need to be updated. + std::ignore = SetEnvironmentVariable(L"SDV_FRAMEWORK_RUNTIME", pathRuntime.native().c_str()); + std::ignore = _wputenv((std::wstring(L"SDV_FRAMEWORK_RUNTIME=") + pathRuntime.native()).c_str()); +#elif defined __unix__ + std::ignore = setenv("SDV_FRAMEWORK_RUNTIME", pathRuntime.generic_u8string().c_str(), 1); +#else +#error The OS is not supported! +#endif + } + + const auto& sInstallNode = parser.Root().GetDirect("CoreLocation.Install"); + if (sInstallNode) + { + // Note: since the system might load multiple executables and the path is relative to this executable, + // but maybe not to another executable, create an absolute path before assigning the environment + // variable. + std::filesystem::path pathInstall = sInstallNode.GetValue(); + if (pathInstall.is_relative()) + pathInstall = (GetExecDirectory() / pathInstall).lexically_normal(); +#ifdef _WIN32 + // NOTE: In windows there are two environment variables which need to be updated. + std::ignore = SetEnvironmentVariable(L"SDV_COMPONENT_INSTALL", pathInstall.native().c_str()); + std::ignore = _wputenv((std::wstring(L"SDV_COMPONENT_INSTALL=") + pathInstall.native()).c_str()); +#elif defined __unix__ + std::ignore = setenv("SDV_COMPONENT_INSTALL", pathInstall.generic_u8string().c_str(), 1); +#else +#error The OS is not supported! +#endif + } + + const auto& sDevToolsNode = parser.Root().GetDirect("CoreLocation.DevTools"); + if (sDevToolsNode) + { + // Note: since the system might load multiple executables and the path is relative to this executable, + // but maybe not to another executable, create an absolute path before assigning the environment + // variable. + std::filesystem::path pathDevTools = sDevToolsNode.GetValue(); + if (pathDevTools.is_relative()) + pathDevTools = (GetExecDirectory() / pathDevTools).lexically_normal(); +#ifdef _WIN32 + // NOTE: In windows there are two environment variables which need to be updated. + std::ignore = SetEnvironmentVariable(L"SDV_FRAMEWORK_DEV_TOOLS", pathDevTools.native().c_str()); + std::ignore = _wputenv((std::wstring(L"SDV_FRAMEWORK_DEV_TOOLS=") + pathDevTools.native()).c_str()); +#elif defined __unix__ + std::ignore = setenv("SDV_FRAMEWORK_DEV_TOOLS", pathDevTools.generic_u8string().c_str(), 1); +#else +#error The OS is not supported! +#endif + } + + const auto& sIncludeNode = parser.Root().GetDirect("CoreLocation.Include"); + if (sIncludeNode) + { + // Note: since the system might load multiple executables and the path is relative to this executable, + // but maybe not to another executable, create an absolute path before assigning the environment + // variable. + std::filesystem::path pathInclude = sIncludeNode.GetValue(); + if (pathInclude.is_relative()) + pathInclude = (GetExecDirectory() / pathInclude).lexically_normal(); +#ifdef _WIN32 + // NOTE: In windows there are two environment variables which need to be updated. + std::ignore = SetEnvironmentVariable(L"SDV_FRAMEWORK_DEV_INCLUDE", pathInclude.native().c_str()); + std::ignore = _wputenv((std::wstring(L"SDV_FRAMEWORK_DEV_INCLUDE=") + pathInclude.native()).c_str()); +#elif defined __unix__ + std::ignore = setenv("SDV_FRAMEWORK_DEV_INCLUDE", pathInclude.generic_u8string().c_str(), 1); +#else +#error The OS is not supported! +#endif + } + } + catch (const std::exception&) + {} + } + + bool m_bInit = false; ///< Is the loader initialized? + core::TModuleID m_tModule = 0; ///< Module ID + IInterfaceAccess* m_pCore = nullptr; ///< Pointer to the core services. + std::filesystem::path m_pathCoreLib; ///< Path to the core library. + std::string m_ssErrMsg; ///< Error message for failed loading. }; } // namespace internal #ifndef NO_SDV_CORE_FUNC /** - * @brief Access to the core. - * @return Smart pointer to the core services interface. + * @brief Access to the core loader. + * @return Reference to the one core loader instance. */ - inline TInterfaceAccessPtr GetCore() + inline internal::CSDVCoreLoader& GetCoreLoader() { static internal::CSDVCoreLoader core; core.Load(); return core; } + /** + * @brief Access to the core interfaces. + * @return Smart pointer to the core services interfaces. + */ + inline TInterfaceAccessPtr GetCore() + { + return GetCoreLoader(); + } + /** * @brief Access to specific interface of the core. * @tparam TInterface Type of interface to return. diff --git a/export/support/signal_support.h b/export/support/signal_support.h index 2485105..0e4d490 100644 --- a/export/support/signal_support.h +++ b/export/support/signal_support.h @@ -546,10 +546,10 @@ namespace sdv if (m_ptrValue) m_ptrValue->Receive(anyVal); } - CDispatchService& m_rDispatch; ///< Reference to the dispatch service. - std::function m_funcSignalReceive; ///< Receive signal data - callback function. - IInterfaceAccess* m_pSubscription = nullptr; ///< Cookie received by adding an receive subscription. - std::unique_ptr m_ptrValue; ///< Value to update instead of a callback function. + CDispatchService& m_rDispatch; ///< Reference to the dispatch service. + std::function m_funcSignalReceive; ///< Receive signal data - callback function. + IInterfaceAccess* m_pSubscription = nullptr; ///< Cookie received by adding a subscription. + std::unique_ptr m_ptrValue; ///< Value to update instead of callback func. }; /** diff --git a/export/support/simple_toml.h b/export/support/simple_toml.h new file mode 100644 index 0000000..89db338 --- /dev/null +++ b/export/support/simple_toml.h @@ -0,0 +1,1010 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +// Some GCC compilers return an overflow warning on string_view functions that use npos as a parameter. This is a known issue +// described here: +// https://stackoverflow.com/questions/79738323/gcc-warning-memcpy-specified-bound-18446744073709551614-exceeds-maximum-objec +// Suppress the warning. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#pragma GCC diagnostic ignored "-Walloc-size-larger-than=" +#pragma GCC diagnostic ignored "-Wstringop-overread" +#endif + +/** + * @brief Simple TOML parser to read keys and tables. This TOML parser can be used to parse a TOML string without the use of the + * SDV framework (e.g. before system startup). + */ +namespace sdv::toml::simple_parser +{ + /** + * @brief Enumeration for all supported TOML node types + */ + enum class ENodeType + { + node_unknown, ///< Node type not valid. + node_table, ///< Node represents a map of key-value pairs + node_array, ///< Node represents a list of elements + node_value ///< Node represents a scalar value (string, number, boolean, etc.) + }; + + // Forward declaration of the main data structure + struct SNode; + + /// Table map alias + using TTableMap = std::map; + + /// Array vector alias + using TNodeArray = std::vector; + + /** + * @brief Main variant structure holding the configuration tree data + */ + struct SNode + { + ENodeType eType = ENodeType::node_unknown; ///< Enumeration Type + std::string ssKey; ///< Name of the value key + std::string ssValueRaw = ""; ///< String raw value + TTableMap mapTable = {}; ///< Map Table + TNodeArray vecArray = {}; ///< Vector Array + + bool bIsInline = false; ///< Locked if defined via inline {...} syntax + bool bIsExplicitlyHeadered = false; ///< Set to true when defined via [header] syntax + + /** + * @brief Helper checking if node is valid. + * @return Returns whether the node is valid (is not unknown). + */ + operator bool() const noexcept + { + return eType != ENodeType::node_unknown; + } + + /** + * @brief Helper checking if node is valid. + * @return Returns whether the node is valid (is not unknown). + */ + bool IsValid() const noexcept + { + return eType != ENodeType::node_unknown; + } + + /** + * @brief Helper checking if node is a table. + * @return Returns whether the node is a table. + */ + bool IsTable() const noexcept + { + return eType == ENodeType::node_table; + } + + /** + * @brief Helper checking if node is an array. + * @return Returns whether the node is an array. + */ + bool IsArray() const noexcept + { + return eType == ENodeType::node_array; + } + + /** + * @brief Helper checking if node is a scalar value. + * @return Returns whether the node is a value node. + */ + bool IsValue() const noexcept + { + return eType == ENodeType::node_value; + } + + /** + * @brief Access function returning the type. + * @return The type of the node. + */ + ENodeType GetType() const noexcept + { + return eType; + } + + /** + * @brief Access function returning the key name. + * @return Reference to the string containing the key name (if not an array member) or an empty string when there is no + * key name. + */ + const std::string& GetName() const noexcept + { + return ssKey; + } + + /** + * @brief Returns the raw value. + * @return Reference to the string containing the raw value of the node. Or returns an empty string when the node is not + * a value node or doesn't contain a value. + */ + const std::string& GetValue() const noexcept + { + return ssValueRaw; + } + + /** + * @brief Return the value converted to the provided type. + * @tparam TType Type to convert to. Supported are integral types, bool, floating point types and std::string. + * @return The result from the conversion or an empty result if the conversion could not be done. + */ + template + TType GetValue() const + { + if constexpr (std::is_same_v) + { + return ssValueRaw == "true" || GetValue() != 0; + } + else if constexpr (std::is_integral_v || std::is_floating_point_v) + { + TType tValue{}; + auto [ptr, ec] = std::from_chars(ssValueRaw.data(), ssValueRaw.data() + ssValueRaw.size(), tValue); + return ec == std::errc() ? tValue : TType{}; + } + else if (std::is_same_v) + { + return ssValueRaw; + } + else + return {}; + } + + /** + * @brief Returns the table map. + * @return If the node is a table, returns a reference to the table map. Or returns an empty map if the node is not a table + * or doesn't contain child nodes. + */ + const TTableMap& GetTable() const noexcept + { + return mapTable; + } + + /** + * @brief Return the array vector. + * @return If the node is an array, returns a reference to the array vector. Or returns an empty vector if the node is not + * an array or doesn't contain any elements. + */ + const TNodeArray& GetArray() const noexcept + { + return vecArray; + } + + /** + * @brief Safe, non-recursive direct lookup using dot and array notation (e.g., "table.array[1].key") + * @param[in] svPath String containing the value path to look for. + * @return Returns a safe, read-only reference to the node of an empty node when not found. + */ + const SNode GetDirect(std::string_view svPath) const noexcept + { + static const SNode sNodeEmpty; + std::reference_wrapper refCurrentNode = *this; + size_t nStart = 0; // Index counter for path segments + + while (nStart < svPath.size()) + { + // 1. Find the next path segment up to the dot delimiter + size_t nDot = svPath.find('.', nStart); + std::string_view svPart = svPath.substr(nStart, nDot == std::string_view::npos ? nDot : nDot - nStart); + + if (svPart.empty()) return sNodeEmpty; + + // 2. Check if the segment contains array brackets [...] (e.g., "arr_mixed[4]") + size_t nOpenBracket = svPart.find('['); + std::string ssKeyLocal(svPart.substr(0, nOpenBracket)); + + // Must be a table node to perform a key lookup + if (!refCurrentNode.get().IsTable()) return sNodeEmpty; + + auto it = refCurrentNode.get().mapTable.find(ssKeyLocal); + if (it == refCurrentNode.get().mapTable.end()) + return sNodeEmpty; + + refCurrentNode = std::cref(it->second); + + // 3. Resolve array indices sequentially (supports multidimensional arrays or inline tables in arrays) + size_t nBracketPos = nOpenBracket; + while (nBracketPos != std::string_view::npos) + { + size_t nCloseBracket = svPart.find(']', nBracketPos); + if (nCloseBracket == std::string_view::npos) + return sNodeEmpty; // Error: Malformed unclosed bracket + + // Extract and parse the index string substring + std::string_view svIndex = svPart.substr(nBracketPos + 1, nCloseBracket - nBracketPos - 1); + + // Ensure the index consists strictly of digits (prevents negative signs or alpha characters) + if (svIndex.empty() || + !std::all_of(svIndex.begin(), svIndex.end(), [](unsigned char c) { return std::isdigit(c); })) + return sNodeEmpty; + + size_t nIdx = 0; + try + { + nIdx = static_cast(std::stoull(std::string(svIndex))); + } + catch (...) + { + return sNodeEmpty; // Safety catch against integer overflow attacks during parsing + } + + // SNode type verification and strict bounds check (Safety-critical constraint) + if (!refCurrentNode.get().IsArray() || nIdx >= refCurrentNode.get().vecArray.size()) + return sNodeEmpty; + + // Advance target reference directly into the array index element + refCurrentNode = std::cref(refCurrentNode.get().vecArray[nIdx]); + + // Check for a consecutive opening bracket immediately following (multidimensional arrays) + nBracketPos = svPart.find('[', nCloseBracket); + } + + // Step over the dot delimiter to move onto the next token sequence + if (nDot == std::string_view::npos) break; + nStart = nDot + 1; + } + return refCurrentNode; + } + }; + + /** + * @brief Main processing unit containing the parsing logic + */ + class CParser + { + public: + /** + * @brief Explicit constructor initializing the parser target view bounds + * @param[in] svInput The TOML string. + */ + // cppcheck-suppress passedByValue + CParser(std::string_view svInput) : m_svSrc(svInput), m_nPos(0) + { + Parse(); + } + + /** + * @brief Get the root node. + * @return Return a reference to the root node. + */ + const SNode& Root() const noexcept + { + return m_sRoot; + } + + private: + std::string_view m_svSrc; ///< Member String View + size_t m_nPos; ///< Member Numerical index) + SNode m_sRoot; ///< Member Object structure + /// Safety Extension: Stores dot-joined path strings of tables defined explicitly via [headers] + std::vector m_vecExplicitlyDefinedTables; + + /** + * @brief Executes the lexical analysis and builds the root node tree + * @return The root node. + */ + SNode Parse() + { + m_sRoot.eType = ENodeType::node_table; + std::vector vCurrentTablePath; + + while (!IsEof()) + { + // Only skip inline whitespace and comments, preserve newlines to check line boundaries + while (!IsEof()) + { + char c = Peek(); + if (c == ' ' || c == '\t' || c == '\r') + { + Consume(); + } + else if (c == '#') + { + while (!IsEof() && Peek() != '\n' && Peek() != '\r') + Consume(); + } + else + { + break; + } + } + if (IsEof()) + break; + + char cNext = Peek(); + if (cNext == '\n') + { + Consume(); // Valid empty line separation + continue; + } + + if (cNext == '[') + { + vCurrentTablePath = ParseTableHeader(); + } + else + { + SNode& oActiveTable = NavigateToTable(vCurrentTablePath); + ParseKeyValue(oActiveTable); + } + + // Safety Constraint: A key-value assignment or header MUST be followed immediately + // by a newline, a comment, or the end of the file. No trailing gibberish allowed on the same line. + while (!IsEof() && (Peek() == ' ' || Peek() == '\t')) + Consume(); + if (!IsEof() && Peek() == '#') + { + while (!IsEof() && Peek() != '\n' && Peek() != '\r') + Consume(); + } + if (!IsEof() && Peek() != '\n' && Peek() != '\r') + { + throw std::runtime_error("Multiple declarations on a single line are strictly invalid in TOML."); + } + } + return m_sRoot; + } + + /** + * @brief Internal safety helper navigating down a string path to return a mutable reference. Automatically diverts into the + * newest element block if tracking a structural Table Array. + * @param[in] rvecPath Reference to vector containing the path + * @return Reference to the node represented by the path. + */ + SNode& NavigateToTable(const std::vector& rvecPath) + { + std::reference_wrapper refCurr = m_sRoot; + for (const auto& rssSection : rvecPath) + { + // Safe traversal step into the standard node map structure + refCurr = std::ref(refCurr.get().mapTable[rssSection]); + + // Context Redirect: If target is a Table Array, dive into the last active table instance + if (refCurr.get().IsArray() && !refCurr.get().vecArray.empty()) + refCurr = std::ref(refCurr.get().vecArray.back()); + } + return refCurr.get(); + } + + /** + * @brief Safety checks ensuring operations remain within string limits + * @return Returns whether the current position exceeds the string size. + */ + bool IsEof() const noexcept + { + return m_nPos >= m_svSrc.size(); + } + + /** + * @brief Non-destructive parsing look-ahead function + * @return The character at the position or a null-character if EOF. + */ + char Peek() const noexcept + { + return IsEof() ? '\0' : m_svSrc[m_nPos]; + } + + /** + * @brief Advances processing cursor forward safely + * @return The character at the position or a null-character if EOF. + */ + char Consume() noexcept + { + return IsEof() ? '\0' : m_svSrc[m_nPos++]; + } + + /** + * @brief Skips standard spacing gaps, multi-line format updates, and hash strings + */ + void SkipWhitespaceAndComments() noexcept + { + while (!IsEof()) + { + char c = Peek(); + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + { + Consume(); + } + else if (c == '#') + { + while (!IsEof() && Peek() != '\n' && Peek() != '\r') + { + Consume(); + } + } + else + { + break; + } + } + } + + /** + * @brief Skips single inline whitespaces (spaces and tabs) + */ + void SkipInlineWhitespace() noexcept + { + while (!IsEof() && (Peek() == ' ' || Peek() == '\t')) + Consume(); + } + + /** + * @brief Evaluates complex dot-separated key sequences safely + * @return Returns a vector containing the dotted key parts. + */ + std::vector ParseDottedKey() + { + std::vector vecKeys; // Vector of strings + while (!IsEof()) + { + SkipInlineWhitespace(); + std::string ssKey = ""; // String token + char c = Peek(); + if (c == '"' || c == '\'') + ssKey = ParseStringNode(); + else + { + while (!IsEof()) + { + char bc = Peek(); // Bare Char + if (std::isalnum(static_cast(bc)) || bc == '-' || bc == '_') + { + ssKey += Consume(); + } + else + { + break; + } + } + if (ssKey.empty()) + throw std::runtime_error("Empty or invalid key sequence."); + } + vecKeys.push_back(ssKey); + SkipInlineWhitespace(); + if (Peek() == '.') + { + Consume(); + } + else + { + break; + } + } + return vecKeys; + } + + /** + * @brief Handles text string isolation and processes updated escape arrays + * @return The string after parsing. + */ + std::string ParseStringNode() + { + char cQuote = Consume(); // Character delimiter + bool bIsMultiline = false; // Boolean flag + if (Peek() == cQuote) + { + Consume(); + // Warning of cppcheck for the condition to be always true. This is not the case due to the Consume function + // execution. Suppress warning. + // cppcheck-suppress knownConditionTrueFalse + if (Peek() == cQuote) + { + Consume(); + bIsMultiline = true; + } + else + { + return ""; + } + } + + std::string ssResult = ""; + while (!IsEof()) + { + if (bIsMultiline && Peek() == cQuote) + { + if (m_nPos + 2 < m_svSrc.size() && m_svSrc[m_nPos + 1] == cQuote && m_svSrc[m_nPos + 2] == cQuote) + { + m_nPos += 3; + return ssResult; + } + } + else if (!bIsMultiline && Peek() == cQuote) + { + Consume(); + return ssResult; + } + + char c = Consume(); + if (cQuote == '"' && c == '\\') + { + if (IsEof()) + throw std::runtime_error("Unfinished escape sequence."); + char cEsc = Consume(); // Character escape sequence identifier + if (cEsc == 'n') + ssResult += '\n'; + else if (cEsc == 't') + ssResult += '\t'; + else if (cEsc == 'r') + ssResult += '\r'; + else if (cEsc == '"') + ssResult += '"'; + else if (cEsc == '\\') + ssResult += '\\'; + else if (cEsc == 'e') + ssResult += '\x1b'; + else if (cEsc == 'x') + { + if (m_nPos + 1 >= m_svSrc.size()) + throw std::runtime_error("Truncated hex character."); + std::string ssHex{Consume(), Consume()}; + ssResult += static_cast(std::stoi(ssHex, nullptr, 16)); + } + else if (bIsMultiline && (cEsc == '\n' || cEsc == '\r')) + { + if (cEsc == '\r' && Peek() == '\n') + Consume(); + SkipInlineWhitespace(); + } + } + else + { + ssResult += c; + } + } + throw std::runtime_error("Unterminated TOML string sequence detected."); + } + + /** + * @brief Evaluates header configurations structural tables or table arrays ([[table.path]]) + * @return Returns a vector with table headers. + */ + //std::vector ParseTableHeader() + //{ + // Consume(); // Consume standard structural '[' + + // bool bIsTableArray = false; + // if (Peek() == '[') + // { + // Consume(); + // bIsTableArray = true; + // } + + // std::vector vecSections = ParseDottedKey(); + // SkipInlineWhitespace(); + + // if (bIsTableArray) + // { + // if (Consume() != ']' || Consume() != ']') + // throw std::runtime_error("Malformed table array closure."); + // } + // else + // { + // if (Consume() != ']') + // throw std::runtime_error("Malformed table closure."); + // } + + // // Generate a unified lookup string for this table path (e.g., "fruit.apple") + // std::string strFullNormalizedPath = ""; + // for (size_t i = 0; i < vecSections.size(); ++i) + // { + // strFullNormalizedPath += vecSections[i] + (i == vecSections.size() - 1 ? "" : "."); + // } + + // // Safety Constraint: Check if this specific exact table header path was already explicitly declared + // if (!bIsTableArray) + // { + // if (std::find(m_vecExplicitlyDefinedTables.begin(), m_vecExplicitlyDefinedTables.end(), strFullNormalizedPath) + // != m_vecExplicitlyDefinedTables.end()) + // { + // throw std::runtime_error("Duplicate table declaration detected: " + strFullNormalizedPath); + // } + // m_vecExplicitlyDefinedTables.push_back(strFullNormalizedPath); + // } + + // std::reference_wrapper refCurrent = m_sRoot; + // for (size_t i = 0; i < vecSections.size() - 1; ++i) + // { + // const auto& ssSection = vecSections[i]; + + // auto it = refCurrent.get().mapTable.find(ssSection); + // if (it != refCurrent.get().mapTable.end()) + // { + // if (it->second.IsValue()) + // { + // throw std::runtime_error("Cannot re-define a scalar key value as an intermediate table structure."); + // } + // // If it exists but is a literal array (not explicitly headered), collision! + // if (it->second.IsArray() && !it->second.bIsExplicitlyHeadered) + // { + // throw std::runtime_error("Type collision: Cannot append table elements to a literal array."); + // } + // } + // else + // { + // SNode sNewNode; + // sNewNode.eType = ENodeType::node_table; + // sNewNode.ssKey = ssSection; + // refCurrent.get().mapTable[ssSection] = sNewNode; + // } + // refCurrent = std::ref(refCurrent.get().mapTable[ssSection]); + // if (refCurrent.get().IsArray() && !refCurrent.get().vecArray.empty()) + // { + // refCurrent = std::ref(refCurrent.get().vecArray.back()); + // } + // } + + // std::string ssFinalSection = vecSections.back(); + // auto itFinal = refCurrent.get().mapTable.find(ssFinalSection); + + // if (itFinal != refCurrent.get().mapTable.end()) + // { + // if (itFinal->second.IsValue()) + // { + // throw std::runtime_error("Conflict: Table header overrides an existing scalar value."); + // } + // if (bIsTableArray && itFinal->second.IsTable()) + // { + // throw std::runtime_error("Type collision: Static table cannot be turned into a table array."); + // } + // if (!bIsTableArray && itFinal->second.IsArray()) + // { + // if (itFinal->second.bIsExplicitlyHeadered) + // { + // throw std::runtime_error("Type collision: Array of tables cannot be redefined as a standard table."); + // } + // else + // { + // throw std::runtime_error("Type collision: Literal array cannot be redefined as a table."); + // } + // } + + // // CRITICAL FIX: The invalid exception here was thrown when itFinal->second.IsTable() was true + // // but it was implicitly created by a sub-key earlier (e.g., fruit.apple). + // // In TOML, a header can safely claim an implicit table, UNLESS it attempts to redefine a value + // // that is an explicit scalar or another explicit table type. + // } + + // if (bIsTableArray) + // { + // if (refCurrent.get().mapTable.find(ssFinalSection) == refCurrent.get().mapTable.end()) + // { + // SNode sNewArrayNode; + // sNewArrayNode.eType = ENodeType::node_array; + // sNewArrayNode.ssKey = ssFinalSection; + // sNewArrayNode.bIsExplicitlyHeadered = true; // Mark as Table Array type + // refCurrent.get().mapTable[ssFinalSection] = sNewArrayNode; + // } + // SNode sNewTableInstance; + // sNewTableInstance.eType = ENodeType::node_table; + // sNewTableInstance.ssKey = ssFinalSection; + // sNewTableInstance.bIsExplicitlyHeadered = true; + // refCurrent.get().mapTable[ssFinalSection].vecArray.push_back(sNewTableInstance); + // } + // else + // { + // if (refCurrent.get().mapTable.find(ssFinalSection) == refCurrent.get().mapTable.end()) + // { + // SNode sNewNode; + // sNewNode.eType = ENodeType::node_table; + // sNewNode.ssKey = ssFinalSection; + // sNewNode.bIsExplicitlyHeadered = true; + // refCurrent.get().mapTable[ssFinalSection] = sNewNode; + // } + // else + // { + // refCurrent.get().mapTable[ssFinalSection].bIsExplicitlyHeadered = true; + // } + // } + + // return vecSections; + //} + // Evaluates header configurations structural tables or table arrays ([[table.path]]) + std::vector ParseTableHeader() + { + Consume(); // Consume standard structural '[' + + bool bIsTableArray = false; + if (Peek() == '[') + { + Consume(); + bIsTableArray = true; + } + + std::vector vecSections = ParseDottedKey(); + SkipInlineWhitespace(); + + if (bIsTableArray) + { + if (Consume() != ']' || Consume() != ']') + throw std::runtime_error("Malformed table array closure."); + } + else + { + if (Consume() != ']') + throw std::runtime_error("Malformed table closure."); + } + + std::reference_wrapper refCurrent = m_sRoot; + + // 1. Traverse down through intermediate namespaces until the second-to-last token component + for (size_t i = 0; i < vecSections.size() - 1; ++i) + { + const auto& ssSection = vecSections[i]; + + if (refCurrent.get().bIsInline) + { + throw std::runtime_error("Cannot add subtables to an immutable inline table."); + } + + auto it = refCurrent.get().mapTable.find(ssSection); + if (it != refCurrent.get().mapTable.end()) + { + if (it->second.IsValue()) + { + throw std::runtime_error("Cannot re-define a scalar key value as an intermediate table structure."); + } + if (it->second.IsArray() && !it->second.bIsExplicitlyHeadered) + { + throw std::runtime_error("Type collision: Cannot append table elements to a literal array."); + } + } + else + { + SNode sNewNode; + sNewNode.eType = ENodeType::node_table; + sNewNode.ssKey = ssSection; + refCurrent.get().mapTable[ssSection] = sNewNode; + } + + // Advance cursor reference + refCurrent = std::ref(refCurrent.get().mapTable[ssSection]); + + // Context Redirect: Always dive into the newest array element block if tracking a structural Table Array + if (refCurrent.get().IsArray() && !refCurrent.get().vecArray.empty()) + { + refCurrent = std::ref(refCurrent.get().vecArray.back()); + } + } + + // 2. Handle the final explicit destination node leaf signature + std::string ssFinalSection = vecSections.back(); + auto itFinal = refCurrent.get().mapTable.find(ssFinalSection); + + if (itFinal != refCurrent.get().mapTable.end()) + { + if (itFinal->second.IsValue()) + { + throw std::runtime_error("Conflict: Table header overrides an existing scalar value."); + } + if (bIsTableArray && itFinal->second.IsTable()) + { + throw std::runtime_error("Type collision: Static table cannot be turned into a table array."); + } + if (!bIsTableArray && itFinal->second.IsArray()) + { + if (itFinal->second.bIsExplicitlyHeadered) + { + throw std::runtime_error("Type collision: Array of tables cannot be redefined as a standard table."); + } + else + { + throw std::runtime_error("Type collision: Literal array cannot be redefined as a table."); + } + } + + // SAFETY CONSTRAINT REMEDIED: + // Only throw duplicate declaration errors if a standard table header is redefined + // *explicitly* inside the exact same local scope block wrapper. + if (!bIsTableArray && itFinal->second.IsTable() && itFinal->second.bIsExplicitlyHeadered) + { + throw std::runtime_error("Duplicate table declaration detected in this scope: " + ssFinalSection); + } + } + + // 3. Construct or instantiate the final target node leaf elements safely + if (bIsTableArray) + { + if (refCurrent.get().mapTable.find(ssFinalSection) == refCurrent.get().mapTable.end()) + { + SNode sNewArrayNode; + sNewArrayNode.eType = ENodeType::node_array; + sNewArrayNode.ssKey = ssFinalSection; + sNewArrayNode.bIsExplicitlyHeadered = true; + refCurrent.get().mapTable[ssFinalSection] = sNewArrayNode; + } + SNode sNewTableInstance; + sNewTableInstance.eType = ENodeType::node_table; + sNewTableInstance.ssKey = ssFinalSection; + sNewTableInstance.bIsExplicitlyHeadered = true; + refCurrent.get().mapTable[ssFinalSection].vecArray.push_back(sNewTableInstance); + } + else + { + if (refCurrent.get().mapTable.find(ssFinalSection) == refCurrent.get().mapTable.end()) + { + SNode sNewNode; + sNewNode.eType = ENodeType::node_table; + sNewNode.ssKey = ssFinalSection; + sNewNode.bIsExplicitlyHeadered = true; + refCurrent.get().mapTable[ssFinalSection] = sNewNode; + } + else + { + refCurrent.get().mapTable[ssFinalSection].bIsExplicitlyHeadered = true; + } + } + + return vecSections; + } + + /** + * @brief Extracts key assignment links and creates required target structural tables. Works relative to the passed + * rsParentTable node, ensuring proper nesting. + * @param[in] rsParentTable Reference to the parent table. + */ + void ParseKeyValue(SNode& rsParentTable) + { + std::vector vecKeys = ParseDottedKey(); + SkipInlineWhitespace(); + if (Consume() != '=') + throw std::runtime_error("Missing valid variable declaration assignment mapping."); + SkipInlineWhitespace(); + + std::reference_wrapper refTarget = std::ref(rsParentTable); + for (size_t i = 0; i < vecKeys.size() - 1; ++i) + { + // ONLY block if we are attempting to add elements down through a table that was completed + // and locked as an immutable inline structure from an external scope. + if (refTarget.get().bIsInline) + { + throw std::runtime_error("Cannot traverse or append keys through an immutable inline table."); + } + + auto it = refTarget.get().mapTable.find(vecKeys[i]); + if (it != refTarget.get().mapTable.end()) + { + if (it->second.IsValue()) + { + throw std::runtime_error("Collision: Implied intermediate segment conflicts with an existing scalar."); + } + if (it->second.bIsInline) + { + throw std::runtime_error("Collision: Implied path segment penetrates an immutable inline table."); + } + } + else + { + SNode sNewNode; + sNewNode.eType = ENodeType::node_table; + sNewNode.ssKey = vecKeys[i]; + refTarget.get().mapTable[vecKeys[i]] = sNewNode; + } + refTarget = std::ref(refTarget.get().mapTable[vecKeys[i]]); + if (refTarget.get().IsArray() && !refTarget.get().vecArray.empty()) + { + refTarget = std::ref(refTarget.get().vecArray.back()); + } + } + + std::string ssFinalKey = vecKeys.back(); + + // Safety Constraint: Check if the final target key value has already been populated in this specific map context + if (refTarget.get().mapTable.find(ssFinalKey) != refTarget.get().mapTable.end()) + { + throw std::runtime_error("Duplicate key configuration definition detected: " + ssFinalKey); + } + + SNode sParsedValue = ParseValueNode(); + + // Safety Constraint: Catch empty data assignment attempts like `key = # comment` + if (sParsedValue.IsValue() && sParsedValue.ssValueRaw.empty()) + { + throw std::runtime_error("Assignments cannot be empty or contain only comment elements."); + } + + sParsedValue.ssKey = ssFinalKey; + refTarget.get().mapTable[ssFinalKey] = sParsedValue; + } + + /** + * @brief Resolves scalar elements, multi-line inline dictionaries, and arrays + * @return The parsed node. + */ + SNode ParseValueNode() + { + SkipWhitespaceAndComments(); + char c = Peek(); + SNode sValue; + + if (c == '"' || c == '\'') + { + sValue.eType = ENodeType::node_value; + sValue.ssValueRaw = ParseStringNode(); + } + else if (c == '{') + { + sValue.eType = ENodeType::node_table; + Consume(); // Consume starting '{' + while (!IsEof()) + { + SkipWhitespaceAndComments(); + if (Peek() == '}') + { + Consume(); + break; + } + ParseKeyValue(sValue); // Can append safely during the parsing lifecycle phase + SkipWhitespaceAndComments(); + if (Peek() == ',') + { + Consume(); + } + else if (Peek() == '}') + { + Consume(); + break; + } + } + // CRITICAL FIX: Lock the inline structure to prevent outside modifications + // ONLY after it has completely finished parsing and its braces are closed! + sValue.bIsInline = true; + } + else if (c == '[') + { + sValue.eType = ENodeType::node_array; + sValue.bIsExplicitlyHeadered = false; // Literal array token + Consume(); + while (!IsEof()) + { + SkipWhitespaceAndComments(); + if (Peek() == ']') + { + Consume(); + break; + } + sValue.vecArray.push_back(ParseValueNode()); + SkipWhitespaceAndComments(); + if (Peek() == ',') + { + Consume(); + } + else if (Peek() == ']') + { + Consume(); + break; + } + } + } + else + { + sValue.eType = ENodeType::node_value; + std::string strRaw = ""; + while (!IsEof() && Peek() != '\n' && Peek() != '\r' && Peek() != ',' && Peek() != '}' && Peek() != ']' + && Peek() != '#') + { + char rc = Consume(); + if (rc != ' ' && rc != '\t') + strRaw += rc; + } + sValue.ssValueRaw = strRaw; + } + return sValue; + } + }; +} // namespace sdv::toml::simple_parser + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif diff --git a/export/support/toml.h b/export/support/toml.h index 659ad17..c00b3eb 100644 --- a/export/support/toml.h +++ b/export/support/toml.h @@ -11,12 +11,14 @@ * Erik Verhoeven - initial API and implementation ********************************************************************************/ -#ifndef SDV_CONFIG_H -#define SDV_CONFIG_H +#ifndef SDV_TOML_H +#define SDV_TOML_H +#include #include "../interfaces/toml.h" #include "interface_ptr.h" #include "local_service_access.h" +#include "interface_ptr.h" namespace sdv::toml { @@ -82,6 +84,18 @@ namespace sdv::toml */ ENodeType GetType() const; + /** + * @brief Get the index of this node within the parent collection. + * @return The index of the node within the parent collection node or npos when no parent is available. + */ + uint32_t GetIndex() const; + + /** + * @brief Is the node defined as inline node? + * @return The inline flag. + */ + bool IsInline() const; + /** * @brief Return any associated comment text for this node. * @return The node comment or an empty string when there is not comment for this node. @@ -132,6 +146,12 @@ namespace sdv::toml */ virtual void Clear(); + /** + * @brief Format the node automatically, remove redundant whitespace. + * @param[in] bRemoveComments When set, the comments are removed from the node. + */ + void AutomaticFormat(bool bRemoveComments); + /** * @brief Get the TOML string from this node including all children. * @return The TOML string. @@ -228,23 +248,29 @@ namespace sdv::toml */ CNode GetDirect(const sdv::u8string& rssNode) const; + /** + * @brief Return the node name for a node in the collection. If the collection is an array, provides the index between + * square brackets. + * @param[in] nIndex Index to return the node name for. + * @return Name of the node or an empty string if the index is larger than the amount of nodes in the collection. + */ + std::string GetNodeNameByIndex(size_t nIndex) const; + /** * @brief Insert a value node before the provided position. - * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before - * standard nodes. - * @param[in] nIndex The index before which to insert the node. Can be larger than the count value as well as - * sdv::toml::npos when adding the node at the end. + * @remarks In TOML, inline nodes are located before standard nodes. Since values are presented as inline node, they + * will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] rssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. * @param[in] rssName Reference to the name of the new value node. If this collection is an array, the name is ignored. * Otherwise the name must be unique within this collection. * @param[in] ranyValue The value to assign to the node. The value also determines the type of value node. * @return Returns the node when successfully inserted or an empty node when not. */ - CNode InsertValue(size_t nIndex, const std::string& rssName, const sdv::any_t& ranyValue); + CNode InsertValue(const std::string& rssInsertBefore, const std::string& rssName, const sdv::any_t& ranyValue); /** * @brief Add a value node to the collection. - * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before - * standard nodes. * @param[in] rssName Reference to the name of the new array collection node. If this collection is an array, the name is * ignored. Otherwise the name must be unique within this collection. * @param[in] ranyValue The value to assign to the node. The value also determines the type of value node. @@ -254,20 +280,18 @@ namespace sdv::toml /** * @brief Insert an array collection before the provided position. - * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before - * standard nodes. - * @param[in] nIndex The index before which to insert the node. Can be larger than the count value as well as - * sdv::toml::npos when adding the node at the end. + * @remarks In TOML, inline nodes are located before standard nodes. Since arrays are presented as inline node, they + * will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] rssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. * @param[in] rssName Reference to the name of the new value node. If this collection is an array, the name is ignored. * Otherwise the name must be unique within this collection. * @return Returns the collection node when successfully inserted or an empty node when not. */ - CNodeCollection InsertArray(size_t nIndex, const std::string& rssName); + CNodeCollection InsertArray(const std::string& rssInsertBefore, const std::string& rssName); /** * @brief Add an array collection to this collection. - * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before - * standard nodes. * @param[in] rssName Reference to the name of the new array collection node. If this collection is an array, the name is * ignored. Otherwise the name must be unique within this collection. * @return Returns the collection node when successfully added or an empty node when not. @@ -276,22 +300,20 @@ namespace sdv::toml /** * @brief Insert a table collection before the provided position. - * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before - * standard nodes. - * @param[in] nIndex The index before which to insert the node. Can be larger than the count value as well as - * sdv::toml::npos when adding the node at the end. + * @remarks In TOML, inline nodes are located before standard nodes. Tables can be inserted as inline node, in which + * case they will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] rssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. * @param[in] rssName Reference to the name of the new table collection node. If this collection is an array, the name is * ignored. Otherwise the name must be unique within this collection. * @param[in] bFavorInline When set, the node will be added as inline collection node. When not, the node will be inserted * as inline collection if the this collection is also inline, as standard when not. * @return Returns the collection node when successfully inserted or an empty node when not. */ - CNodeCollection InsertTable(size_t nIndex, const std::string& rssName, bool bFavorInline = false); + CNodeCollection InsertTable(const std::string& rssInsertBefore, const std::string& rssName, bool bFavorInline = false); /** * @brief Add a table collection to this collection. - * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before - * standard nodes. * @param[in] rssName Reference to the name of the new table collection node. If this collection is an array, the name is * ignored. Otherwise the name must be unique within this collection. * @param[in] bFavorInline When set, the node will be added as inline collection node. When not, the node will be inserted @@ -302,12 +324,12 @@ namespace sdv::toml /** * @brief Insert a table array collection before the provided position. - * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before - * standard nodes. * @remarks A table array is an array with table inside. Inserting a table array node can also be done by creating an array, * if not existing already, and adding a table to the array. - * @param[in] nIndex The index before which to insert the node. Can be larger than the count value as well as - * sdv::toml::npos when adding the node at the end. + * @remarks In TOML, inline nodes are located before standard nodes. Table arrays can be inserted as inline node, in + * which case they will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] rssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. * @param[in] rssName Reference to the name of the new table array node. If this collection is an array, the name is * ignored. Otherwise the name must be unique within this collection. * @param[in] bFavorInline When set, the node will be added as inline collection node. When not, the node will be inserted @@ -315,12 +337,10 @@ namespace sdv::toml * @return Returns the collection node when successfully inserted or an empty node when not. The collection node represents * a table collection. */ - CNodeCollection InsertTableArray(size_t nIndex, const std::string& rssName, bool bFavorInline = false); + CNodeCollection InsertTableArray(const std::string& rssInsertBefore, const std::string& rssName, bool bFavorInline = false); /** * @brief Add a table array collection to this collection. - * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before - * standard nodes. * @remarks A table array is an array with table inside. Inserting a table array node can also be done by creating an array, * if not existing already, and adding a table to the array. * @param[in] rssName Reference to the name of the new table array node. If this collection is an array, the name is @@ -335,10 +355,10 @@ namespace sdv::toml /** * @brief Insert a TOML string to the collection. All nodes specified in the TOML will be added in the collection except * when the nodes already exist. Comment and whitespace are preserved when possible. - * @remarks The actual position depends on the type of node and the order the nodes are stored. Inline nodes come before - * standard nodes. - * @param[in] nIndex The index before which to insert the node. Can be larger than the count value as well as - * sdv::toml::npos when adding the node at the end. + * @remarks In TOML, inline nodes are located before standard nodes. Dependable on the nodes defined in the TOML they + * might be transferred to inline or they might be inserted at a different location. + * @param[in] rssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. * @param[in] rssTOML Reference to the TOML string containing the nodes. The TOML string can be empty, which is not an * error. If required the TOML nodes are converted to inline nodes. * @param[in] bAllowPartial When set, duplicate nodes (already present in this collection) will be ignored and do not @@ -346,7 +366,7 @@ namespace sdv::toml * @return Returns 1 if the complete TOMl could be inserted, 0 if no TOML could be inserted or -1 when the TOML could be * partially inserted. */ - int InsertTOML(size_t nIndex, const std::string& rssTOML, bool bAllowPartial = false); + int InsertTOML(const std::string& rssInsertBefore, const std::string& rssTOML, bool bAllowPartial = false); /** * @brief Add a TOML string to this collection. All nodes specified in the TOML will be added in the collection except @@ -372,16 +392,11 @@ namespace sdv::toml class CTOMLParser : public CNodeCollection { public: - /** - * @brief Default constructor. - */ - CTOMLParser() = default; - /** * @brief Constructor providing automatic processing. * @param[in] rssConfig Reference to the configuration. */ - CTOMLParser(const std::string& rssConfig); + CTOMLParser(const std::string& rssConfig = ""); /** * @brief Process a configuration. This will clear any previous configuration. @@ -414,6 +429,52 @@ namespace sdv::toml ITOMLParser* m_pParser = nullptr; ///< Pointer to the parser interface. }; + /** + * @brief Compare flags + */ + enum class ECompareFlags : uint32_t + { + compare_ignore_whitespace = 1, ///< Compare, but ignore whitespace + compare_ignore_comments = 2, ///< Compare, but ignore comments + compare_ignore_inline = 8, ///< Compare, but ignore inline or explicit + compare_ignore_all = 255, ///< Compare with all ignore flags + }; + + /** + * @brief Compare result + */ + enum class ECompareResult : int32_t + { + compare_identical = 0, ///< The comparison resulted into identical TOML strings + compare_different = 1, ///< The comparison resulted into different TOML strings + compare_error = -1 ///< The comparison could not be done due to a failure + }; + + /// Internal namespace + namespace internal + { + /** + * @brief Compare the content of the node with the content of another node. + * @attention This function changes the nodes dependable on the provided flags. + * @param[in] rnode1 First node interface to compare with the second node. + * @param[in] rnode2 Second node interface to compare with the first node. + * @param[in] uiCompareFlags Zero or more flags from ECompareFlags. + * @return The comparison result. + */ + ECompareResult CompareNodes(CNode& rnode1, CNode& rnode2, + uint32_t uiCompareFlags = static_cast(ECompareFlags::compare_ignore_all)); + } // namespace internal; + + /** + * @brief Compare the content of TOML string with the content of another TOML string. + * @param[in] rssToml1 Reference to the first TOML string to compare with the second TOML string. + * @param[in] rssToml2 Reference to the second TOML string to compare with the first TOML string. + * @param[in] uiCompareFlags Zero or more flags from ECompareFlags. + * @return The comparison result. + */ + ECompareResult Compare(const std::string& rssToml1, const std::string& rssToml2, + uint32_t uiCompareFlags = static_cast(ECompareFlags::compare_ignore_all)); + inline CNode::CNode(const TInterfaceAccessPtr& rptrNode) { m_pNodeInfo = rptrNode.GetInterface(); @@ -461,6 +522,16 @@ namespace sdv::toml return m_pNodeInfo ? m_pNodeInfo->GetType() : ENodeType::node_invalid; } + inline uint32_t CNode::GetIndex() const + { + return m_pNodeInfo ? m_pNodeInfo->GetIndex() : npos; + } + + inline bool CNode::IsInline() const + { + return m_pNodeInfo ? m_pNodeInfo->IsInline() : true; + } + inline std::string CNode::GetComment() const { if (!m_pNodeInfo) return {}; @@ -527,7 +598,9 @@ namespace sdv::toml { INodeUpdate* pNodeUpdate = m_ptrNode.GetInterface(); if (!pNodeUpdate) return false; - return pNodeUpdate->DeleteNode(); + bool bRet = pNodeUpdate->DeleteNode(); + Clear(); // Not valid any more. + return bRet; } inline void CNode::Clear() @@ -536,6 +609,11 @@ namespace sdv::toml m_pNodeInfo = nullptr; } + inline void CNode::AutomaticFormat(bool bRemoveComments) + { + if (m_pNodeInfo) m_pNodeInfo->AutomaticFormat(bRemoveComments); + } + inline sdv::u8string CNode::GetTOML() const { return m_pNodeInfo ? m_pNodeInfo->GetTOML() : sdv::u8string(); @@ -605,76 +683,86 @@ namespace sdv::toml return m_pCollection ? CNode(m_pCollection->GetNodeDirect(rssNode)) : CNode(); } - inline CNode CNodeCollection::InsertValue(size_t nIndex, const std::string& rssName, const sdv::any_t& ranyValue) + inline std::string CNodeCollection::GetNodeNameByIndex(size_t nIndex) const + { + if (!m_pCollection) return {}; + if (nIndex >= m_pCollection->GetCount()) return {}; + if (GetType() == ENodeType::node_array) return "[" + std::to_string(nIndex) + "]"; + TInterfaceAccessPtr ptrNode = m_pCollection->GetNode(static_cast(nIndex)); + const auto* pAccess = ptrNode.GetInterface(); + if (!pAccess) return {}; + return pAccess->GetName(); + } + + inline CNode CNodeCollection::InsertValue(const std::string& rssInsertBefore, const std::string& rssName, + const sdv::any_t& ranyValue) { INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); if (!pInsert) return {}; - return CNode(pInsert->InsertValue(static_cast(nIndex), rssName, ranyValue)); + return CNode(pInsert->InsertValue(rssInsertBefore, rssName, ranyValue)); } inline CNode CNodeCollection::AddValue(const std::string& rssName, const sdv::any_t& ranyValue) { INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); if (!pInsert) return {}; - return CNode(pInsert->InsertValue(npos, rssName, ranyValue)); + return CNode(pInsert->InsertValue("", rssName, ranyValue)); } - inline CNodeCollection CNodeCollection::InsertArray(size_t nIndex, const std::string& rssName) + inline CNodeCollection CNodeCollection::InsertArray(const std::string& rssInsertBefore, const std::string& rssName) { INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); if (!pInsert) return {}; - return CNodeCollection(pInsert->InsertArray(static_cast(nIndex), rssName)); + return CNodeCollection(pInsert->InsertArray(rssInsertBefore, rssName)); } inline CNodeCollection CNodeCollection::AddArray(const std::string& rssName) { INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); if (!pInsert) return {}; - return CNodeCollection(pInsert->InsertArray(npos, rssName)); + return CNodeCollection(pInsert->InsertArray("", rssName)); } - inline CNodeCollection CNodeCollection::InsertTable(size_t nIndex, const std::string& rssName, bool bFavorInline /*= false*/) + inline CNodeCollection CNodeCollection::InsertTable(const std::string& rssInsertBefore, const std::string& rssName, + bool bFavorInline /*= false*/) { INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); if (!pInsert) return {}; - return CNodeCollection(pInsert->InsertTable(static_cast(nIndex), rssName, - bFavorInline ? INodeCollectionInsert::EInsertPreference::prefer_inline : - INodeCollectionInsert::EInsertPreference::prefer_standard)); + return CNodeCollection(pInsert->InsertTable(rssInsertBefore, rssName, bFavorInline ? EInsertPreference::prefer_inline : + EInsertPreference::prefer_standard)); } inline CNodeCollection CNodeCollection::AddTable(const std::string& rssName, bool bFavorInline /*= false*/) { INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); if (!pInsert) return {}; - return CNodeCollection(pInsert->InsertTable(npos, rssName, - bFavorInline ? INodeCollectionInsert::EInsertPreference::prefer_inline : - INodeCollectionInsert::EInsertPreference::prefer_standard)); + return CNodeCollection(pInsert->InsertTable("", rssName, bFavorInline ? EInsertPreference::prefer_inline : + EInsertPreference::prefer_standard)); } - inline CNodeCollection CNodeCollection::InsertTableArray(size_t nIndex, const std::string& rssName, + inline CNodeCollection CNodeCollection::InsertTableArray(const std::string& rssInsertBefore, const std::string& rssName, bool bFavorInline /*= false*/) { INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); if (!pInsert) return {}; - return CNodeCollection(pInsert->InsertTableArray(static_cast(nIndex), rssName, - bFavorInline ? INodeCollectionInsert::EInsertPreference::prefer_inline : - INodeCollectionInsert::EInsertPreference::prefer_standard)); + return CNodeCollection(pInsert->InsertTableArray(rssInsertBefore, rssName, bFavorInline ? EInsertPreference::prefer_inline : + EInsertPreference::prefer_standard)); } inline CNodeCollection CNodeCollection::AddTableArray(const std::string& rssName, bool bFavorInline /*= false*/) { INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); if (!pInsert) return {}; - return CNodeCollection(pInsert->InsertTableArray(npos, rssName, - bFavorInline ? INodeCollectionInsert::EInsertPreference::prefer_inline : - INodeCollectionInsert::EInsertPreference::prefer_standard)); + return CNodeCollection(pInsert->InsertTableArray("", rssName, bFavorInline ? EInsertPreference::prefer_inline : + EInsertPreference::prefer_standard)); } - inline int CNodeCollection::InsertTOML(size_t nIndex, const std::string& rssTOML, bool bAllowPartial /*= false*/) + inline int CNodeCollection::InsertTOML(const std::string& rssInsertBefore, const std::string& rssTOML, + bool bAllowPartial /*= false*/) { INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); if (!pInsert) return 0; - INodeCollectionInsert::EInsertResult eRet = pInsert->InsertTOML(static_cast(nIndex), rssTOML, !bAllowPartial); + INodeCollectionInsert::EInsertResult eRet = pInsert->InsertTOML(rssInsertBefore, rssTOML, !bAllowPartial); switch (eRet) { case INodeCollectionInsert::EInsertResult::insert_success: @@ -691,7 +779,7 @@ namespace sdv::toml { INodeCollectionInsert* pInsert = m_ptrNode.GetInterface(); if (!pInsert) return 0; - INodeCollectionInsert::EInsertResult eRet = pInsert->InsertTOML(npos, rssTOML, !bAllowPartial); + INodeCollectionInsert::EInsertResult eRet = pInsert->InsertTOML("", rssTOML, !bAllowPartial); switch (eRet) { case INodeCollectionInsert::EInsertResult::insert_success: @@ -746,6 +834,59 @@ namespace sdv::toml m_ptrParserUtil.Clear(); } + namespace internal + { + inline ECompareResult CompareNodes(CNode& rnode1, CNode& rnode2, + uint32_t uiCompareFlags /*= static_cast(ECompareFlags::compare_ignore_all)*/) + { + if (!rnode1 || !rnode2) return ECompareResult::compare_error; + + // Format the nodes when ignore whitespace and/or comments + bool bIgnoreComments = uiCompareFlags & static_cast(ECompareFlags::compare_ignore_comments); + bool bIgnoreWhitespace = bIgnoreComments || + (uiCompareFlags & static_cast(ECompareFlags::compare_ignore_whitespace)); + if (bIgnoreWhitespace) + { + INodeInfo* pNodeInfo1 = rnode1.GetInterface().GetInterface(); + if (!pNodeInfo1) return ECompareResult::compare_error; + pNodeInfo1->AutomaticFormat(bIgnoreComments); + INodeInfo* pNodeInfo2 = rnode2.GetInterface().GetInterface(); + if (!pNodeInfo2) return ECompareResult::compare_error; + pNodeInfo2->AutomaticFormat(bIgnoreComments); + } + + // Convert to standard if ignoring inline + INodeCollectionConvert* pConvert1 = rnode1.GetInterface().GetInterface(); + INodeCollectionConvert* pConvert2 = rnode2.GetInterface().GetInterface(); + if (pConvert1 && pConvert2 && uiCompareFlags & static_cast(ECompareFlags::compare_ignore_inline)) + { + // Making inline nodes as standard, might only change the upper node. + // Making standard nodes inline, will have all child nodes be made inline as well, because inline nodes can only + // have inline nodes. + pConvert1->MakeInline(); + pConvert2->MakeInline(); + } + + // Generate the TOMLs and compare + return rnode1.GetTOML() == rnode2.GetTOML() ? ECompareResult::compare_identical : ECompareResult::compare_different; + } + } // namespace internal + + inline ECompareResult Compare(const std::string& rssToml1, const std::string& rssToml2, + uint32_t uiCompareFlags /*= static_cast(ECompareFlags::compare_ignore_all)*/) + { + try + { + CTOMLParser parser1(rssToml1); + CTOMLParser parser2(rssToml2); + return internal::CompareNodes(parser1, parser2, uiCompareFlags); + } + catch (const sdv::toml::XTOMLParseException&) + { + return ECompareResult::compare_error; + } + } + } -#endif // !defined SDV_CONFIG_H \ No newline at end of file +#endif // !defined SDV_TOML_H \ No newline at end of file diff --git a/funding_logos/CHIPS_logo_RGB_full_colour.png b/funding_logos/CHIPS_logo_RGB_full_colour.png new file mode 100644 index 0000000..3b76409 Binary files /dev/null and b/funding_logos/CHIPS_logo_RGB_full_colour.png differ diff --git a/funding_logos/EN_Co-fundedbytheEU_RGB_POS.png b/funding_logos/EN_Co-fundedbytheEU_RGB_POS.png new file mode 100644 index 0000000..2a494fb Binary files /dev/null and b/funding_logos/EN_Co-fundedbytheEU_RGB_POS.png differ diff --git a/funding_logos/HAL4SDV_logo_on_white.jpg b/funding_logos/HAL4SDV_logo_on_white.jpg new file mode 100644 index 0000000..66fb8d1 Binary files /dev/null and b/funding_logos/HAL4SDV_logo_on_white.jpg differ diff --git a/funding_logos/Shift2sdv_logo_tagline_blue.svg b/funding_logos/Shift2sdv_logo_tagline_blue.svg new file mode 100644 index 0000000..1d9f53a --- /dev/null +++ b/funding_logos/Shift2sdv_logo_tagline_blue.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/global/dbcparser/dbcparser.cpp b/global/dbcparser/dbcparser.cpp index 80410b6..f2ccf66 100644 --- a/global/dbcparser/dbcparser.cpp +++ b/global/dbcparser/dbcparser.cpp @@ -451,7 +451,7 @@ namespace dbc if (!bNoDefaultDef) { // Parse the standard attribute definitions - CDbcSource source(std::string(R"code( + CDbcSource source(std::string(R"dbc( BA_DEF_ BO_ "VFrameFormat" ENUM "StandardCAN","ExtendedCAN","StandardFD","ExtendedFD"; BA_DEF_ BO_ "GenMsgSendType" ENUM "cyclic","triggered","cyclicIfActive","cyclicAndTriggered","cyclicIfActiveAndTriggered","none"; BA_DEF_ BO_ "GenMsgCycleTime" INT 0 10000; @@ -472,7 +472,7 @@ BA_DEF_DEF_ "GenSigDelayTime" 0; BA_DEF_DEF_ "GenSigCycleTime" 0; BA_DEF_DEF_ "GenSigStartDelayTime" 0; BA_DEF_DEF_ "GenSigStartValue" 0; - )code")); + )dbc")); Parse(source); } } diff --git a/global/debug_log.h b/global/debug_log.h index 24cfc50..549b80f 100644 --- a/global/debug_log.h +++ b/global/debug_log.h @@ -176,9 +176,9 @@ namespace debug */ struct SLogMsg { - std::thread::id id; ///< Thread ID - size_t nDepth; ///< Depth within the calls - std::string ssMsg; ///< Message to log + std::thread::id id; ///< Thread ID + size_t nDepth; ///< Depth within the calls + std::string ssMsg; ///< Message to log }; /** @@ -271,7 +271,7 @@ namespace debug std::filesystem::path m_pathLogFile; ///< Path to the log file. #if DECOUPLED_DEBUG_LOG != 0 - std::mutex m_mtxLogger; ///< Protect against multiple log entries at the same time. + std::mutex m_mtxLogger; ///< Protect against multiple log entries at the same time. std::thread m_threadLogger; ///< Logger thread std::atomic_bool m_bShutdown = false; ///< When set, terminate the logging thread std::queue m_queueLogger; ///< Queue with messages to log. diff --git a/global/scheduler/scheduler.h b/global/scheduler/scheduler.h index 28de486..c702dcd 100644 --- a/global/scheduler/scheduler.h +++ b/global/scheduler/scheduler.h @@ -11,8 +11,8 @@ * Erik Verhoeven - initial API and implementation ********************************************************************************/ -#ifndef THREAD_POOL_H -#define THREAD_POOL_H +#ifndef SCHEDULER_H +#define SCHEDULER_H #include #include @@ -27,33 +27,38 @@ #include "../flags.h" /** - * @brief Job scheduler that uses a dynamic threadpool to schedule the task. + * @brief Schedule flags to influence the scheduling. */ +enum class EScheduleFlags +{ + normal = 0x0, ///< If thread level threshold has been reached, queue the call at the end of the queue. + priority = 0x1, ///< If thread level threshold has been reached, insert the call at the begin of the queue. + no_queue = 0x2, ///< If thread level threshold has been reached, fail the schedule call. +}; + +/** + * @brief Task scheduler that uses a dynamic threadpool to schedule the task. + * @tparam TThread Type of thread class to use for scheduling; must be derived from std::thread. + * @tparam nMinIdle The amount of idle threads that should stay present if there is nothing to process at the moment. + * @tparam nMaxBusy The maximum amount of threads that is used for processing. + */ +template class CTaskScheduler { public: + // The thread class must be derived from std::thread. + static_assert(std::is_base_of_v); + /** * @brief Constructor - * @param[in] nMinIdle The amount of idle threads that should stay present if there is nothing to process at the moment. - * @param[in] nMaxBusy The maximum amount of threads that is used for processing. */ - CTaskScheduler(size_t nMinIdle = 4, size_t nMaxBusy = 32); + CTaskScheduler(); /** * @brief Destructor */ ~CTaskScheduler(); - /** - * @brief Schedule flags to influence the scheduling. - */ - enum class EScheduleFlags - { - normal = 0x0, ///< If thread level threshold has been reached, queue the call at the end of the queue. - priority = 0x1, ///< If thread level threshold has been reached, insert the call at the begin of the queue. - no_queue = 0x2, ///< If thread level threshold has been reached, fail the schedule call. - }; - /** * @brief Schedule the asynchronous execution of the task. * @details If an idle thread is available, the thread will execute the task. If no thread is available and the maximum thread @@ -125,7 +130,7 @@ private: */ void ThreadFunc(); - std::thread m_thread; ///< The thread that executes the tasks. + TThread m_thread; ///< The thread that executes the tasks. std::atomic_bool m_bShutdown = false; ///< Set when the thread should terminate. std::atomic_bool m_bStarted = false; ///< Set when the thread has started. std::function m_fnTask; ///< The task to execute (will be updated with new tasks before execution). @@ -143,15 +148,18 @@ private: * @param[in] itThread The thread to use for the execution. * @param[in] fnTask The task to execute. */ - void Execute(std::list>::iterator itThread, std::function fnTask); + void Execute(typename std::list>::iterator itThread, std::function fnTask); size_t m_nMinIdle = 4; ///< The minimal required amount of idle threads size_t m_nMaxBusy = 32; ///< The maximum allowed amount of busy threads size_t m_nMaxThreads = 0; ///< The maximum amount threads at the same time. mutable std::mutex m_mtxQueueAccess; ///< Sync access to queue, list and double-ended-queue. - std::queue>::iterator> m_queueIdleThreads; ///< Idle thread queue. - std::list> m_lstThreads; ///< List with all threads. + std::queue>::iterator> m_queueIdleThreads; ///< Idle thread queue. + std::list> m_lstThreads; ///< List with all threads. std::deque> m_dequeTasks; ///< Double ended task queue. }; -#endif // !defined THREAD_POOL_H \ No newline at end of file +// Include the implementation +#include "scheduler.inl" + +#endif // !defined SCHEDULER_H \ No newline at end of file diff --git a/global/scheduler/scheduler.cpp b/global/scheduler/scheduler.inl similarity index 74% rename from global/scheduler/scheduler.cpp rename to global/scheduler/scheduler.inl index 274d688..3b11a89 100644 --- a/global/scheduler/scheduler.cpp +++ b/global/scheduler/scheduler.inl @@ -11,10 +11,17 @@ * Erik Verhoeven - initial API and implementation ********************************************************************************/ +#ifndef SCHEDULER_H #include "scheduler.h" +#endif + +#ifndef SCHEDULER_INL +#define SCHEDULER_INL + #include -CTaskScheduler::CTaskScheduler(size_t nMinIdle /*= 4*/, size_t nMaxBusy /*= 32*/) : +template +inline CTaskScheduler::CTaskScheduler() : m_nMinIdle(nMinIdle), m_nMaxBusy(nMaxBusy) { // Check min/max values @@ -30,17 +37,20 @@ CTaskScheduler::CTaskScheduler(size_t nMinIdle /*= 4*/, size_t nMaxBusy /*= 32*/ m_nMaxThreads = m_lstThreads.size(); } -CTaskScheduler::~CTaskScheduler() +template +inline CTaskScheduler::~CTaskScheduler() { WaitForExecution(); } -bool CTaskScheduler::Schedule(std::function fnTask, hlpr::flags flags /*= 0*/) +template +inline bool CTaskScheduler::Schedule(std::function fnTask, + hlpr::flags flags /*= 0*/) { std::unique_lock lock(m_mtxQueueAccess); // Get a thread - either from the idle queue or new. - std::list>::iterator itThread = m_lstThreads.end(); + typename std::list>::iterator itThread = m_lstThreads.end(); if (m_queueIdleThreads.empty()) { // No thread is available. Allowed to make one? @@ -77,7 +87,8 @@ bool CTaskScheduler::Schedule(std::function fnTask, hlpr::flags +inline void CTaskScheduler::WaitForExecution() { std::unique_lock lock(m_mtxQueueAccess); @@ -111,31 +122,37 @@ void CTaskScheduler::WaitForExecution() m_nMinIdle = nMinIdleTemp; } -size_t CTaskScheduler::GetThreadCount() const +template +inline size_t CTaskScheduler::GetThreadCount() const { std::unique_lock lock(m_mtxQueueAccess); return m_lstThreads.size(); } -size_t CTaskScheduler::GetMaxThreadCount() const +template +inline size_t CTaskScheduler::GetMaxThreadCount() const { std::unique_lock lock(m_mtxQueueAccess); return m_nMaxThreads; } -size_t CTaskScheduler::GetBusyThreadCount() const +template +inline size_t CTaskScheduler::GetBusyThreadCount() const { std::unique_lock lock(m_mtxQueueAccess); return m_lstThreads.size() - m_queueIdleThreads.size(); } -size_t CTaskScheduler::GetIdleThreadCount() const +template +inline size_t CTaskScheduler::GetIdleThreadCount() const { std::unique_lock lock(m_mtxQueueAccess); return m_queueIdleThreads.size(); } -void CTaskScheduler::Execute(std::list>::iterator itThread, std::function fnTask) +template +inline void CTaskScheduler::Execute(typename std::list>::iterator itThread, + std::function fnTask) { std::shared_ptr& rptrThread = *itThread; @@ -176,16 +193,18 @@ void CTaskScheduler::Execute(std::list>::iterator itThr }); } -CTaskScheduler::CThread::CThread() +template +inline CTaskScheduler::CThread::CThread() { // Wait for the start std::unique_lock lockStart(m_mtxSyncStart); - m_thread = std::thread(&CThread::ThreadFunc, this); + m_thread = TThread(&CThread::ThreadFunc, this); while (!m_bStarted) m_cvStarted.wait_for(lockStart, std::chrono::milliseconds(10)); } -CTaskScheduler::CThread::~CThread() +template +inline CTaskScheduler::CThread::~CThread() { // Lock to prevent execution still to take place. Then shutdown. std::unique_lock lock(m_mtxSyncExecute); @@ -196,7 +215,8 @@ CTaskScheduler::CThread::~CThread() m_thread.join(); } -void CTaskScheduler::CThread::Execute(std::function fnTask) +template +inline void CTaskScheduler::CThread::Execute(std::function fnTask) { // Assign the task and notify for execution. std::unique_lock lock(m_mtxSyncExecute); @@ -205,7 +225,8 @@ void CTaskScheduler::CThread::Execute(std::function fnTask) lock.unlock(); } -void CTaskScheduler::CThread::ThreadFunc() +template +inline void CTaskScheduler::CThread::ThreadFunc() { // Thread has started (needed, since the condition variable doesn't keep its state). m_bStarted = true; @@ -232,3 +253,5 @@ void CTaskScheduler::CThread::ThreadFunc() m_fnTask = {}; } } + +#endif // !defined SCHEDULER_INL \ No newline at end of file diff --git a/global/tracefifo/trace_fifo.h b/global/tracefifo/trace_fifo.h index f664a06..5a4a4b8 100644 --- a/global/tracefifo/trace_fifo.h +++ b/global/tracefifo/trace_fifo.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include /** * @brief trace fifo open flags diff --git a/global/unique_id.h b/global/unique_id.h new file mode 100644 index 0000000..ea80a33 --- /dev/null +++ b/global/unique_id.h @@ -0,0 +1,114 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#ifndef UNIQUE_ID_H +#define UNIQUE_ID_H + +#include +#include +#include +#include + +/** + * @brief Create a unique ID using the random number generator. + * @tparam TType Type of the variable that represents the ID. + * @tparam nDepth The depth of the ID generation in bits. + */ +template +class CUniqueID +{ +public: + static_assert(nDepth <= sizeof(TType) * 8); + static_assert(nDepth > 0); + + /** + * @brief Constructor + */ + CUniqueID() + { + std::srand(static_cast(std::time(nullptr))); + } + + /** + * @brief Generate a unique ID. + * @return The optional result with the ID if successful or 0 when not successful. + */ + TType Generate() + { + // Create a number with the supplied bit amount. + auto fnCreate = [](size_t nMaxDepth) + { + TType tNumber{}; + + // Create a new number + size_t nBitsFilled = 0; + while (nBitsFilled < nMaxDepth) + { + size_t nBitsToShiftIn = std::min(nMaxDepth - nBitsFilled, m_nRandMaxBits); + tNumber = (tNumber << nBitsToShiftIn) | static_cast(static_cast(std::rand()) & ((1u << nBitsToShiftIn) - 1)); + nBitsFilled += nBitsToShiftIn; + } + return tNumber; + }; + + // Treat a depth of 48 bits and more separately + if constexpr (nDepth >= 48) + { + // Combination of the time in seconds (22 bits only - can hold 48 days). + return (static_cast(std::time(nullptr)) << (nDepth - 22)) | fnCreate(nDepth - 22); + } + else + { + // Maximum iterations is 10000 for less than 24 bits and otherwise unlimited (value 0) + size_t nMaxIterations = nDepth < 24 ? 10000u : 0u; + + // Generate + size_t nCnt = 0; + while (!nMaxIterations || (++nCnt < nMaxIterations)) + { + TType tID = fnCreate(nDepth); + + // Check whether not 0 and not generated already + if (tID && (m_setUsedNumbers.find(tID) == m_setUsedNumbers.end())) + { + m_setUsedNumbers.insert(tID); + return tID; + } + } + + // No ID found + return TType{}; + } + } + +private: + /** + * @brief Recursive constexpr function to find the number of bits needed to hold the number. + * @param uiNumber The number to check for. + * @return The amount of bits needed to hold the number. + */ + static constexpr size_t GetBitWidth(uint64_t uiNumber) + { + return (!uiNumber) ? 0u : 1u + GetBitWidth(uiNumber >> 1); + } + + /// The bits needed to hold the random number + static constexpr size_t m_nRandMaxBits = GetBitWidth(static_cast(RAND_MAX)); + + // Static assertion to verify at compile-time + static_assert(m_nRandMaxBits > 0, "RAND_MAX bits must be greater than 0"); + + std::set m_setUsedNumbers; ///< Numbers generated previously already (numbers < 48 bits only). +}; + +#endif // !defined UNIQUE_ID_H diff --git a/sdv_executables/error_msg.h b/sdv_executables/error_msg.h index e5f4667..99fb89d 100644 --- a/sdv_executables/error_msg.h +++ b/sdv_executables/error_msg.h @@ -60,12 +60,22 @@ MAKE_ERROR_MSG(-134, CONFIG_SERVICE_ACCESS_ERROR, "Configuration service is not MAKE_ERROR_MSG(-135, COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR, "Communication control service is not accessible.", "The communication control service service could not be accessed.") MAKE_ERROR_MSG(-136, APP_CONTROL_SERVICE_ACCESS_ERROR, "Application control service is not accessible.", "The application control service could not be accessed.") MAKE_ERROR_MSG(-170, CANNOT_FIND_OBJECT, "The object cannot be found.", "A search for an object with supplied name was not successful.") +MAKE_ERROR_MSG(-180, CANNOT_LOAD_SETTINGS, "The system settings cannot be loaded.", "For a specific instance, the system settings cannot be loaded.") +MAKE_ERROR_MSG(-180, CANNOT_SAVE_SETTINGS, "The system settings cannot be saved.", "For a specific instance, the system settings cannot be saved.") +MAKE_ERROR_MSG(-181, PROCESS_LISTENER_CONFIG_ERROR, "The listener configuration contains errors.", "The listener configuration contains errors and cannot be processed.") +MAKE_ERROR_MSG(-182, PROCESS_CONNECTION_CONFIG_ERROR, "The connection configuration contains errors.", "The connection configuration contains errors and cannot be processed.") +MAKE_ERROR_MSG(-183, ADD_LISTENER_ERROR, "Failed to add a listener to the system settings.", "The listener configuration could not be added to the system settings. This might be caused by a duplicate listener configuration.") +MAKE_ERROR_MSG(-184, REMOVE_LISTENER_ERROR, "Failed to remove a listener from the system settings.", "The listener configuration could not be removed from the system settings. This might be caused by a missing listener configuration.") +MAKE_ERROR_MSG(-185, SHOW_LISTENER_ERROR, "Failed to request the listener configuration from the system settings.", "The listener configuration could not be requested. This might be caused by a missing listener configuration.") +MAKE_ERROR_MSG(-186, ADD_CONNECTION_ERROR, "Failed to add a connection to the system settings.", "The connection configuration could not be added to the system settings. This might be caused by a duplicate connection configuration.") +MAKE_ERROR_MSG(-187, REMOVE_CONNECTION_ERROR, "Failed to remove a connection from the system settings.", "The connection configuration could not be removed from the system settings. This might be caused by a missing connection configuration.") +MAKE_ERROR_MSG(-188, SHOW_CONNECTION_ERROR, "Failed to request the connection configuration from the system settings.", "The connection configuration could not be requested. This might be caused by a missing connection configuration.") ////////// LOCAL APP CONTROL ERROR CODES //////////// MAKE_ERROR_MSG(-204, APP_CONTROL_STARTUP_ERROR, "Cannot start application control.", "Application control start was initiated, but returned an error.") -MAKE_ERROR_MSG(-205, APP_CONTROL_START_LOOP_FAIELD, "Starting the running loop failed.", "Failed to enter the processing loop for a server application.") +MAKE_ERROR_MSG(-205, APP_CONTROL_START_LOOP_FAILED, "Starting the running loop failed.", "Failed to enter the processing loop for a server application.") MAKE_ERROR_MSG(-206, APP_CONTROL_DUPLICATE_INSTANCE, "Another process with specified instance is already running.", "Another core process initiated the application control with the same instance. Only one core process per instance is allowed.") MAKE_ERROR_MSG(-210, APP_CONTROL_INVALID_ISOLATION_CONFIG, "An invalid isolation configuration was supplied.", "The application control configuration section contains an invalid isolation configuration.") MAKE_ERROR_MSG(-218, SETTING_FILE_VERSION_INVALID, "Settings file has an invalid version.", "Settings file contains an invalid/incompatible version number.") diff --git a/sdv_executables/sdv_control/CMakeLists.txt b/sdv_executables/sdv_control/CMakeLists.txt index 9be7b01..7a7e419 100644 --- a/sdv_executables/sdv_control/CMakeLists.txt +++ b/sdv_executables/sdv_control/CMakeLists.txt @@ -25,7 +25,7 @@ add_executable(sdv_control "startup_shutdown.h" "startup_shutdown.cpp" "context.h" - "print_table.h" "start_stop_service.cpp" "start_stop_service.h" "installation.h" "installation.cpp") + "print_table.h" "start_stop_service.cpp" "start_stop_service.h" "installation.h" "installation.cpp" "connection_config.h" "connection_config.cpp") target_link_libraries(sdv_control ${CMAKE_DL_LIBS}) diff --git a/sdv_executables/sdv_control/connection_config.cpp b/sdv_executables/sdv_control/connection_config.cpp new file mode 100644 index 0000000..e0cada3 --- /dev/null +++ b/sdv_executables/sdv_control/connection_config.cpp @@ -0,0 +1,471 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include "connection_config.h" +#include "../../global/cmdlnparser/cmdlnparser.h" +#include +#include +#include "../error_msg.h" +#include + +void ListenerHelp(const SContext& rsContext) +{ + // First argument should be "LISTENER" + if (rsContext.seqCmdLine.size() < 1) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: missing listener command..." << std::endl; + return; + } + if (iequals(rsContext.seqCmdLine[0], "LISTENER")) + { + CCommandLine::PrintHelpText(std::cout, + "Usage: sdv_control LISTENER ADD [options...]\n" + " sdv_control LISTENER REMOVE [options...]\n" + " sdv_control LISTENER SHOW [options...]\n\n" + "Configure the system to add (or update) or remove a listener. The command doesn't check for valid parameters. A " + "server restart is required for the new listener settings to take effect.\n\n" + "A list of actively configured listeners can be requested by executing \"sdv_control LIST LISTENERS\"\n\n" + "Note: if not explicitly added, a default listener will added to the list for the communication between isolated " + "applications and the core system.\n\n"); + return; + } + if (!rsContext.bSilent) + std::cerr << "ERROR: invalid listener command..." << std::endl; +} + +void ConnectionHelp(const SContext& rsContext) +{ + // First argument should be "CONNECTION" + if (rsContext.seqCmdLine.size() < 1) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: missing connection command..." << std::endl; + return; + } + if (iequals(rsContext.seqCmdLine[0], "CONNECTION")) + { + CCommandLine::PrintHelpText(std::cout, + "Usage: sdv_control CONNECTION ADD [options...]\n" + " sdv_control CONNECTION REMOVE [options...]\n" + " sdv_control CONNECTION SHOW [options...]\n\n" + "Configure the system to add (or update) or remove a connection. The command doesn't check for valid parameters. A " + "server restart is required for the new connection settings to take effect.\n\n" + "A list of actively configured connections can be requested by executing \"sdv_control LIST CONNECTIONS\"\n\n" + "Note: if not explicitly added, a default connetion will be added to the list for the communication between external " + "applications and the core system.\n\n" + "Options:\n" + " --insert_before With ADD, Insert the connection before an existing connection with the supplied name.\n\n"); + return; + } + if (!rsContext.bSilent) + std::cerr << "ERROR: invalid connection command..." << std::endl; +} + +int ConfigureListener(const SContext& rsContext) +{ + // First argument should be "CONNECTION" + if (rsContext.seqCmdLine.size() < 2) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listener command... " << std::endl; + return CMDLN_ARG_ERR; + } + if (!iequals(rsContext.seqCmdLine[0], "LISTENER")) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command: " << rsContext.seqCmdLine[0] << std::endl; + return CMDLN_ARG_ERR; + } + + // Which list command is requested + enum class ECommand { unknown, add, remove, show } eCommand = ECommand::unknown; + if (iequals(rsContext.seqCmdLine[1], "ADD")) eCommand = ECommand::add; + else if (iequals(rsContext.seqCmdLine[1], "REMOVE")) eCommand = ECommand::remove; + else if (iequals(rsContext.seqCmdLine[1], "SHOW")) eCommand = ECommand::show; + else + { + if (!rsContext.bSilent) + { + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid listener command." << std::endl; + ListenerHelp(rsContext); + } + return CMDLN_ARG_ERR; + } + + if (rsContext.seqCmdLine.size() < 3) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listener name... " << std::endl; + return CMDLN_ARG_ERR; + } + + // Get the name + std::string ssListener = rsContext.seqCmdLine[2]; + + switch (eCommand) + { + case ECommand::remove: + return RemoveListener(ssListener, rsContext); + case ECommand::show: + return ShowListenerConfig(ssListener, rsContext); + case ECommand::add: + default: + break; + } + + // For add, check for provider and parameters + if (rsContext.seqCmdLine.size() < 4) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listener provider... " << std::endl; + return CMDLN_ARG_ERR; + } + std::string ssProvider = rsContext.seqCmdLine[3]; + + sdv::toml::CTOMLParser parser; + parser.AddValue("Provider.Name", ssProvider); + + // Check for parameters + // Parameters have the format "param=value". It is possible that parameters have additional sub-tables. Then the format is: + // "table.param=value". + sdv::toml::CNodeCollection ipc_channel_table = parser.AddTable("IpcChannel"); + for (size_t nIndex = 4; nIndex < rsContext.seqCmdLine.size(); nIndex++) + { + std::string ssRawParam = rsContext.seqCmdLine[nIndex]; + + // Search for the equal sign + size_t nEqualPos = ssRawParam.find_first_of('='); + std::string ssParamName = ssRawParam.substr(0, nEqualPos); + while (!ssParamName.empty() && std::isspace(ssParamName.back())) + ssParamName.pop_back(); + if (ssParamName.empty() || nEqualPos == std::string::npos) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid listener parameter: " << ssRawParam << std::endl; + return CMDLN_ARG_ERR; + } + sdv::any_t anyValue; + size_t nValuePos = nEqualPos + 1; + while (nValuePos < ssRawParam.size() && std::isspace(ssRawParam[nValuePos])) + nValuePos++; + if (nValuePos < ssRawParam.size()) + { + std::string ssValue = ssRawParam.substr(nValuePos); + if (!ssValue.empty() && ssValue.find_first_not_of("-0123456789") == std::string::npos) + { + int64_t iValue = 0; + if (std::from_chars(ssValue.data(), ssValue.data() + ssValue.size(), iValue).ec == std::errc()) + anyValue = iValue; + else + anyValue = ssValue; + } + else + anyValue = ssValue; + + } + ipc_channel_table.AddValue(ssParamName, anyValue); + } + + return AddListener(ssListener, parser.GetTOML(), rsContext); +} + +int AddListener(const std::string& rssListener, const std::string& rssConfig, const SContext& rsContext) +{ + // Load the settings. + auto ptrAppService = sdv::core::GetObject("AppSettingsService"); + sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface(); + sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface(); + if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl; + return CANNOT_LOAD_SETTINGS; + } + + // Add the listener + if (!pAppConnections->AddListenerConfig(rssListener, rssConfig)) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << ADD_LISTENER_ERROR_MSG << std::endl; + return ADD_LISTENER_ERROR; + } + + // Save the settings. + if (!pAppSettingsPersist->SaveSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_SAVE_SETTINGS_MSG << std::endl; + return CANNOT_SAVE_SETTINGS; + } + + return NO_ERROR; +} + +int RemoveListener(const std::string& rssListener, const SContext& rsContext) +{ + // Load the settings. + auto ptrAppService = sdv::core::GetObject("AppSettingsService"); + sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface(); + sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface(); + if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl; + return CANNOT_LOAD_SETTINGS; + } + + // Remove the listener + if (!pAppConnections->RemoveListenerConfig(rssListener)) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << REMOVE_LISTENER_ERROR_MSG << std::endl; + return REMOVE_LISTENER_ERROR; + } + + // Save the settings. + if (!pAppSettingsPersist->SaveSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_SAVE_SETTINGS_MSG << std::endl; + return CANNOT_SAVE_SETTINGS; + } + + return NO_ERROR; +} + +int ShowListenerConfig(const std::string& rssListener, const SContext& rsContext) +{ + // Load the settings. + auto ptrAppService = sdv::core::GetObject("AppSettingsService"); + sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface(); + sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface(); + if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl; + return CANNOT_LOAD_SETTINGS; + } + + // Remove the listener + std::string ssConfig = pAppConnections->GetListenerConfig(rssListener); + if (ssConfig.empty()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << SHOW_LISTENER_ERROR_MSG << std::endl; + return SHOW_LISTENER_ERROR; + } + + std::cout << ssConfig << std::endl; + + return NO_ERROR; +} + +int ConfigureConnection(const SContext& rsContext) +{ + // First argument should be "CONNECTION" + if (rsContext.seqCmdLine.size() < 2) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing connection command... " << std::endl; + return CMDLN_ARG_ERR; + } + if (!iequals(rsContext.seqCmdLine[0], "CONNECTION")) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command: " << rsContext.seqCmdLine[0] << std::endl; + return CMDLN_ARG_ERR; + } + + // Which list command is requested + enum class ECommand { unknown, add, remove, show } eCommand = ECommand::unknown; + if (iequals(rsContext.seqCmdLine[1], "ADD")) eCommand = ECommand::add; + else if (iequals(rsContext.seqCmdLine[1], "REMOVE")) eCommand = ECommand::remove; + else if (iequals(rsContext.seqCmdLine[1], "SHOW")) eCommand = ECommand::show; + else + { + if (!rsContext.bSilent) + { + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid connection command." << std::endl; + ConnectionHelp(rsContext); + } + return CMDLN_ARG_ERR; + } + + if (rsContext.seqCmdLine.size() < 3) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing connection name... " << std::endl; + return CMDLN_ARG_ERR; + } + + // Get the name + std::string ssConnection = rsContext.seqCmdLine[2]; + + switch (eCommand) + { + case ECommand::remove: + return RemoveConnection(ssConnection, rsContext); + case ECommand::show: + return ShowConnectionConfig(ssConnection, rsContext); + case ECommand::add: + default: + break; + } + + // For add, check for provider and parameters + if (rsContext.seqCmdLine.size() < 4) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing connection provider... " << std::endl; + return CMDLN_ARG_ERR; + } + std::string ssProvider = rsContext.seqCmdLine[3]; + + sdv::toml::CTOMLParser parser; + parser.AddValue("Provider.Name", ssProvider); + + // Check for parameters + // Parameters have the format "param=value". It is possible that parameters have additional sub-tables. Then the format is: + // "table.param=value". + sdv::toml::CNodeCollection ipc_channel_table = parser.AddTable("IpcChannel"); + for (size_t nIndex = 4; nIndex < rsContext.seqCmdLine.size(); nIndex++) + { + std::string ssRawParam = rsContext.seqCmdLine[nIndex]; + + // Search for the equal sign + size_t nEqualPos = ssRawParam.find_first_of('='); + std::string ssParamName = ssRawParam.substr(0, nEqualPos); + while (!ssParamName.empty() && std::isspace(ssParamName.back())) + ssParamName.pop_back(); + if (ssParamName.empty() || nEqualPos == std::string::npos) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid listener parameter: " << ssRawParam << std::endl; + return CMDLN_ARG_ERR; + } + sdv::any_t anyValue; + size_t nValuePos = nEqualPos + 1; + while (nValuePos < ssRawParam.size() && std::isspace(ssRawParam[nValuePos])) + nValuePos++; + if (nValuePos < ssRawParam.size()) + { + std::string ssValue = ssRawParam.substr(nValuePos); + if (!ssValue.empty() && ssValue.find_first_not_of("-0123456789") == std::string::npos) + { + int64_t iValue = 0; + if (std::from_chars(ssValue.data(), ssValue.data() + ssValue.size(), iValue).ec == std::errc()) + anyValue = iValue; + else + anyValue = ssValue; + } + else + anyValue = ssValue; + + } + ipc_channel_table.AddValue(ssParamName, anyValue); + } + + return AddConnection(ssConnection, parser.GetTOML(), rsContext); +} + +int AddConnection(const std::string& rssConnection, const std::string& rssConfig, const SContext& rsContext) +{ + // Load the settings. + auto ptrAppService = sdv::core::GetObject("AppSettingsService"); + sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface(); + sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface(); + if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl; + return CANNOT_LOAD_SETTINGS; + } + + // Add the connection + if (!pAppConnections->AddConnectionConfig(rssConnection, rssConfig, rsContext.ssInsertBeforeConnection)) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << ADD_CONNECTION_ERROR_MSG << std::endl; + return ADD_CONNECTION_ERROR; + } + + // Save the settings. + if (!pAppSettingsPersist->SaveSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_SAVE_SETTINGS_MSG << std::endl; + return CANNOT_SAVE_SETTINGS; + } + + return NO_ERROR; +} + +int RemoveConnection(const std::string& rssConnection, const SContext& rsContext) +{ + // Load the settings. + auto ptrAppService = sdv::core::GetObject("AppSettingsService"); + sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface(); + sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface(); + if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl; + return CANNOT_LOAD_SETTINGS; + } + + // Remove the connection + if (!pAppConnections->RemoveConnectionConfig(rssConnection)) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << REMOVE_CONNECTION_ERROR_MSG << std::endl; + return REMOVE_CONNECTION_ERROR; + } + + // Save the settings. + if (!pAppSettingsPersist->SaveSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_SAVE_SETTINGS_MSG << std::endl; + return CANNOT_SAVE_SETTINGS; + } + + return NO_ERROR; +} + +int ShowConnectionConfig(const std::string& rssConnection, const SContext& rsContext) +{ + // Load the settings. + auto ptrAppService = sdv::core::GetObject("AppSettingsService"); + sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface(); + sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface(); + if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl; + return CANNOT_LOAD_SETTINGS; + } + + // Remove the connection + std::string ssConfig = pAppConnections->GetConnectionConfig(rssConnection); + if (ssConfig.empty()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << SHOW_CONNECTION_ERROR_MSG << std::endl; + return SHOW_CONNECTION_ERROR; + } + + std::cout << ssConfig << std::endl; + + return NO_ERROR; +} diff --git a/sdv_executables/sdv_control/connection_config.h b/sdv_executables/sdv_control/connection_config.h new file mode 100644 index 0000000..5b2ceae --- /dev/null +++ b/sdv_executables/sdv_control/connection_config.h @@ -0,0 +1,98 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#ifndef CONNECTION_CONFIG_H +#define CONNECTION_CONFIG_H + +#include "context.h" +#include +#include + +/** + * @brief Help for configuring listeners. + * @param[in] rsContext Reference to the context. + */ +void ListenerHelp(const SContext& rsContext); + +/** + * @brief Help for configuring connections. + * @param[in] rsContext Reference to the context. + */ +void ConnectionHelp(const SContext& rsContext); + +/** + * @brief Configure the listener. + * @param[in] rsContext Reference to the context. + * @return The application exit code. 0 is no error. + */ +int ConfigureListener(const SContext& rsContext); + +/** + * @brief Add a listener to the system settings. + * @param[in] rssListener Reference to the string containing the listener configuration name. + * @param[in] rssConfig Reference to the string containing the listener configuration. + * @param[in] rsContext Reference to the context. + * @return The application exit code. 0 is no error. + */ +int AddListener(const std::string& rssListener, const std::string& rssConfig, const SContext& rsContext); + +/** + * @brief Remove a listener from the system settings. + * @param[in] rssListener Reference to the string containing the listener configuration name. + * @param[in] rsContext Reference to the context. + * @return The application exit code. 0 is no error. + */ +int RemoveListener(const std::string& rssListener, const SContext& rsContext); + +/** + * @brief Show a listener configuration. + * @param[in] rssListener Reference to the string containing the listener configuration name. + * @param[in] rsContext Reference to the context. + * @return The application exit code. 0 is no error. + */ +int ShowListenerConfig(const std::string& rssListener, const SContext& rsContext); + +/** + * @brief Configure the connection. + * @param[in] rsContext Reference to the context. + * @return The application exit code. 0 is no error. + */ +int ConfigureConnection(const SContext& rsContext); + +/** + * @brief Add a connection to the system settings. + * @param[in] rssConnection Reference to the string containing the connection configuration name. + * @param[in] rssConfig Reference to the string containing the connection configuration. + * @param[in] rsContext Reference to the context. + * @return The application exit code. 0 is no error. + */ +int AddConnection(const std::string& rssConnection, const std::string& rssConfig, const SContext& rsContext); + +/** + * @brief Remove a connection from the system settings. + * @param[in] rssConnection Reference to the string containing the connection configuration name. + * @param[in] rsContext Reference to the context. + * @return The application exit code. 0 is no error. + */ +int RemoveConnection(const std::string& rssConnection, const SContext& rsContext); + +/** + * @brief Show a connection configuration. + * @param[in] rssConnection Reference to the string containing the connection configuration name. + * @param[in] rsContext Reference to the context. + * @return The application exit code. 0 is no error. + */ +int ShowConnectionConfig(const std::string& rssConnection, const SContext& rsContext); + + +#endif // !defined CONNECTION_CONFIG_H diff --git a/sdv_executables/sdv_control/context.h b/sdv_executables/sdv_control/context.h index 49ad06b..e4932be 100644 --- a/sdv_executables/sdv_control/context.h +++ b/sdv_executables/sdv_control/context.h @@ -33,6 +33,7 @@ struct SContext bool bListShort = false; ///< Print only a shortened list with one column. sdv::sequence seqCmdLine; ///< The commands provided on the command line. std::filesystem::path pathInstallDir; ///< Optional installation directory. + std::string ssInsertBeforeConnection; ///< Insert before the provided connection }; #include // std::tolower diff --git a/sdv_executables/sdv_control/installation.cpp b/sdv_executables/sdv_control/installation.cpp index 4dffd9e..dc17d11 100644 --- a/sdv_executables/sdv_control/installation.cpp +++ b/sdv_executables/sdv_control/installation.cpp @@ -88,7 +88,7 @@ int Install(const SContext& rsContext) ssName = rsContext.seqCmdLine[1]; // Try to connect - sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID); + sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(); if (!ptrRepository) { if (!rsContext.bSilent) diff --git a/sdv_executables/sdv_control/list_elements.cpp b/sdv_executables/sdv_control/list_elements.cpp index 4a5bd7b..a4e1fbe 100644 --- a/sdv_executables/sdv_control/list_elements.cpp +++ b/sdv_executables/sdv_control/list_elements.cpp @@ -14,8 +14,10 @@ #include "list_elements.h" #include "print_table.h" #include +#include #include #include +#include #include "../../global/cmdlnparser/cmdlnparser.h" #include "../../global/exec_dir_helper.h" #include "../../global/flags.h" @@ -38,13 +40,14 @@ void ListHelp(const SContext& rsContext) } // Which list command help is requested - enum class EListCommand { unknown, modules, classes, components, installations, connections } eListCommand = EListCommand::unknown; + enum class EListCommand { unknown, modules, classes, components, installations, listeners, connections } eListCommand = EListCommand::unknown; if (rsContext.seqCmdLine.size() >= 2) { if (iequals(rsContext.seqCmdLine[1], "MODULES")) eListCommand = EListCommand::modules; else if (iequals(rsContext.seqCmdLine[1], "CLASSES")) eListCommand = EListCommand::classes; else if (iequals(rsContext.seqCmdLine[1], "COMPONENTS")) eListCommand = EListCommand::components; else if (iequals(rsContext.seqCmdLine[1], "INSTALLATIONS")) eListCommand = EListCommand::installations; + else if (iequals(rsContext.seqCmdLine[1], "LISTENERS")) eListCommand = EListCommand::listeners; else if (iequals(rsContext.seqCmdLine[1], "CONNECTIONS")) eListCommand = EListCommand::connections; } @@ -65,20 +68,24 @@ void ListHelp(const SContext& rsContext) case EListCommand::installations: CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST INSTALLATIONS [options...]\n\nShow a list of all installations.\n"); break; + case EListCommand::listeners: + CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST LISTENERS [options...]\n\nShow a list of all configured listeners.\n"); + break; case EListCommand::connections: - CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST CONNECTIONS [options...]\n\nShow a list of all connected applications.\n"); + CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST CONNECTIONS [options...]\n\nShow a list of all configured connections.\n"); break; default: - CCommandLine::PrintHelpText(std::cout, R"code(Usage: sdv_control LIST [options...] + CCommandLine::PrintHelpText(std::cout, R"text(Usage: sdv_control LIST [options...] Supported listing commands: LIST MODULES Show a list of loaded server modules. LIST CLASSES Show a list of available component classes. LIST COMPONENTS Show a list of instantiated server components LIST INSTALLATIONS Show a list of installations. + LIST LISTENERS Show a list of current listeners. LIST CONNECTIONS Show a list of current connections. -)code"); +)text"); break; } std::cout << "Options:\n"; @@ -103,33 +110,39 @@ int ListElements(const SContext& rsContext, std::ostream& rstream /*= std::cout* return CMDLN_ARG_ERR; } - // Which list command help is requested - enum class EListCommand { unknown, modules, classes, components, installations, connections } eListCommand = EListCommand::unknown; + // Which list command is requested + enum class EListCommand { unknown, modules, classes, components, installations, listeners, connections } eListCommand = EListCommand::unknown; if (rsContext.seqCmdLine.size() >= 2) { if (iequals(rsContext.seqCmdLine[1], "MODULES")) eListCommand = EListCommand::modules; else if (iequals(rsContext.seqCmdLine[1], "CLASSES")) eListCommand = EListCommand::classes; else if (iequals(rsContext.seqCmdLine[1], "COMPONENTS")) eListCommand = EListCommand::components; else if (iequals(rsContext.seqCmdLine[1], "INSTALLATIONS")) eListCommand = EListCommand::installations; + else if (iequals(rsContext.seqCmdLine[1], "LISTENERS")) eListCommand = EListCommand::listeners; else if (iequals(rsContext.seqCmdLine[1], "CONNECTIONS")) eListCommand = EListCommand::connections; } else { if (!rsContext.bSilent) { - std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listing command.." << std::endl << std::endl; + std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listing command." << std::endl; ListHelp(rsContext); } return CMDLN_ARG_ERR; } - // Try to connect - sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID); - if (!ptrRepository) + // Try to connect (except when listing the listeners and connections... they can be read from the settings). + sdv::TObjectPtr ptrRepository; + if (eListCommand != EListCommand::listeners && eListCommand != EListCommand::connections) { - if (!rsContext.bSilent) - std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << "." << std::endl; - return CONNECT_SDV_SERVER_ERROR; + ptrRepository = sdv::com::ConnectToLocalServerRepository(); + if (!ptrRepository) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << "." + << std::endl; + return CONNECT_SDV_SERVER_ERROR; + } } int iRet = -100; @@ -147,12 +160,16 @@ int ListElements(const SContext& rsContext, std::ostream& rstream /*= std::cout* case EListCommand::installations: iRet = ListInstallations(rsContext, ptrRepository, rstream); break; + case EListCommand::listeners: + iRet = ListListeners(rsContext, rstream); + break; case EListCommand::connections: - iRet = ListConnections(rsContext, ptrRepository, rstream); + iRet = ListConnections(rsContext, rstream); break; default: break; } + rstream << std::endl; return iRet; } @@ -388,8 +405,107 @@ int ListInstallations(const SContext& rsContext, const sdv::TObjectPtr& rptrRepo return NO_ERROR; } -int ListConnections(const SContext& /*rsContext*/, const sdv::TObjectPtr& /*rptrRepository*/, std::ostream& /*rstream = std::cout */ ) +std::string BuildParameters(const sdv::toml::CNodeCollection& rtable, const std::string& ssPrefix = std::string()) { - std::cout << "ERROR: " << NOT_IMPLEMENTED_MSG << " :-(" << std::endl; - return NOT_IMPLEMENTED; + std::string ssParams; + for (uint32_t uiIndex = 0; uiIndex < rtable.GetCount(); uiIndex++) + { + sdv::toml::CNode node = rtable.Get(uiIndex); + if (!node) continue; + std::string ssNodeText; + if (node.GetType() == sdv::toml::ENodeType::node_table) + { + sdv::toml::CNodeCollection tableChild(node); + if (!tableChild) continue; + ssNodeText = BuildParameters(tableChild, static_cast(tableChild.GetName()) + "."); + } + else if (node.GetType() == sdv::toml::ENodeType::node_array) + { + // TODO... + } + else + ssNodeText = ssPrefix + node.GetName() + "=" + node.GetValueAsString(); + + // Add the text + if (!ssParams.empty()) + ssParams += ", "; + ssParams += ssNodeText; + } + return ssParams; +} + +int ListListeners(const SContext& rsContext, std::ostream& rstream /*= std::cout */ ) +{ + std::vector> vecListenerList; + vecListenerList.push_back({"Listener", "Provider", "Parameters"}); + + // Request the settings. + auto ptrAppService = sdv::core::GetObject("AppSettingsService"); + sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface(); + sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface(); + if (!pAppSettingsPersist || !pAppConnections) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl; + return CANNOT_LOAD_SETTINGS; + } + auto seqListeners = pAppConnections->GetListeners(); + for (const sdv::u8string& rssListener : seqListeners) + { + std::string ssListener = rssListener; + auto ssConfig = pAppConnections->GetListenerConfig(rssListener); + sdv::toml::CTOMLParser parser(ssConfig); + if (!parser) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << PROCESS_LISTENER_CONFIG_ERROR_MSG << std::endl; + return PROCESS_LISTENER_CONFIG_ERROR; + } + std::string ssProvider = parser.GetDirect("Provider.Name").GetValueAsString(); + std::string ssParams = BuildParameters(parser.GetDirect("IpcChannel")); + vecListenerList.push_back({ssListener, ssProvider, ssParams}); + } + + PrintTable(vecListenerList, rstream, rsContext.bListNoHdr); + if (vecListenerList.size() < 2) + rstream << " No listeners configured!" << std::endl; + return NO_ERROR; +} + +int ListConnections(const SContext& rsContext, std::ostream& rstream /*= std::cout */ ) +{ + std::vector> vecConnectionList; + vecConnectionList.push_back({"Connection", "Provider", "Parameters"}); + + // Request the settings. + auto ptrAppService = sdv::core::GetObject("AppSettingsService"); + sdv::app::IAppSettingsPersist* pAppSettingsPersist = ptrAppService.GetInterface(); + sdv::app::IAppConnections* pAppConnections = ptrAppService.GetInterface(); + if (!pAppSettingsPersist || !pAppConnections || !pAppSettingsPersist->LoadSettings()) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << CANNOT_LOAD_SETTINGS_MSG << std::endl; + return CANNOT_LOAD_SETTINGS; + } + auto seqConnections = pAppConnections->GetConnections(); + for (const sdv::u8string& rssConnection : seqConnections) + { + std::string ssConnection = rssConnection; + auto ssConfig = pAppConnections->GetConnectionConfig(rssConnection); + sdv::toml::CTOMLParser parser(ssConfig); + if (!parser) + { + if (!rsContext.bSilent) + std::cerr << "ERROR: " << PROCESS_CONNECTION_CONFIG_ERROR_MSG << std::endl; + return PROCESS_CONNECTION_CONFIG_ERROR; + } + std::string ssProvider = parser.GetDirect("Provider.Name").GetValueAsString(); + std::string ssParams = BuildParameters(parser.GetDirect("IpcChannel")); + vecConnectionList.push_back({ssConnection, ssProvider, ssParams}); + } + + PrintTable(vecConnectionList, rstream, rsContext.bListNoHdr); + if (vecConnectionList.size() < 2) + rstream << " No connections configured!" << std::endl; + return NO_ERROR; } diff --git a/sdv_executables/sdv_control/list_elements.h b/sdv_executables/sdv_control/list_elements.h index 2515a18..80952c3 100644 --- a/sdv_executables/sdv_control/list_elements.h +++ b/sdv_executables/sdv_control/list_elements.h @@ -71,12 +71,19 @@ int ListComponents(const SContext& rsContext, const sdv::TObjectPtr& rptrReposit int ListInstallations(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream); /** - * @brief List the current connections. + * @brief List the current configured listeners. * @param[in] rsContext Reference to the context. - * @param[in] rptrRepository Reference to the saerver repository. * @param[in] rstream The output stream to use for printing (table only). * @return The application exit code. 0 is no error. */ -int ListConnections(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream); +int ListListeners(const SContext& rsContext, std::ostream& rstream); + +/** + * @brief List the current connections. + * @param[in] rsContext Reference to the context. + * @param[in] rstream The output stream to use for printing (table only). + * @return The application exit code. 0 is no error. + */ +int ListConnections(const SContext& rsContext, std::ostream& rstream); #endif // !defined LIST_ELEMENTS_H \ No newline at end of file diff --git a/sdv_executables/sdv_control/main.cpp b/sdv_executables/sdv_control/main.cpp index 86ed273..434ff34 100644 --- a/sdv_executables/sdv_control/main.cpp +++ b/sdv_executables/sdv_control/main.cpp @@ -27,6 +27,7 @@ #include "list_elements.h" #include "start_stop_service.h" #include "installation.h" +#include "connection_config.h" #include "../error_msg.h" /** @@ -53,12 +54,6 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) // NOTE EVE 27.05.2025: This task has been taken over by the process watchdog. CProcessWatchdog watchdog; - // If not set, set the runtime location to the EXE directory. - if (sdv::app::CAppControl::GetFrameworkRuntimeDirectory().empty()) - sdv::app::CAppControl::SetFrameworkRuntimeDirectory(GetExecDirectory()); - if (sdv::app::CAppControl::GetComponentInstallDirectory().empty()) - sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory()); - CCommandLine cmdln(static_cast(CCommandLine::EParseFlags::no_assignment_character)); SContext sContext; bool bError = false; @@ -67,24 +62,40 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) std::string ssArgError; try { - auto& rArgHelpDef = cmdln.DefineOption("?", bHelp, "Show help. Use --help for specific help on the command."); + // Argument groups 1 - 10: + // STARTUP + // SHUTDOWN + // LIST + // INSTALL + // UPDATE + // UNINSTALL + // START + // STOP + // LISTENER + // CONNECTION + auto& rArgHelpDef = + cmdln.DefineOption("?", bHelp, "Show help. Use --help for specific help on the command."); rArgHelpDef.AddSubOptionName("help"); - auto& rArgSilentDef = cmdln.DefineOption("s", sContext.bSilent, "Do not show any information on std::cout. Not compatible with 'verbose'."); + auto& rArgSilentDef = cmdln.DefineOption("s", sContext.bSilent, "Do not show any information on std::cout. Not compatible " + "with 'verbose'."); rArgSilentDef.AddSubOptionName("silent"); - auto& rArgVerboseDef = cmdln.DefineOption("v", sContext.bVerbose, "Provide verbose information. Not compatible with 'silent'."); + auto& rArgVerboseDef = cmdln.DefineOption("v", sContext.bVerbose, "Provide verbose information. Not compatible with " + "'silent'."); rArgVerboseDef.AddSubOptionName("verbose"); cmdln.DefineSubOption("version", bVersion, "Show version information."); cmdln.DefineSubOption("instance", sContext.uiInstanceID, "The instance ID of the SDV instance (default ID is 1000)."); cmdln.DefineSubOption("server_silent", sContext.bServerSilent, "Only used with STARTUP command: Server is started using " - "silent option. Not compatible with 'server_verbose'."); + "silent option. Not compatible with 'server_verbose'.", true, 1); cmdln.DefineSubOption("server_verbose", sContext.bServerVerbose, "Only used with STARTUP command: Server is started using " - "verbose option. Not compatible with 'server_silent'."); + "verbose option. Not compatible with 'server_silent'.", true, 1); cmdln.DefineSubOption("install_dir", sContext.pathInstallDir, "Only used with STARTUP command: Installation directory " - "(absolute or relative to the sdv_core executable)."); + "(absolute or relative to the sdv_core executable).", true, 1); cmdln.DefineSubOption("no_header", sContext.bListNoHdr, "Only used with LIST command: Do not print a header for the " - "listing table."); + "listing table.", true, 3); cmdln.DefineSubOption("short", sContext.bListShort, "Only used with LIST command: Print only the most essential " - "information as one column."); + "information as one column.", true, 3); + cmdln.DefineSubOption( + "insert_before", sContext.ssInsertBeforeConnection, "Name of the connection to insert before.", true, 10); cmdln.DefineDefaultArgument(sContext.seqCmdLine, "COMMAND"); cmdln.Parse(static_cast(iArgc), rgszArgv); @@ -98,7 +109,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (!sContext.bSilent) { std::cout << "SDV Server Control Utility" << std::endl; - std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl; + std::cout << "Copyright (C): 2022-2026 ZF Friedrichshafen AG" << std::endl; std::cout << "Author: Erik Verhoeven" << std::endl << std::endl; } @@ -113,7 +124,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) bError = true; } - enum class ECommand { unknown, startup, shutdown, list, install, update, uninstall, start, stop } eCommand = ECommand::unknown; + enum class ECommand { unknown, startup, shutdown, list, install, update, uninstall, start, stop, listener, connection } eCommand = ECommand::unknown; if (!sContext.seqCmdLine.empty()) { if (iequals(sContext.seqCmdLine[0], "STARTUP")) eCommand = ECommand::startup; @@ -124,6 +135,8 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) else if (iequals(sContext.seqCmdLine[0], "UNINSTALL")) eCommand = ECommand::uninstall; else if (iequals(sContext.seqCmdLine[0], "START")) eCommand = ECommand::start; else if (iequals(sContext.seqCmdLine[0], "STOP")) eCommand = ECommand::stop; + else if (iequals(sContext.seqCmdLine[0], "LISTENER")) eCommand = ECommand::listener; + else if (iequals(sContext.seqCmdLine[0], "CONNECTION")) eCommand = ECommand::connection; else { if (!sContext.bSilent) @@ -157,17 +170,25 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) case ECommand::uninstall: InstallationHelp(sContext); break; + case ECommand::listener: + ListenerHelp(sContext); + break; + case ECommand::connection: + ConnectionHelp(sContext); + break; default: - cmdln.PrintHelp(std::cout, R"code(Supported commands: - STARTUP Start the core application server - SHUTDOWN Stop the core application server - LIST List module/classes/component/installation/connection information. - INSTALL Install a new application or service. - UPDATE Update an existing installation. - UNINSTALL Uninstall an installed application or service. - START Start a service (complex services only). - STOP Stop a service (complex services only). -)code"); + cmdln.PrintHelp(std::cout, R"text(Supported commands: + STARTUP Start the core application server + SHUTDOWN Stop the core application server + LIST List information about modules/classes/components/installations/listeners/connections. + INSTALL Install a new application or service. + UPDATE Update an existing installation. + UNINSTALL Uninstall an installed application or service. + START Start a service (complex services and vehicle functions only). + STOP Stop a service (complex services and vehicle functions only). + LISTENER Add or remove a communication listener configuration. + CONNECTION Add or remove a connection configuration. +)text"); break; } } @@ -185,11 +206,11 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (sContext.bVerbose) std::cout << "Starting local application control.." << std::endl; sdv::app::CAppControl appcontrol; - std::string ssAppConfig = std::string(R"code( + std::string ssAppConfig = std::string(R"toml( [Application] Mode = "Maintenance" -Instance = )code") + std::to_string(sContext.uiInstanceID) + R"code( -)code"; +Instance = )toml") + std::to_string(sContext.uiInstanceID) + R"toml( +)toml"; if (!appcontrol.Startup(ssAppConfig)) { if (!sContext.bSilent) @@ -224,6 +245,12 @@ Instance = )code") + std::to_string(sContext.uiInstanceID) + R"code( case ECommand::uninstall: iRet = Uninstall(sContext); break; + case ECommand::listener: + iRet = ConfigureListener(sContext); + break; + case ECommand::connection: + iRet = ConfigureConnection(sContext); + break; default: std::cout << "Command missing :-(" << std::endl; break; diff --git a/sdv_executables/sdv_control/start_stop_service.cpp b/sdv_executables/sdv_control/start_stop_service.cpp index 879111e..c830e21 100644 --- a/sdv_executables/sdv_control/start_stop_service.cpp +++ b/sdv_executables/sdv_control/start_stop_service.cpp @@ -35,8 +35,8 @@ void StartStopServiceHelp(const SContext& rsContext) " sdv_control START [options...]\n\n" "Start a complex service with the supplied object name as is defined in the configuration (first usage) or the " "supplied class name and with object name assignment and add to the configuration (second usage).\n" - "Only complex services can be started. If the service depends on not running other services, these are started as " - "well.\n\n"); + "Only vehicle functions and complex services can be started. If the service depends on not running other services, " + "these are started as well.\n\n"); return; } if (iequals(rsContext.seqCmdLine[0], "STOP")) @@ -47,15 +47,14 @@ void StartStopServiceHelp(const SContext& rsContext) " sdv_control STOP [options...]\n\n" "Stop the complex service(s) with the supplied object name (first usage), with supplied object ID (second usage), with " "supplied class name (third usage) or contained in the module with the supplied module name (fourth usage).\n" - "Only complex services can be stopped. Dependent services are stopped as well. If one of object to be stopped is not a " - "complex service, the command fails.\n\n"); + "Only vehicle functions and complex services can be stopped. Dependent services are stopped as well. If one of object " + "to be stopped is not a vehicle function or a complex service, the command fails.\n\n"); return; } if (!rsContext.bSilent) std::cerr << "ERROR: invalid start/stop command..." << std::endl; } - int StartService(const SContext& rsContext) { // First argument should be "START" @@ -88,7 +87,7 @@ int StartService(const SContext& rsContext) ssName = rsContext.seqCmdLine[1]; // Try to connect - sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID); + sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(); if (!ptrRepository) { if (!rsContext.bSilent) @@ -154,7 +153,7 @@ int StopService(const SContext& rsContext) ssName = rsContext.seqCmdLine[1]; // Try to connect - sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID); + sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(); if (!ptrRepository) { if (!rsContext.bSilent) diff --git a/sdv_executables/sdv_control/startup_shutdown.cpp b/sdv_executables/sdv_control/startup_shutdown.cpp index 57c8c0f..e29d77b 100644 --- a/sdv_executables/sdv_control/startup_shutdown.cpp +++ b/sdv_executables/sdv_control/startup_shutdown.cpp @@ -76,9 +76,9 @@ void StartupShutdownHelp(const SContext& rsContext) "Start the SDV server with the supplied instance ID. If no instance ID is supplied, use the default instance " "ID #1000.\n\n" "Options:\n" - " --server_silent Server is started using silent option. Not compatible with 'server_verbose'.\n" - " --server_verbose Server is started using verbose option. Not compatible with 'server_silent'.\n" - " --install_dir Installation directory (absolute or relative to the sdv_core executable).\n\n"); + " --server_silent Server is started using silent option. Not compatible with 'server_verbose'.\n" + " --server_verbose Server is started using verbose option. Not compatible with 'server_silent'.\n" + " --install_dir Installation directory (absolute or relative to the sdv_core executable).\n\n"); return; } if (iequals(rsContext.seqCmdLine[0], "SHUTDOWN")) @@ -156,7 +156,7 @@ int StartupSDVServer(const SContext& rsContext) } // Try to connect - auto ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID); + auto ptrRepository = sdv::com::ConnectToLocalServerRepository(); if (!ptrRepository) { if (!rsContext.bSilent) @@ -183,7 +183,7 @@ int ShutdownSDVServer(const SContext& rsContext) std::cout << "Connecting to the SDV #" << rsContext.uiInstanceID << " server..." << std::endl; // Try to connect - auto ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID); + auto ptrRepository = sdv::com::ConnectToLocalServerRepository(); if (!ptrRepository) { if (!rsContext.bSilent) diff --git a/sdv_executables/sdv_core/main.cpp b/sdv_executables/sdv_core/main.cpp index 012c37d..857b100 100644 --- a/sdv_executables/sdv_core/main.cpp +++ b/sdv_executables/sdv_core/main.cpp @@ -18,6 +18,40 @@ #include #include "../error_msg.h" +#ifdef _WIN32 +/** + * @brief Console application control handler (CTRL+C, close, shutdown, etc.). + * @param[in] dwCtrlType The control type triggered by the system. + * @return Returns TRUE when the signal was handled. + */ +static BOOL WINAPI ControlHandler(DWORD dwCtrlType) +{ + switch (dwCtrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + return TRUE; + break; + default: + return FALSE; + } +} +#elif defined __unix__ +#include + + /** + * @brief Signal handler to catch signal events. + * @param[in] iSigNum The signal type that was triggered. + */ +static void SignalHandler(int /*iSigNum*/) +{ + // Do nothing... +} +#endif + /** * @brief Main function of the executable. * @param[in] iArgc Amount of arguments provided. @@ -46,12 +80,6 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (thread.joinable()) thread.join(); - // If not set, set the runtime location to the EXE directory. - if (sdv::app::CAppControl::GetFrameworkRuntimeDirectory().empty()) - sdv::app::CAppControl::SetFrameworkRuntimeDirectory(GetExecDirectory()); - if (sdv::app::CAppControl::GetComponentInstallDirectory().empty()) - sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory()); - CCommandLine cmdln(static_cast(CCommandLine::EParseFlags::no_assignment_character)); bool bHelp = false; bool bError = false; @@ -91,7 +119,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (!bSilent && !bNoBanner) { std::cout << "SDV core application" << std::endl; - std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl; + std::cout << "Copyright (C): 2022-2026 ZF Friedrichshafen AG" << std::endl; std::cout << "Author: Erik Verhoeven" << std::endl << std::endl; } @@ -185,14 +213,40 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) return APP_CONTROL_STARTUP_ERROR; } +#ifdef _WIN32 + // Register the console control handler + if (!SetConsoleCtrlHandler(&ControlHandler, TRUE)) + { + if (!bSilent) + std::cerr << "ERROR: " << APP_CONTROL_START_LOOP_FAILED_MSG << " - failed registering control handler." << std::endl; + return APP_CONTROL_START_LOOP_FAILED; + } +#elif defined __unix__ + // Install our signal handler + struct sigaction sSigAction + {}; + sSigAction.sa_handler = SignalHandler; + sSigAction.sa_flags = SA_RESTART; + if (sigaction(SIGINT, &sSigAction, NULL) == -1) + { + if (!bSilent) + std::cerr << "ERROR: " << APP_CONTROL_START_LOOP_FAILED_MSG << " - failed registering control handler." << std::endl; + return APP_CONTROL_START_LOOP_FAILED; + } +#endif + // Start the running loop if (!appcontrol.RunLoop()) { if (!bSilent) - std::cerr << "ERROR: " << APP_CONTROL_DUPLICATE_INSTANCE_MSG << std::endl; - return APP_CONTROL_DUPLICATE_INSTANCE; + std::cerr << "ERROR: " << APP_CONTROL_START_LOOP_FAILED_MSG << std::endl; + return APP_CONTROL_START_LOOP_FAILED; } +#ifdef _WIN32 + SetConsoleCtrlHandler(&ControlHandler, FALSE); +#endif + if (bVerbose) { std::cout << "Shutdown request received..." << std::endl; diff --git a/sdv_executables/sdv_dbc_util/fmu_templates.h b/sdv_executables/sdv_dbc_util/fmu_templates.h index 3505e95..485e028 100644 --- a/sdv_executables/sdv_dbc_util/fmu_templates.h +++ b/sdv_executables/sdv_dbc_util/fmu_templates.h @@ -372,7 +372,10 @@ bool CreateCoreServiceTomlFile(const std::string& resources) std::ofstream tomlFile("sdv_core_reloc.toml"); if (tomlFile.is_open()) { - tomlFile << "# Location of the SDV binaries and configuration files\ndirectory = \""; + tomlFile << R"toml(# Location of the SDV binaries and configuration files +[CoreLocation] +Version = 100 +Framework = ")toml"; tomlFile << resources; tomlFile << "\"\n"; tomlFile.close(); diff --git a/sdv_executables/sdv_dbc_util/main.cpp b/sdv_executables/sdv_dbc_util/main.cpp index 6cfc0d0..1bf81d6 100644 --- a/sdv_executables/sdv_dbc_util/main.cpp +++ b/sdv_executables/sdv_dbc_util/main.cpp @@ -82,7 +82,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (!bSilent) { std::cout << "DBC utility" << std::endl; - std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl; + std::cout << "Copyright (C): 2022-2026 ZF Friedrichshafen AG" << std::endl; std::cout << "Author: Erik Verhoeven" << std::endl; } if (bHelp) diff --git a/sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp b/sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp index a53432b..cb5f889 100644 --- a/sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp +++ b/sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp @@ -174,6 +174,9 @@ std::string CProxyGenerator::GetFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& r else sstreamExceptions << R"code(sdv::XUnknownException exception; exception.unknown_id = except_id; + // Cpp-Check 2.7 warns about the uninitialized variable _id. This variable is allocated as inline static const and is + // assigned a value. Suppress the warning. + // cppcheck-suppress uninitvar throw exception;)code"; for (const std::string& rssException : rvecExceptions) { @@ -181,6 +184,9 @@ std::string CProxyGenerator::GetFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& r { )code" << rssException << R"code( exception; desOutput >> exception; + // Cpp-Check 2.7 warns about the uninitialized variable _id. This variable is allocated as inline static const and is + // assigned a value. Suppress the warning. + // cppcheck-suppress uninitvar throw exception; } )code"; @@ -190,6 +196,9 @@ std::string CProxyGenerator::GetFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& r { sdv::XUnknownException exception; exception.unknown_id = except_id; + // Cpp-Check 2.7 warns about the uninitialized variable _id. This variable is allocated as inline static const and is + // assigned a value. Suppress the warning. + // cppcheck-suppress uninitvar throw exception; } })code"; diff --git a/sdv_executables/sdv_idl_compiler/main.cpp b/sdv_executables/sdv_idl_compiler/main.cpp index 4723265..d6781a7 100644 --- a/sdv_executables/sdv_idl_compiler/main.cpp +++ b/sdv_executables/sdv_idl_compiler/main.cpp @@ -41,7 +41,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) CProcessWatchdog watchdog; std::cout << "SDV IDL Compiler" << std::endl; - std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl; + std::cout << "Copyright (C): 2022-2026 ZF Friedrichshafen AG" << std::endl; std::cout << "Author: Erik Verhoeven" << std::endl; // Create a list of UTF-8 arguments diff --git a/sdv_executables/sdv_idl_compiler/source.cpp b/sdv_executables/sdv_idl_compiler/source.cpp index cbe7e0c..2fef48a 100644 --- a/sdv_executables/sdv_idl_compiler/source.cpp +++ b/sdv_executables/sdv_idl_compiler/source.cpp @@ -28,6 +28,7 @@ #include "token.h" #include #include +#include #include #include #include @@ -71,7 +72,7 @@ const std::string& CSource::GetCodeRef() const void CSource::ReadFile(const std::filesystem::path& rpath) { // Set the locale to work with UTF8 - std::setlocale(LC_ALL, "en_US.utf8"); + ::setlocale(LC_ALL, "en_US.utf8"); if (rpath.empty()) throw CCompileException("File name missing"); diff --git a/sdv_executables/sdv_iso/main.cpp b/sdv_executables/sdv_iso/main.cpp index d979154..4b51128 100644 --- a/sdv_executables/sdv_iso/main.cpp +++ b/sdv_executables/sdv_iso/main.cpp @@ -22,6 +22,40 @@ #include #include "../error_msg.h" +#ifdef _WIN32 +/** + * @brief Console application control handler (CTRL+C, close, shutdown, etc.). + * @param[in] dwCtrlType The control type triggered by the system. + * @return Returns TRUE when the signal was handled. + */ +static BOOL WINAPI ControlHandler(DWORD dwCtrlType) +{ + switch (dwCtrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + return TRUE; + break; + default: + return FALSE; + } +} +#elif defined __unix__ +#include + + /** + * @brief Signal handler to catch signal events. + * @param[in] iSigNum The signal type that was triggered. + */ +static void SignalHandler(int /*iSigNum*/) +{ + // Do nothing... +} +#endif + /** * @brief Connect event callback wrapper. Calls shutdown request on disconnect or error. */ @@ -93,12 +127,6 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (thread.joinable()) thread.join(); - // If not set, set the runtime location to the EXE directory. - if (sdv::app::CAppControl::GetFrameworkRuntimeDirectory().empty()) - sdv::app::CAppControl::SetFrameworkRuntimeDirectory(GetExecDirectory()); - if (sdv::app::CAppControl::GetComponentInstallDirectory().empty()) - sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory()); - CCommandLine cmdln(static_cast(CCommandLine::EParseFlags::no_assignment_character)); bool bHelp = false; bool bError = false; @@ -126,23 +154,29 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) { ssArgError = rsExcept.what(); bHelp = true; - bError = true; } if (!bSilent) { std::cout << "SDV isolation application" << std::endl; - std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl; + std::cout << "Copyright (C): 2022-2026 ZF Friedrichshafen AG" << std::endl; std::cout << "Author: Erik Verhoeven" << std::endl << std::endl; } - if (!ssArgError.empty() && !bSilent) + if (!ssArgError.empty() && !bSilent && !bHelp && !bVersion) std::cerr << "ERROR: " << ssArgError << std::endl; - if (ssConfigBase64.empty()) + if (bVersion || bVerbose) { - ssArgError = "Missing app configuration."; - bHelp = true; + std::cout << "Version: " << (SDVFrameworkBuildVersion / 100) << "." << (SDVFrameworkBuildVersion % 100) << " build " + << SDVFrameworkSubbuildVersion << " interface " << SDVFrameworkInterfaceVersion << std::endl; + } + + if (!bHelp && ssConfigBase64.empty()) + { + if (!bSilent && !bVersion) + std::cout << "Missing app configuration."; + bError = true; } if (bHelp) @@ -153,17 +187,14 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) std::cout << std::endl; cmdln.PrintHelp(std::cout, "The isolation application is used to isolate components from the core process. The " "isolation aims to improve the overall stability of the system.\n"); - return bError ? CMDLN_ARG_ERR : NO_ERROR; } + return (bError && !bVersion && !bHelp) ? CMDLN_ARG_ERR : NO_ERROR; } - if (bError) return CMDLN_ARG_ERR; + if (bError) return (bError && !bVersion && !bHelp) ? CMDLN_ARG_ERR : NO_ERROR; // Check for encoded configuration std::string ssIsoAppConfig = Base64DecodePlainText(ssConfigBase64); - if (bVersion || bVerbose) - std::cout << "Version: " << (SDVFrameworkBuildVersion / 100) << "." << (SDVFrameworkBuildVersion % 100) << " build " << - SDVFrameworkSubbuildVersion << " interface " << SDVFrameworkInterfaceVersion << std::endl; if (!bSilent) std::cout << "Instance ID: " << uiInstanceID << std::endl; @@ -267,7 +298,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) std::cerr << "ERROR: " << LINK_REPO_SERVICE_ERROR_MSG << std::endl; return LINK_REPO_SERVICE_ERROR; } - pLinkCoreRepo->LinkCoreRepository(pCoreRepo); + sdv::core::TLinkID tRepoLink = pLinkCoreRepo->LinkCoreRepository(pCoreRepo); // Create the one object... is this a service or a utility? std::string ssClassName = parser.GetDirect("Isolation.Class").GetValue(); @@ -332,6 +363,28 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) // The lifetime of the object is now taken over by the core or if failed, the object will be destroyed. ptrObject.Clear(); +#ifdef _WIN32 + // Register the console control handler + if (!SetConsoleCtrlHandler(&ControlHandler, TRUE)) + { + if (!bSilent) + std::cerr << "ERROR: " << APP_CONTROL_START_LOOP_FAILED_MSG << " - failed registering control handler." << std::endl; + return APP_CONTROL_START_LOOP_FAILED; + } +#elif defined __unix__ + // Install our signal handler + struct sigaction sSigAction + {}; + sSigAction.sa_handler = SignalHandler; + sSigAction.sa_flags = SA_RESTART; + if (sigaction(SIGINT, &sSigAction, NULL) == -1) + { + if (!bSilent) + std::cerr << "ERROR: " << APP_CONTROL_START_LOOP_FAILED_MSG << " - failed registering control handler." << std::endl; + return APP_CONTROL_START_LOOP_FAILED; + } +#endif + // Deactivate the configuration mode. appcontrol.SetRunningMode(); @@ -339,10 +392,14 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (!appcontrol.RunLoop()) { if (!bSilent) - std::cerr << "ERROR: " << APP_CONTROL_START_LOOP_FAIELD_MSG << std::endl; - return APP_CONTROL_START_LOOP_FAIELD; + std::cerr << "ERROR: " << APP_CONTROL_START_LOOP_FAILED_MSG << std::endl; + return APP_CONTROL_START_LOOP_FAILED; } +#ifdef _WIN32 + SetConsoleCtrlHandler(&ControlHandler, FALSE); +#endif + // TODO EVE if (appcontrol.IsConfiguring()) std::cout << "Iso app configuring... " << __LINE__ << std::endl; @@ -358,7 +415,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) // Shutdwown pConnect->UnregisterStateEventCallback(uiCookie); parser.Clear(); - pLinkCoreRepo->UnlinkCoreRepository(); + pLinkCoreRepo->UnlinkCoreRepository(tRepoLink); appcontrol.Shutdown(); if (!bSilent) diff --git a/sdv_executables/sdv_local_shutdown/main.cpp b/sdv_executables/sdv_local_shutdown/main.cpp index b103e16..1a5d8c0 100644 --- a/sdv_executables/sdv_local_shutdown/main.cpp +++ b/sdv_executables/sdv_local_shutdown/main.cpp @@ -32,12 +32,6 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) // NOTE EVE 27.05.2025: This task has been taken over by the process watchdog. CProcessWatchdog watchdog; - // If not set, set the runtime location to the EXE directory. - if (sdv::app::CAppControl::GetFrameworkRuntimeDirectory().empty()) - sdv::app::CAppControl::SetFrameworkRuntimeDirectory(GetExecDirectory()); - if (sdv::app::CAppControl::GetComponentInstallDirectory().empty()) - sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory()); - CCommandLine cmdln(static_cast(CCommandLine::EParseFlags::no_assignment_character)); bool bHelp = false; bool bError = false; @@ -67,7 +61,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (!bSilent) { std::cout << "SDV Local Instance Shutdown Utility" << std::endl; - std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl; + std::cout << "Copyright (C): 2022-2026 ZF Friedrichshafen AG" << std::endl; std::cout << "Author: Erik Verhoeven" << std::endl << std::endl; } diff --git a/sdv_executables/sdv_packager/core_control.cpp b/sdv_executables/sdv_packager/core_control.cpp index a58493a..bdd79e4 100644 --- a/sdv_executables/sdv_packager/core_control.cpp +++ b/sdv_executables/sdv_packager/core_control.cpp @@ -24,4 +24,5 @@ #include "../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../sdv_services/core/toml_parser/miscellaneous.cpp" #include "../../sdv_services/core/toml_parser/code_snippet.cpp" +#include "../../sdv_services/core/toml_parser/parser_node_indexer.cpp" diff --git a/sdv_executables/sdv_packager/environment.cpp b/sdv_executables/sdv_packager/environment.cpp index ef57ceb..27d05f7 100644 --- a/sdv_executables/sdv_packager/environment.cpp +++ b/sdv_executables/sdv_packager/environment.cpp @@ -12,9 +12,8 @@ ********************************************************************************/ #include "environment.h" - #include - +#include "../../global/exec_dir_helper.h" #include "../../global/cmdlnparser/cmdlnparser.cpp" CSdvPackagerEnvironment::CSdvPackagerEnvironment() : @@ -862,13 +861,16 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector& { #ifdef _WIN32 const wchar_t* szInstallDir = _wgetenv(L"SDV_COMPONENT_INSTALL"); + if (!szInstallDir) szInstallDir = _wgetenv(L"SDV_FRAMEWORK_RUNTIME"); if (szInstallDir) m_pathTargetLocation = szInstallDir; #elif defined __unix__ const char* szInstallDir = getenv("SDV_COMPONENT_INSTALL"); + if (!szInstallDir) szInstallDir = getenv("SDV_FRAMEWORK_RUNTIME"); if (szInstallDir) m_pathTargetLocation = std::filesystem::u8path(szInstallDir); #else #error OS not supported! #endif + if (m_pathTargetLocation.empty()) m_pathTargetLocation = GetExecDirectory(); } // Is the directory existing? If not, create the directory diff --git a/sdv_executables/sdv_packager/main.cpp b/sdv_executables/sdv_packager/main.cpp index 28f40c3..2b30e9f 100644 --- a/sdv_executables/sdv_packager/main.cpp +++ b/sdv_executables/sdv_packager/main.cpp @@ -41,12 +41,6 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) // NOTE EVE 27.05.2025: This task has been taken over by the process watchdog. CProcessWatchdog watchdog; - // If not set, set the runtime location to the EXE directory. - if (sdv::app::CAppControl::GetFrameworkRuntimeDirectory().empty()) - sdv::app::CAppControl::SetFrameworkRuntimeDirectory(GetExecDirectory()); - if (sdv::app::CAppControl::GetComponentInstallDirectory().empty()) - sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory()); - // Process the command line. CSdvPackagerEnvironment environment(iArgc, rgszArgv); @@ -54,7 +48,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (!environment.Silent()) { std::cout << "SDV Component Installation Package Utility" << std::endl; - std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl; + std::cout << "Copyright (C): 2022-2026 ZF Friedrichshafen AG" << std::endl; std::cout << "Author: Erik Verhoeven" << std::endl << std::endl; } @@ -114,7 +108,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) } // If running as server, load the settings file - if (!environment.Local() && !GetAppSettings().LoadSettingsFile()) + if (!environment.Local() && !GetAppSettings().LoadSettings()) { std::cerr << "ERROR: Failed to load the application settings file; cannot continue!" << std::endl; return -1; diff --git a/sdv_executables/sdv_packager/packager.cpp b/sdv_executables/sdv_packager/packager.cpp index de03560..f0fb875 100644 --- a/sdv_executables/sdv_packager/packager.cpp +++ b/sdv_executables/sdv_packager/packager.cpp @@ -450,7 +450,7 @@ bool CPackager::Configure() m_ssArgError = CMDLN_MISSING_TARGET_MSG; return false; } - if (!GetAppSettings().SaveSettingsFile()) + if (!GetAppSettings().SaveSettings()) { SDV_LOG_ERROR("Failed to save application settings. Cannot configure components!"); return false; @@ -743,7 +743,7 @@ bool CPackager::ConfigureFromManifest(const CInstallManifest& rmanifest) auto pathConfig = GetAppSettings().GetConfigPath(CAppSettings::EConfigType::user_config); WriteConfig(vecClasses, pathConfig, vecComponentsToAdd, true, vecAddedToConfig); } - if (!GetAppSettings().SaveSettingsFile()) + if (!GetAppSettings().SaveSettings()) { SDV_LOG_ERROR("Failed to save application settings. Cannot configure components!"); return false; @@ -806,11 +806,11 @@ void CPackager::WriteConfig(const std::vector& rvecAllClasses, break; case sdv::EObjectType::complex_service: case sdv::EObjectType::vehicle_function: - case sdv::EObjectType::utility: eIncompatible = bUserConfig ? ECompatibility::compatible : ECompatibility::incompatible; break; case sdv::EObjectType::proxy: case sdv::EObjectType::stub: + case sdv::EObjectType::utility: eIncompatible = ECompatibility::silent_incompatible; break; default: diff --git a/sdv_executables/sdv_trace_mon/main.cpp b/sdv_executables/sdv_trace_mon/main.cpp index 0fac0e3..bc276cd 100644 --- a/sdv_executables/sdv_trace_mon/main.cpp +++ b/sdv_executables/sdv_trace_mon/main.cpp @@ -65,7 +65,7 @@ static BOOL WINAPI ControlHandler(DWORD dwCtrlType) } #elif defined __unix__ /** - * @brief Signal handler to catch signal eevents. + * @brief Signal handler to catch signal events. * @param[in] iSigNum The signal type that was triggered. */ static void SignalHandler(int iSigNum) @@ -97,12 +97,6 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (thread.joinable()) thread.join(); - // If not set, set the runtime location to the EXE directory. - if (sdv::app::CAppControl::GetFrameworkRuntimeDirectory().empty()) - sdv::app::CAppControl::SetFrameworkRuntimeDirectory(GetExecDirectory()); - if (sdv::app::CAppControl::GetComponentInstallDirectory().empty()) - sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory()); - CCommandLine cmdln(static_cast(CCommandLine::EParseFlags::no_assignment_character)); bool bHelp = false; bool bVersion = false; @@ -123,7 +117,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) } std::cout << "SDV Server Log Monitor Utility" << std::endl; - std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl; + std::cout << "Copyright (C): 2022-2026 ZF Friedrichshafen AG" << std::endl; std::cout << "Author: Erik Verhoeven" << std::endl << std::endl; if (bVersion) diff --git a/sdv_executables/sdv_vss_util/main.cpp b/sdv_executables/sdv_vss_util/main.cpp index 9785294..5422f1b 100644 --- a/sdv_executables/sdv_vss_util/main.cpp +++ b/sdv_executables/sdv_vss_util/main.cpp @@ -109,7 +109,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) if (!bSilent) { std::cout << "abtract device / basic services utility" << std::endl; - std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl; + std::cout << "Copyright (C): 2022-2026 ZF Friedrichshafen AG" << std::endl; std::cout << "Author: Thomas Pfleiderer" << std::endl; } diff --git a/sdv_services/CMakeLists.txt b/sdv_services/CMakeLists.txt index ba5db0a..5924c17 100644 --- a/sdv_services/CMakeLists.txt +++ b/sdv_services/CMakeLists.txt @@ -170,6 +170,14 @@ add_custom_command( COMMAND sdv_idl_compiler ${INTERFACE_DIR}/repository.idl -O${INTERFACE_DIR} VERBATIM ) +add_custom_command( + OUTPUT ${INTERFACE_DIR}/permission.h + DEPENDS sdv_idl_compiler + MAIN_DEPENDENCY ${INTERFACE_DIR}/permission.idl + COMMENT "Compiling permission.idl" + COMMAND sdv_idl_compiler ${INTERFACE_DIR}/permission.idl -O${INTERFACE_DIR} --no_ps + VERBATIM + ) add_custom_command( OUTPUT ${INTERFACE_DIR}/timer.h DEPENDS sdv_idl_compiler @@ -198,6 +206,7 @@ add_custom_target(CompileCoreIDL ${INTERFACE_DIR}/process.h ${INTERFACE_DIR}/param.h ${INTERFACE_DIR}/repository.h + ${INTERFACE_DIR}/permission.h ${INTERFACE_DIR}/timer.h ) @@ -212,10 +221,14 @@ add_subdirectory(task_timer) add_subdirectory(ipc_com) add_subdirectory(ipc_connect) add_subdirectory(ipc_shared_mem) -add_subdirectory(uds_unix_sockets) -add_subdirectory(uds_unix_tunnel) -add_subdirectory(uds_win_sockets) -add_subdirectory(uds_win_tunnel) +if(UNIX) + add_subdirectory(uds_unix_sockets) + add_subdirectory(uds_unix_tunnel) +endif() +if(WIN32) + add_subdirectory(uds_win_sockets) + add_subdirectory(uds_win_tunnel) +endif() add_subdirectory(process_control) add_subdirectory(hardware_ident) add_subdirectory(manifest_util) diff --git a/sdv_services/can_communication_sim/can_com_sim.h b/sdv_services/can_communication_sim/can_com_sim.h index 2496b49..dc8e261 100644 --- a/sdv_services/can_communication_sim/can_com_sim.h +++ b/sdv_services/can_communication_sim/can_com_sim.h @@ -118,7 +118,7 @@ private: */ void PlaybackFunc(const asc::SCanMessage& rsMsg); - std::thread m_threadReceive; ///< Receive thread. + sdv::core::secure_thread m_threadReceive; ///< Receive thread. mutable std::mutex m_mtxReceivers; ///< Protect the receiver set. std::set m_setReceivers; ///< Set with receiver interfaces. mutable std::mutex m_mtxInterfaces; ///< Protect the nodes set. diff --git a/sdv_services/can_communication_socket_can/can_com_sockets.cpp b/sdv_services/can_communication_socket_can/can_com_sockets.cpp index ec17be0..02319a3 100644 --- a/sdv_services/can_communication_socket_can/can_com_sockets.cpp +++ b/sdv_services/can_communication_socket_can/can_com_sockets.cpp @@ -64,7 +64,7 @@ bool CCANSockets::OnInitialize() } LogConfigurations(); - m_threadReceive = std::thread(&CCANSockets::ReceiveThreadFunc, this); + m_threadReceive = sdv::core::secure_thread(&CCANSockets::ReceiveThreadFunc, this); return true; } diff --git a/sdv_services/can_communication_socket_can/can_com_sockets.h b/sdv_services/can_communication_socket_can/can_com_sockets.h index 18381bd..6717686 100644 --- a/sdv_services/can_communication_socket_can/can_com_sockets.h +++ b/sdv_services/can_communication_socket_can/can_com_sockets.h @@ -146,7 +146,7 @@ private: std::string name; ///< interface name, can be empty in case of an invalid socket element }; - std::thread m_threadReceive; ///< Receive thread. + sdv::core::secure_thread m_threadReceive; ///< Receive thread. mutable std::mutex m_mtxReceivers; ///< Protect the receiver set. std::set m_setReceivers; ///< Set with receiver interfaces. mutable std::mutex m_mtxSockets; ///< Protect the socket list. diff --git a/sdv_services/core/CMakeLists.txt b/sdv_services/core/CMakeLists.txt index 37ce962..70006e6 100644 --- a/sdv_services/core/CMakeLists.txt +++ b/sdv_services/core/CMakeLists.txt @@ -49,13 +49,14 @@ add_library(core_services SHARED "toml_parser_util.h" "installation_manifest.h" "installation_manifest.cpp" + "../../global/tracefifo/trace_fifo.cpp" "local_shutdown_request.h" "iso_monitor.h" "iso_monitor.cpp" "installation_composer.h" "installation_composer.cpp" - "toml_parser/lexer_toml_token.h" "toml_parser/lexer_toml_token.cpp" "toml_parser/miscellaneous.h" "toml_parser/miscellaneous.cpp" "toml_parser/code_snippet.h" "toml_parser/code_snippet.cpp" "app_settings.h" "app_settings.cpp" "app_config_file.h" "app_config_file.cpp") + "toml_parser/lexer_toml_token.h" "toml_parser/lexer_toml_token.cpp" "toml_parser/miscellaneous.h" "toml_parser/miscellaneous.cpp" "toml_parser/code_snippet.h" "toml_parser/code_snippet.cpp" "app_settings.h" "app_settings.cpp" "app_config_file.h" "app_config_file.cpp" "toml_parser/parser_node_indexer.h" "toml_parser/parser_node_indexer.cpp" "permission_control.h" "permission_control.cpp") # Compiler settiings add_compile_definitions(SDV_NO_EXPORT_DEFINITION) diff --git a/sdv_services/core/app_config.cpp b/sdv_services/core/app_config.cpp index 7cb921a..4a3c398 100644 --- a/sdv_services/core/app_config.cpp +++ b/sdv_services/core/app_config.cpp @@ -33,61 +33,6 @@ CAppConfig& GetAppConfig() return app_config; } -// GetCoreDirectory might have been redirected for unit tests. -#ifndef GetCoreDirectory -std::filesystem::path GetCoreDirectory() -{ - static std::filesystem::path pathCoreDir; - if (!pathCoreDir.empty()) - return pathCoreDir; - -#ifdef _WIN32 - // Windows specific - std::wstring ssPath(32768, '\0'); - - MEMORY_BASIC_INFORMATION sMemInfo{}; - if (!VirtualQuery(&pathCoreDir, &sMemInfo, sizeof(sMemInfo))) - return pathCoreDir; - DWORD dwLength = GetModuleFileNameW(reinterpret_cast(sMemInfo.AllocationBase), ssPath.data(), 32767); - ssPath.resize(dwLength); - pathCoreDir = std::filesystem::path(ssPath); - return pathCoreDir.remove_filename(); -#elif __linux__ - // Read the maps file. It contains all loaded SOs. - std::ifstream fstream("/proc/self/maps"); - std::stringstream sstreamMap; - sstreamMap << fstream.rdbuf(); - std::string ssMap = sstreamMap.str(); - if (ssMap.empty()) - return pathCoreDir; // Some error - - // Find the "core_services.sdv" - size_t nPos = ssMap.find("core_services.sdv"); - if (nPos == std::string::npos) - return pathCoreDir; - size_t nEnd = nPos; - - // Find the start... runbackwards until the beginning of the line and remember the earliest occurance of a slash - size_t nBegin = 0; - while (nPos && ssMap[nPos] != '\n') - { - if (ssMap[nPos] == '/') - nBegin = nPos; - nPos--; - } - if (!nBegin) - nBegin = nPos; - - // Return the path - pathCoreDir = ssMap.substr(nBegin, nEnd - nBegin); - - return pathCoreDir; -#else - #error The OS is not supported! -#endif -} -#endif // !defined GetCoreDirectory - bool CAppConfig::LoadInstallationManifests() { // Check for allowance @@ -96,13 +41,14 @@ bool CAppConfig::LoadInstallationManifests() { case sdv::app::EAppContext::main: case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::external: case sdv::app::EAppContext::maintenance: bServerApp = true; default: break; } - std::filesystem::path pathCore = GetCoreDirectory(); + std::filesystem::path pathCore = GetAppSettings().GetFrameworkDir(); std::filesystem::path pathExe = GetExecDirectory(); std::filesystem::path pathInstall = GetAppSettings().GetInstallDir(); @@ -155,8 +101,8 @@ void CAppConfig::UnloadInstallatonManifests() bool CAppConfig::LoadAppConfigs() { - // Isolated applications do not load configurations - if (GetAppSettings().IsIsolatedApplication()) return false; + // Isolated and external applications do not load configurations + if (GetAppSettings().IsIsolatedApplication() || GetAppSettings().IsExternalApplication()) return false; // When running as server application, load the system configurations if (GetAppSettings().IsMainApplication() || GetAppSettings().IsMaintenanceApplication()) @@ -243,6 +189,7 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st { case sdv::app::EAppContext::main: case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::external: case sdv::app::EAppContext::maintenance: return sdv::core::EConfigProcessResult::failed; default: @@ -275,7 +222,7 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st //size_t nLoadable = 0; //size_t nNotLoadable = 0; //if (!GetAppSettings().IsMainApplication() && !GetAppSettings().IsIsolatedApplication() && - // !GetAppSettings().IsMaintenanceApplication()) + // !GetAppSettings().IsExternalApplication() && !GetAppSettings().IsMaintenanceApplication()) //{ // // Load all modules in the component section // auto ptrComponents = parser.Root().Direct("Component"); @@ -394,7 +341,7 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st // // loaded; get the module ID. // sdv::core::TObjectID tObjectID = 0; // if (!GetAppSettings().IsMainApplication() && !GetAppSettings().IsIsolatedApplication() && - // !GetAppSettings().IsMaintenanceApplication()) + // !GetAppSettings().IsExternalApplication() && !GetAppSettings().IsMaintenanceApplication()) // { // auto itModule = mapModules.find(pathModule); // if (itModule == mapModules.end()) continue; // Module was not loaded before... @@ -880,6 +827,7 @@ std::filesystem::path CAppConfig::FindInstalledModule(const std::filesystem::pat { case sdv::app::EAppContext::main: case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::external: case sdv::app::EAppContext::maintenance: bServerApp = true; default: @@ -914,6 +862,7 @@ std::string CAppConfig::FindInstalledModuleManifest(const std::filesystem::path& { case sdv::app::EAppContext::main: case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::external: case sdv::app::EAppContext::maintenance: bServerApp = true; default: @@ -948,6 +897,7 @@ std::optional CAppConfig::FindInstalledComponent(const std::str { case sdv::app::EAppContext::main: case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::external: case sdv::app::EAppContext::maintenance: bServerApp = true; default: @@ -980,6 +930,7 @@ bool CAppConfig::RemoveFromConfig(const CInstallManifest& /*rManifest*/) //{ //case sdv::app::EAppContext::main: //case sdv::app::EAppContext::isolated: + //case sdv::app::EAppContext::external: //case sdv::app::EAppContext::maintenance: // bServerApp = true; //default: @@ -1043,7 +994,7 @@ void CAppConfig::AddCurrentPath() if (!m_lstSearchPaths.empty()) return; // Add the core directory - std::filesystem::path pathCoreDir = GetCoreDirectory().lexically_normal(); + std::filesystem::path pathCoreDir = GetAppSettings().GetFrameworkDir().lexically_normal(); m_lstSearchPaths.push_back(pathCoreDir / "config/"); // Add the exe dir diff --git a/sdv_services/core/app_config.h b/sdv_services/core/app_config.h index 11b3575..9ea79df 100644 --- a/sdv_services/core/app_config.h +++ b/sdv_services/core/app_config.h @@ -39,12 +39,6 @@ // The installation might need companion files to be installed in various relative sub-directories. // @endcond -/** - * @brief Get the location of the core_services.sdv. - * @return Path to the directory containing the loaded core directory. - */ -std::filesystem::path GetCoreDirectory(); - /** * @brief Application configuration service * @details In the configuration system objects, devices, basic services, complex services and apps are defined and will be started diff --git a/sdv_services/core/app_config_file.cpp b/sdv_services/core/app_config_file.cpp index 48d64bd..ffe520d 100644 --- a/sdv_services/core/app_config_file.cpp +++ b/sdv_services/core/app_config_file.cpp @@ -346,9 +346,9 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml( // Modules leftover in the vector are added to the list auto vecModuleListCopy = m_vecModuleList; sdv::toml::CNodeCollection nodeModules = nodeConfig.GetDirect("Module"); - for (size_t nIndex = nodeModules.GetCount() - 1; nIndex < nodeModules.GetCount(); --nIndex) + for (int64_t iIndex = static_cast(nodeModules.GetCount() - 1); iIndex >= 0; --iIndex) { - sdv::toml::CNodeCollection tableModule = nodeModules.Get(nIndex); + sdv::toml::CNodeCollection tableModule = nodeModules.Get(static_cast(iIndex)); std::filesystem::path pathModule = tableModule.GetDirect("Path").GetValue().get(); auto itModule = std::find_if(vecModuleListCopy.begin(), vecModuleListCopy.end(), [&](const SModule& rsModule) { return pathModule == rsModule.pathModule; }); @@ -374,9 +374,9 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml( // Classes leftover in the vector are added to the list auto vecClassListCopy = m_vecClassList; sdv::toml::CNodeCollection arrayClasses = nodeConfig.GetDirect("Class"); - for (size_t nIndex = arrayClasses.GetCount() -1; nIndex < arrayClasses.GetCount(); --nIndex) + for (int64_t iIndex = static_cast(arrayClasses.GetCount() - 1); iIndex >= 0; --iIndex) { - sdv::toml::CNodeCollection tableClass = arrayClasses.Get(nIndex); + sdv::toml::CNodeCollection tableClass = arrayClasses.Get(static_cast(iIndex)); std::filesystem::path pathModule; if (!bServerApp) pathModule = tableClass.GetDirect("Path").GetValue().get(); @@ -419,8 +419,7 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml( if (ssExistingTOML != itClass->ssDefaultConfig) { if (tableParams) tableParams.Delete(); - if (!itClass->ssDefaultConfig.empty()) - tableClass.InsertTOML(sdv::toml::npos, itClass->ssDefaultConfig); + if (!itClass->ssDefaultConfig.empty()) tableClass.InsertTOML("", itClass->ssDefaultConfig); rbChanged = true; } @@ -433,8 +432,7 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml( tableClass.AddValue("Class", rsClass.ssName); if (!bServerApp) tableClass.AddValue("Path", std::filesystem::u8path(static_cast(rsClass.ssModulePath))); - if (!rsClass.ssDefaultConfig.empty()) - tableClass.InsertTOML(sdv::toml::npos, rsClass.ssDefaultConfig); + if (!rsClass.ssDefaultConfig.empty()) tableClass.InsertTOML("", rsClass.ssDefaultConfig); rbChanged = true; } @@ -444,9 +442,9 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml( // Components leftover in the vector are added to the list auto vecComponentListCopy = m_vecComponentList; sdv::toml::CNodeCollection nodeComponents = nodeConfig.GetDirect("Component"); - for (size_t nIndex = nodeComponents.GetCount() - 1; nIndex < nodeComponents.GetCount(); --nIndex) + for (int64_t iIndex = static_cast(nodeComponents.GetCount() - 1); iIndex >= 0; --iIndex) { - sdv::toml::CNodeCollection tableComponent = nodeComponents.Get(nIndex); + sdv::toml::CNodeCollection tableComponent = nodeComponents.Get(static_cast(iIndex)); std::filesystem::path pathModule; if (!bServerApp) pathModule = tableComponent.GetDirect("Path").GetValue().get(); @@ -518,8 +516,8 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml( else if (!tableParams) { // Simply add the parameters - tableParams = tableComponent.InsertTable(sdv::toml::npos, "Parameters"); - tableParams.InsertTOML(sdv::toml::npos, itComponent->ssParameterTOML); + tableParams = tableComponent.InsertTable("", "Parameters"); + tableParams.InsertTOML("", itComponent->ssParameterTOML); rbChanged = true; } } @@ -536,8 +534,8 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml( tableComponent.AddValue("Name", rsComponent.ssInstanceName); if (!rsComponent.ssParameterTOML.empty()) { - sdv::toml::CNodeCollection tableParams = tableComponent.InsertTable(sdv::toml::npos, "Parameters"); - tableParams.InsertTOML(sdv::toml::npos, rsComponent.ssParameterTOML); + sdv::toml::CNodeCollection tableParams = tableComponent.InsertTable("", "Parameters"); + tableParams.InsertTOML("", rsComponent.ssParameterTOML); } rbChanged = true; } @@ -628,12 +626,12 @@ bool CAppConfigFile::InsertComponent(size_t nIndex, const std::filesystem::path& // Need to add a group? if (prKey.first != ssGroup) { - group = root.InsertTable(sdv::toml::npos, prKey.first); + group = root.InsertTable("", prKey.first); ssGroup = prKey.first; } // Add the parameter - group.InsertValue(sdv::toml::npos, prKey.second, prParameter.second); + group.InsertValue("", prKey.second, prParameter.second); } ssParameterTOML = parser.GenerateTOML(); } diff --git a/sdv_services/core/app_config_file.h b/sdv_services/core/app_config_file.h index 02e6329..1e13a2f 100644 --- a/sdv_services/core/app_config_file.h +++ b/sdv_services/core/app_config_file.h @@ -266,6 +266,9 @@ public: */ void RemoveModule(const std::filesystem::path& rpathModule); + /** + * @brief Merge result enum. + */ enum class EMergeResult { successful, diff --git a/sdv_services/core/app_control.cpp b/sdv_services/core/app_control.cpp index dfd32fe..1ea881b 100644 --- a/sdv_services/core/app_control.cpp +++ b/sdv_services/core/app_control.cpp @@ -15,7 +15,6 @@ #include "module_control.h" #include "repository.h" #include "../../global/base64.h" -#include "../../global/tracefifo/trace_fifo.cpp" #include "toml_parser/parser_toml.h" #include "local_shutdown_request.h" #include "app_settings.h" @@ -49,6 +48,9 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac { m_pEvent = pEventHandler ? pEventHandler->GetInterface() : nullptr; + // Allow this thread full access + m_optpermission = GetPermissionControl().CreatePermissionObject(sdv::core::EAccessPermission::full_access); + // Intercept the logging... std::stringstream sstreamCOUT, sstreamCLOG, sstreamCERR; std::streambuf* pstreambufCOUT = std::cout.rdbuf(sstreamCOUT.rdbuf()); @@ -86,7 +88,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac } // Open the stream buffer and attach the streams if the application control is initialized as main app. - if (GetAppSettings().IsMainApplication()) + if (!GetAppSettings().RedirectMonitorToConsole() && GetAppSettings().IsMainApplication()) { m_fifoTraceStreamBuffer.SetInstanceID(GetAppSettings().GetInstanceID()); m_fifoTraceStreamBuffer.Open(1000); @@ -99,7 +101,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac std::cerr << sstreamCERR.str(); // Check for a correctly opened stream buffer - if (GetAppSettings().IsMainApplication() && !m_fifoTraceStreamBuffer.IsOpened()) + if (!GetAppSettings().RedirectMonitorToConsole() && GetAppSettings().IsMainApplication() && !m_fifoTraceStreamBuffer.IsOpened()) { if (!GetAppSettings().IsConsoleSilent()) std::cerr << "ERROR: Log streaming could not be initialized; cannot continue!" << std::endl; @@ -142,7 +144,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac }; auto fnCreateObject = [&ssErrorString](const sdv::u8string& rssClass, const sdv::u8string& rssObject, const sdv::u8string& rssConfig) -> bool { - bool bLocalRet = GetRepository().CreateObject2(rssClass, rssObject, rssConfig); + bool bLocalRet = GetRepository().CreateObject(rssClass, rssObject, rssConfig); if (!bLocalRet) { ssErrorString = std::string("Failed to instantiate a new object from class '") + rssClass + "'"; @@ -179,7 +181,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac sdv::IInterfaceAccess* pLoggerObj = GetRepository().GetObject(GetAppSettings().GetLoggerClass()); if (!pLoggerObj) { - GetRepository().DestroyObject2(GetAppSettings().GetLoggerClass()); + GetRepository().DestroyObject(GetAppSettings().GetLoggerClass()); if (!GetAppSettings().IsConsoleSilent()) std::cerr << "ERROR: Failed to start the logger. Cannot continue!" << std::endl; Shutdown(true); @@ -247,7 +249,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac } // Load the application settings. - if (!GetAppSettings().LoadSettingsFile()) + if (!GetAppSettings().LoadSettings()) { if (!GetAppSettings().IsConsoleSilent()) std::cerr << "ERROR: Failed to load application settings file. Cannot continue!" << std::endl; @@ -275,32 +277,88 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac bRet = fnLoadModule("hardware_ident.sdv") ? true : false; if (bRet) bRet = fnCreateObject("HardwareIdentificationService", "", ""); - // Load shared memory channel - if (bRet) bRet = fnLoadModule("ipc_shared_mem.sdv") ? true : false; - if (bRet) bRet = fnCreateObject("DefaultSharedMemoryChannelControl", "", ""); + // Load default communication provider + if (bRet) bRet = fnCreateObject(GetAppSettings().GetDefaultComProvider(), "", ""); // Load IPC service if (bRet) bRet = fnLoadModule("ipc_com.sdv") ? true : false; if (bRet) bRet = fnCreateObject("CommunicationControl", "", ""); - // Load IPC service and create listener local connections - if (bRet) bRet = fnLoadModule("ipc_listener.sdv") ? true : false; - if (bRet) bRet = fnCreateObject("ConnectionListenerService", "", R"code([Listener] -Type = "Local" -)code"); - - // Load IPC service - if (bRet) bRet = fnLoadModule("ipc_connect.sdv") ? true : false; - if (bRet) bRet = fnCreateObject("ConnectionService", "", ""); - // Load proxy/stub for core interfaces if (bRet) bRet = fnLoadModule("core_ps.sdv") ? true : false; -// // Start the listener -// if (bRet) bRet = fnLoadModule("ipc_listener.sdv") ? true : false; -// if (bRet) bRet = GetRepository().CreateObject("ConnectionListenerService", "ConnectionListenerService", R"code([Listener] -//Type="local" -//Instance=)code" + std::to_string(GetInstanceID())); + // Load the connection services + if (bRet) bRet = fnLoadModule("ipc_listener.sdv") ? true : false; + if (bRet) bRet = fnLoadModule("ipc_connect.sdv") ? true : false; + + // For the main application start the configured listeners. + if (bRet) + { + auto seqListeners = GetAppSettings().GetListeners(); + for (const auto& rssListener : seqListeners) + { + if (!bRet) break; + auto ssListenerConfig = GetAppSettings().GetListenerConfig(rssListener); + bRet = fnCreateObject("ListenerConnectService", "Listener_" + rssListener, ssListenerConfig); + } + } + + // For the main application connect the configured connections (except default connection). + if (bRet) + { + auto seqConnections = GetAppSettings().GetConnections(); + for (const auto& rssConnection : seqConnections) + { + if (!bRet) break; + if (rssConnection == "Default") continue; + + if (!GetAppSettings().IsConsoleSilent()) + std::cout << "INFO: Trying to connect to " << rssConnection << "..." << std::endl; + + auto ssConnectionConfig = GetAppSettings().GetConnectionConfig(rssConnection); + std::string ssObjectName = "Client_" + rssConnection; + bRet = fnCreateObject("ClientConnectService", ssObjectName, ssConnectionConfig); + + // Try connect (for at least nTries) and when successful bind repositories + sdv::TInterfaceAccessPtr ptrClient = GetRepository().GetObject(ssObjectName); + sdv::com::IClientConnect* pClientConnect = ptrClient.GetInterface(); + if (pClientConnect) + { + for (uint32_t uiCnt = 0; uiCnt < GetAppSettings().GetConnectRetries(); uiCnt++) + { + // Try to connect. If not working, wait for 300 ms + if (pClientConnect->Connect()) break; + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } + + if (pClientConnect->IsConnected()) + { + // Register the core repo as repo access + sdv::core::TLinkID tLinkID = GetRepository().LinkCoreRepository(pClientConnect->GetRemoteRepository()); + + if (!GetAppSettings().IsConsoleSilent()) + std::cout << "INFO: Connection established... " << std::endl; + + // Store the connection object. + m_vecConnections.push_back(std::make_pair(ssObjectName, tLinkID)); + } + else + { + ssErrorString = "Not connected..."; + if (!GetAppSettings().IsConsoleSilent()) + std::cout << "ERROR: Failed to connect to " << ssConnectionConfig << std::endl; + bRet = false; + } + } + else + { + ssErrorString = "No client connect interface..."; + if (!GetAppSettings().IsConsoleSilent()) + std::cerr << "ERROR: Cannot create connection object for " << rssConnection << std::endl; + bRet = false; + } + } + } if (!bRet) { @@ -314,25 +372,69 @@ Type = "Local" } else if (bRet && bLoadRPCClient) { - // Interpret the connect string... - - // Load hardware identification bRet = fnLoadModule("hardware_ident.sdv") ? true : false; if (bRet) bRet = fnCreateObject("HardwareIdentificationService", "", ""); - // Load shared memory channel - if (bRet) bRet = fnLoadModule("ipc_shared_mem.sdv") ? true : false; - if (bRet) bRet = fnCreateObject("DefaultSharedMemoryChannelControl", "", ""); + // Load default communication provider + if (bRet) bRet = fnCreateObject(GetAppSettings().GetDefaultComProvider(), "", ""); // Load IPC service if (bRet) bRet = fnLoadModule("ipc_com.sdv") ? true : false; if (bRet) bRet = fnCreateObject("CommunicationControl", "", ""); - // Load IPC service - if (bRet) bRet = fnLoadModule("ipc_connect.sdv") ? true : false; - if (bRet) bRet = fnCreateObject("ConnectionService", "", ""); + // Connect to the core system when running as external aplication. Use the default connection for this. + if (bRet && GetAppSettings().GetContextType() == sdv::app::EAppContext::external) + { + // Load the connection services + bRet = fnLoadModule("ipc_connect.sdv") ? true : false; + if (!GetAppSettings().IsConsoleSilent()) + std::cout << "INFO: Trying to connect to the core system..." << std::endl; + + // And the one connection + auto ssConnectionConfig = GetAppSettings().GetConnectionConfig("Default"); + if (bRet && !ssConnectionConfig.empty()) + bRet = fnCreateObject("ClientConnectService", "Client_Default", ssConnectionConfig); + + // Try connect (for at least nTries) and when successful bind repositories + sdv::TInterfaceAccessPtr ptrClient = GetRepository().GetObject("Client_Default"); + sdv::com::IClientConnect* pClientConnect = ptrClient.GetInterface(); + if (pClientConnect) + { + for (uint32_t uiCnt = 0; uiCnt < GetAppSettings().GetConnectRetries(); uiCnt++) + { + // Try to connect. If not working, wait for 300 ms + if (pClientConnect->Connect()) break; + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } + + if (pClientConnect->IsConnected()) + { + // Register the core repo as repo access + sdv::core::TLinkID tLinkID = GetRepository().LinkCoreRepository(pClientConnect->GetRemoteRepository()); + + if (!GetAppSettings().IsConsoleSilent()) + std::cout << "INFO: Connection established... " << std::endl; + + // Store the connection object. + m_vecConnections.push_back(std::make_pair("Default", tLinkID)); + } + else + { + if (!GetAppSettings().IsConsoleSilent()) + std::cout << "ERROR: Failed to connect to the core system" << std::endl; + bRet = false; + } + } + else + { + if (!GetAppSettings().IsConsoleSilent()) + std::cerr << "ERROR: Cannot create connection object for connecting to the core system." << std::endl; + bRet = false; + } + } + // Load proxy/stub for core interfaces if (bRet) bRet = fnLoadModule("core_ps.sdv") ? true : false; @@ -428,6 +530,7 @@ void CAppControl::RunLoop() { case sdv::app::EAppContext::main: case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::external: bLocal = false; break; case sdv::app::EAppContext::maintenance: @@ -490,8 +593,22 @@ void CAppControl::Shutdown(/*in*/ bool bForce) // Disable automatic configuration saving. m_bAutoSaveConfig = false; - // Update the application settings file - GetAppSettings().SaveSettingsFile(); + // TODO EVE: This is likely not wanted + //// Update the application settings file + //GetAppSettings().SaveSettings(); + + // Disconnect all automatic connections. + for (const auto& rprConnectObject : m_vecConnections) + { + // If there is a registered link ID, unlink the ID. + if (rprConnectObject.second) + GetRepository().UnlinkCoreRepository(rprConnectObject.second); + + sdv::TInterfaceAccessPtr ptrClient = GetRepository().GetObject(rprConnectObject.first); + sdv::com::IClientConnect* pClientConnect = ptrClient.GetInterface(); + if (pClientConnect) pClientConnect->Disconnect(); + } + m_vecConnections.clear(); // Destroy all objects... this should also remove any registered services, except the custom logger. GetRepository().DestroyAllObjects(std::vector({GetAppSettings().GetLoggerClass()}), bForce); @@ -537,7 +654,7 @@ void CAppControl::Shutdown(/*in*/ bool bForce) } // End trace streaming - if (GetAppSettings().IsMainApplication()) + if (!GetAppSettings().RedirectMonitorToConsole() && GetAppSettings().IsMainApplication()) { std::cout << "**********************************************" << std::endl; @@ -551,6 +668,7 @@ void CAppControl::Shutdown(/*in*/ bool bForce) m_bAutoSaveConfig = false; m_bEnableAutoSave = false; GetAppSettings().Reset(); + m_optpermission.reset(); } void CAppControl::RequestShutdown() diff --git a/sdv_services/core/app_control.h b/sdv_services/core/app_control.h index 39ed3af..0e8a032 100644 --- a/sdv_services/core/app_control.h +++ b/sdv_services/core/app_control.h @@ -18,6 +18,8 @@ #include #include #include "../../global/tracefifo/trace_fifo.h" +#include "permission_control.h" +#include /** * @brief Application control class. @@ -140,17 +142,20 @@ private: */ void BroadcastOperationState(sdv::app::EAppOperationState eState); - sdv::app::EAppOperationState m_eState = sdv::app::EAppOperationState::not_started; ///< The current operation state. - sdv::app::IAppEvent* m_pEvent = nullptr; ///< Pointer to the app event interface. - sdv::core::TModuleID m_tLoggerModuleID = 0; ///< ID of the logger module. - bool m_bEnableAutoSave = false; ///< When set and when enabled in the system settings, allows - ///< the automatic saving of the configuration. - bool m_bRunLoop = false; ///< Used to detect end of running loop function. - std::filesystem::path m_pathLockFile; ///< Lock file path name. - FILE* m_pLockFile = nullptr; ///< Lock file to test for other instances. - CTraceFifoStdBuffer m_fifoTraceStreamBuffer; ///< Trace stream buffer to redirect std::log, std::out and - ///< std::err when running as service. - bool m_bAutoSaveConfig = false; ///< System setting for automatic saving of the user configuration. + sdv::app::EAppOperationState m_eState = sdv::app::EAppOperationState::not_started; ///< The current operation state. + sdv::app::IAppEvent* m_pEvent = nullptr; ///< Pointer to the app event interface. + sdv::core::TModuleID m_tLoggerModuleID = 0; ///< ID of the logger module. + bool m_bEnableAutoSave = false; ///< When set and when enabled in the system settings, allows + ///< the automatic saving of the configuration. + bool m_bRunLoop = false; ///< Used to detect end of running loop function. + std::filesystem::path m_pathLockFile; ///< Lock file path name. + FILE* m_pLockFile = nullptr; ///< Lock file to test for other instances. + CTraceFifoStdBuffer m_fifoTraceStreamBuffer; ///< Trace stream buffer to redirect std::log, std::out and + ///< std::err when running as service. + bool m_bAutoSaveConfig = false; ///< System setting for automatic saving of the user configuration. + std::vector> m_vecConnections; ///< Connection objects that were successfully + ///< connected. + std::optional m_optpermission; ///< Main access permission for the core. }; /** diff --git a/sdv_services/core/app_settings.cpp b/sdv_services/core/app_settings.cpp index ddf69d2..fb2a7b6 100644 --- a/sdv_services/core/app_settings.cpp +++ b/sdv_services/core/app_settings.cpp @@ -17,252 +17,12 @@ #include "../../global/exec_dir_helper.h" #include "../../global/ipc_named_mutex.h" #include +#include -CAppSettings& GetAppSettings() -{ - static CAppSettings app_settings; - return app_settings; -} - -CAppSettings::CAppSettings() -{} - -CAppSettings::~CAppSettings() -{} - -bool CAppSettings::ProcessAppStartupConfig(const sdv::u8string& rssConfig) -{ - toml_parser::CParser parserStartupConfig; - std::string ssError; - try - { - // Read the configuration - if (!parserStartupConfig.Process(rssConfig)) - return false; - } - catch (const sdv::toml::XTOMLParseException& rexcept) - { - ssError = std::string("ERROR: Failed to parse application configuration: ") + rexcept.what(); - } - - sdv::toml::CNodeCollection tableStartupConfig(&parserStartupConfig.Root()); - - // Get the reporting settings (if this succeeded at all...) - m_bSilent = tableStartupConfig.GetDirect("Console.Report").GetValue().get() == "Silent"; - m_bVerbose = tableStartupConfig.GetDirect("Console.Report").GetValue().get() == "Verbose"; - - // Report the outstanding error (if there is one...) - if (!ssError.empty()) - { - if (!m_bSilent) - std::cerr << ssError << std::endl; - return false; - } - - // Allow a custom logger to be defined - m_pathLoggerModule = tableStartupConfig.GetDirect("LogHandler.Path").GetValue().get(); - m_ssLoggerClass = tableStartupConfig.GetDirect("LogHandler.Class").GetValue().get(); - if (m_ssLoggerClass.empty()) - m_ssLoggerClass = "DefaultLoggerService"; - - // Get an optional program tag for the logger - m_ssProgramTag = tableStartupConfig.GetDirect("LogHandler.Tag").GetValue().get(); - - // Get the application-mode - std::string ssApplication = tableStartupConfig.GetDirect("Application.Mode").GetValue(); - if (ssApplication.empty()) ssApplication = "Standalone"; - if (ssApplication == "Standalone") - m_eContextMode = sdv::app::EAppContext::standalone; - else if (ssApplication == "External") - m_eContextMode = sdv::app::EAppContext::external; - else if (ssApplication == "Isolated") - m_eContextMode = sdv::app::EAppContext::isolated; - else if (ssApplication == "Main") - m_eContextMode = sdv::app::EAppContext::main; - else if (ssApplication == "Essential") - m_eContextMode = sdv::app::EAppContext::essential; - else if (ssApplication == "Maintenance") - m_eContextMode = sdv::app::EAppContext::maintenance; - else - { - if (!m_bSilent) - std::cerr << "ERROR: Failed to process startup config: invalid application-mode specified for core library: " - << ssApplication << std::endl; - return false; - } - - // Get the severity level filter for the logger - auto fnTranslateSevFilter = [this](const std::string& rssLogFilter, const sdv::core::ELogSeverity eDefault) - { - sdv::core::ELogSeverity eSeverityFilter = eDefault; - if (rssLogFilter == "Trace") - eSeverityFilter = sdv::core::ELogSeverity::trace; - else if (rssLogFilter == "Debug") - eSeverityFilter = sdv::core::ELogSeverity::debug; - else if (rssLogFilter == "Info") - eSeverityFilter = sdv::core::ELogSeverity::info; - else if (rssLogFilter == "Warning") - eSeverityFilter = sdv::core::ELogSeverity::warning; - else if (rssLogFilter == "Error") - eSeverityFilter = sdv::core::ELogSeverity::error; - else if (rssLogFilter == "Fatal") - eSeverityFilter = sdv::core::ELogSeverity::fatal; - else if (!rssLogFilter.empty()) - { - if (!m_bSilent) - std::cerr << "ERROR: Failed to process application log: invalid severity level filter: '" << rssLogFilter << "'" - << std::endl; - } - return eSeverityFilter; - }; - sdv::core::ELogSeverity eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::error; - if (IsMainApplication() || IsIsolatedApplication()) eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::info; - m_eSeverityFilter = fnTranslateSevFilter(tableStartupConfig.GetDirect("LogHandler.Filter").GetValue(), - sdv::core::ELogSeverity::info); - m_eSeverityViewFilter = fnTranslateSevFilter( - tableStartupConfig.GetDirect("LogHandler.ViewFilter").GetValue(), eLogDefaultViewSeverityFilter); - - // Get the optional instance ID. - sdv::any_t anyInstanceID = tableStartupConfig.GetDirect("Application.Instance").GetValue(); - if (anyInstanceID) m_uiInstanceID = anyInstanceID; - else - m_uiInstanceID = 1000u; - - // Number of attempts to establish a connection to a running instance. - m_uiRetries = tableStartupConfig.GetDirect("Application.Retries").GetValue(); - if (m_uiRetries > 30) - m_uiRetries = 30; - else if (m_uiRetries < 3) - m_uiRetries = 3; - - // Main and isolated apps specific information. - if (IsMainApplication() || IsIsolatedApplication() || IsMaintenanceApplication()) - { - // Get the optional installation directory. - m_pathRootDir = tableStartupConfig.GetDirect("Application.InstallDir").GetValueAsPath(); - if (!m_pathRootDir.empty()) - { - if (m_pathRootDir.is_relative()) - m_pathRootDir = GetExecDirectory() / m_pathRootDir; - } - else - m_pathRootDir = GetExecDirectory(); - m_pathInstallDir = m_pathRootDir / std::to_string(m_uiInstanceID); - try - { - std::filesystem::create_directories(m_pathInstallDir); - } - catch (const std::filesystem::filesystem_error& rexcept) - { - if (!m_bSilent) - { - std::cerr << "Cannot create installation directory: " << m_pathInstallDir << std::endl; - std::cerr << " Reason: " << rexcept.what() << std::endl; - } - return false; - } - } - - // Maintenance, main and isolated applications cannot load specific configs. The others can specify a configuration file, but - // not auto-updateable. - if (!IsMainApplication() && !IsMaintenanceApplication() && !IsIsolatedApplication()) - m_pathUserConfig = tableStartupConfig.GetDirect("Application.Config").GetValueAsPath(); - - // Read the settings... if existing. And only for the main application - if (IsMainApplication() && !LoadSettingsFile()) - return false; - - return true; -} - -bool CAppSettings::LoadSettingsFile() -{ - // Check for the proper context - switch (m_eContextMode) - { - case sdv::app::EAppContext::main: - case sdv::app::EAppContext::isolated: - case sdv::app::EAppContext::maintenance: - break; - default: - return true; // Not an error... - } - - // If the template is not existing, this is not an error... - if (!std::filesystem::exists(m_pathInstallDir / "settings.toml")) - return true; - - std::ifstream fstream(m_pathInstallDir / "settings.toml"); - std::string ssSettings((std::istreambuf_iterator(fstream)), std::istreambuf_iterator()); - fstream.close(); - - try - { - // Read the configuration - toml_parser::CParser parserSettings(ssSettings); - - // If there is no "Settings" table, this is not an error... - sdv::toml::CNodeCollection tableSettings(parserSettings.Root().GetNodeDirect("Settings")); - if (!tableSettings) return true; - - // Check for the version - uint32_t uiVersion = tableSettings.GetDirect("Version").GetValue(); - if (uiVersion != SDVFrameworkInterfaceVersion) - { - if (!m_bSilent) - std::cerr << "ERROR: Invalid version of application settings file (expected version " - << SDVFrameworkInterfaceVersion << ", but available version " << uiVersion << ")" << std::endl; - return false; - } - - // Get the platform config - but only when not specified over the app-control-config. - if (m_pathPlatformConfig.empty()) - m_pathPlatformConfig = tableSettings.GetDirect("PlatformConfig").GetValueAsPath(); - - // Get the vehicle interface config - but only when not specified over the app-control-config. - if (m_pathVehIfcConfig.empty()) - m_pathVehIfcConfig = tableSettings.GetDirect("VehIfcConfig").GetValueAsPath(); - - // Get the vehicle abstraction config - but only when not specified over the app-control-config. - if (m_pathVehAbstrConfig.empty()) - m_pathVehAbstrConfig = tableSettings.GetDirect("VehAbstrConfig").GetValueAsPath(); - - // Get the application config - but only when not specified over the app-control-config. - if (m_pathUserConfig.empty()) - m_pathUserConfig = tableSettings.GetDirect("AppConfig").GetValueAsPath(); - } - catch (const sdv::toml::XTOMLParseException& rexcept) - { - if (!m_bSilent) - std::cerr << "ERROR: Failed to parse application settings: " << rexcept.what() << std::endl; - return false; - } - return true; -} - -bool CAppSettings::SaveSettingsFile() -{ - // Check for the proper context - switch (m_eContextMode) - { - case sdv::app::EAppContext::main: - case sdv::app::EAppContext::isolated: - case sdv::app::EAppContext::maintenance: - break; - default: - return true; // Not an error... - } - - // Protect against multiple write actions at the same time. - ipc::named_mutex mtx("LockSdvAppSettings_" + std::to_string(m_uiInstanceID)); - // Warning of cppcheck for locking a local mutex, which doesn't have any effect. Since this is a named mutex between - // applciations, the warning is not correct. Suppress warning. - // cppcheck-suppress localMutex - std::unique_lock lock(mtx); - - const std::string ssSettingsTemplate = R"toml(# Settings file +const std::string g_ssSettingsTemplate = R"toml(# Settings file [Settings] -Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml( +Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + + R"toml( # The system configuration is divided into: # platform config - containing all the components needed to interact with the OS, @@ -290,22 +50,533 @@ VehAbstrConfig = "" # config by providing the "AppConfig" keyword as a string value. A relative path is relative to # the installation directory (being "exe_location/instance_id"). # -# Example +# Example: # AppConfig = "app_config.toml" AppConfig = "" + +# A list of zero or more listener definitions that should be instantiated during startup of the +# main application. If no listener definition is available, the default shared-memory listener +# is being instantiated. +# +# Example: +# [[Settings.Listener]] +# Name = "SharedMemListener" +# [Settings.Listerner.Provider] +# Name = "DefaultSharedMemory" +# [Settings.Listener.IpcChannel] +# Name = "CHANNEL_1234" +# Size = 1024 +# +# A list of zero or more client connections that should be instantiated during startup. +# +# Example: +# [[Settings.Connection]] +# Name = "SharedMemConnection" +# [Settings.Connection.Provider] +# Name = "DefaultSharedMemory" +# [Settings.Listener.IpcChannel] +# Name = "CHANNEL_1234" + )toml"; + + +CAppSettings& GetAppSettings() +{ + static CAppSettings app_settings; + return app_settings; +} + +CAppSettings::CAppSettings() +{ +#ifdef _WIN32 + // Windows specific + std::wstring ssPath(32768, '\0'); + + MEMORY_BASIC_INFORMATION sMemInfo{}; + if (VirtualQuery(&m_pathFrameworkDir, &sMemInfo, sizeof(sMemInfo))) + { + DWORD dwLength = GetModuleFileNameW(reinterpret_cast(sMemInfo.AllocationBase), ssPath.data(), 32767); + ssPath.resize(dwLength); + m_pathFrameworkDir = std::filesystem::path(ssPath).remove_filename(); + } + +#elif __linux__ + // Read the maps file. It contains all loaded SOs. + std::ifstream fstream("/proc/self/maps"); + std::stringstream sstreamMap; + sstreamMap << fstream.rdbuf(); + std::string ssMap = sstreamMap.str(); + if (!ssMap.empty()) + { + // Find the "core_services.sdv" + size_t nPos = ssMap.find("core_services.sdv"); + if (nPos != std::string::npos) + { + size_t nEnd = nPos; + + // Find the start... runbackwards until the beginning of the line and remember the earliest occurance of a slash + size_t nBegin = 0; + while (nPos && ssMap[nPos] != '\n') + { + if (ssMap[nPos] == '/') nBegin = nPos; + nPos--; + } + if (!nBegin) nBegin = nPos; + + // Return the path + m_pathFrameworkDir = ssMap.substr(nBegin, nEnd - nBegin); + } +} +#else + #error The OS is not supported! +#endif +} + +CAppSettings::~CAppSettings() +{} + +bool CAppSettings::ProcessAppStartupConfig(const sdv::u8string& rssConfig) +{ + // Initialize the parameter map + InitParamMap(); + + // Process configuration + toml_parser::CParser parserStartupConfig; + std::string ssError; + try + { + // Read the configuration + if (!parserStartupConfig.Process(rssConfig)) + return false; + } + catch (const sdv::toml::XTOMLParseException& rexcept) + { + ssError = std::string("ERROR: Failed to parse application configuration: ") + rexcept.what(); + } + + sdv::toml::CNodeCollection tableStartupConfig(&parserStartupConfig.Root()); + + // Get the reporting settings (if this succeeded at all...) + std::string ssConsoleRerporting = tableStartupConfig.GetDirect("Console.Report").GetValueAsString(); + if (ssConsoleRerporting == "Silent") + m_eConsoleReporting = EAppConsoleReporting::silent; + if (ssConsoleRerporting == "Verbose") + m_eConsoleReporting = EAppConsoleReporting::verbose; + + // Get the application-mode + std::string ssApplication = tableStartupConfig.GetDirect("Application.Mode").GetValue(); + if (ssApplication.empty()) + ssApplication = "Standalone"; + if (ssApplication == "Standalone") + m_eAppContextType = sdv::app::EAppContext::standalone; + else if (ssApplication == "External") + m_eAppContextType = sdv::app::EAppContext::external; + else if (ssApplication == "Isolated") + m_eAppContextType = sdv::app::EAppContext::isolated; + else if (ssApplication == "Main") + m_eAppContextType = sdv::app::EAppContext::main; + else if (ssApplication == "Essential") + m_eAppContextType = sdv::app::EAppContext::essential; + else if (ssApplication == "Maintenance") + m_eAppContextType = sdv::app::EAppContext::maintenance; + else + { + if (!IsConsoleSilent()) + std::cerr << "ERROR: Failed to process startup config: invalid application-mode specified for core library: " + << ssApplication << std::endl; + return false; + } + + // Redirect monitoring? Only for main application + if (m_eAppContextType == sdv::app::EAppContext::main) + m_bRedirectMon = tableStartupConfig.GetDirect("Console.RedirectMon"); + + // Report the outstanding error (if there is one...) + if (!ssError.empty()) + { + if (!IsConsoleSilent()) + std::cerr << ssError << std::endl; + return false; + } + + // Allow a custom logger to be defined + m_pathLoggerModule = tableStartupConfig.GetDirect("LogHandler.Path").GetValue().get(); + m_ssLoggerClass = tableStartupConfig.GetDirect("LogHandler.Class").GetValue().get(); + if (m_ssLoggerClass.empty()) + m_ssLoggerClass = "DefaultLoggerService"; + + // Get an optional program tag for the logger + m_ssProgramTag = tableStartupConfig.GetDirect("LogHandler.Tag").GetValue().get(); + + // Get the severity level filter for the logger + auto fnTranslateSevFilter = [this](const std::string& rssLogFilter, sdv::core::ELogSeverity eDefaultSeverity) -> std::optional + { + if (rssLogFilter.empty()) return eDefaultSeverity; + else if (rssLogFilter == "Trace") return sdv::core::ELogSeverity::trace; + else if (rssLogFilter == "Debug") return sdv::core::ELogSeverity::debug; + else if (rssLogFilter == "Info") return sdv::core::ELogSeverity::info; + else if (rssLogFilter == "Warning") return sdv::core::ELogSeverity::warning; + else if (rssLogFilter == "Error") return sdv::core::ELogSeverity::error; + else if (rssLogFilter == "Fatal") return sdv::core::ELogSeverity::fatal; + + if (!IsConsoleSilent()) + std::cerr << "ERROR: Failed to process application log: invalid severity level filter: '" << rssLogFilter << "'" + << std::endl; + return {}; + }; + sdv::core::ELogSeverity eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::error; + switch (m_eAppContextType) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::external: + eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::info; + break; + default: + break; + } + auto optFilter = fnTranslateSevFilter(tableStartupConfig.GetDirect("LogHandler.Filter").GetValue(), + sdv::core::ELogSeverity::info); + if (!optFilter) return false; + m_eSeverityFilter = *optFilter; + optFilter = fnTranslateSevFilter(tableStartupConfig.GetDirect("LogHandler.ViewFilter").GetValue(), + eLogDefaultViewSeverityFilter); + if (!optFilter) return false; + m_eSeverityViewFilter = *optFilter; + + // Get the optional instance ID. + sdv::any_t anyInstanceID = tableStartupConfig.GetDirect("Application.Instance").GetValue(); + if (anyInstanceID) m_uiInstanceID = anyInstanceID; + else + m_uiInstanceID = 1000u; + + // Number of attempts to establish a connection to a running instance. + sdv::toml::CNode nodeRetries = tableStartupConfig.GetDirect("Connections.Retries"); + if (nodeRetries) + { + m_uiConnectRetries = nodeRetries.GetValue(); + if (m_uiConnectRetries > 30) + m_uiConnectRetries = 30; + else if (m_uiConnectRetries < 3) + m_uiConnectRetries = 3; + } + + // Main and isolated apps specific information. + if (IsMainApplication() || IsIsolatedApplication() || IsExternalApplication() || IsMaintenanceApplication()) + { + // Get the optional installation directory - either from the startup parameters (relative to exe), or the environment + // variable (relative to framework) or the exe when nothing else is specified. + m_pathRootDir = tableStartupConfig.GetDirect("Application.InstallDir").GetValueAsPath(); + if (!m_pathRootDir.empty() && m_pathRootDir.is_relative()) + m_pathRootDir = GetExecDirectory() / m_pathRootDir; + if (m_pathRootDir.empty()) + { +#ifdef _WIN32 + const wchar_t* szInstallDir = _wgetenv(L"SDV_COMPONENT_INSTALL"); + if (!szInstallDir) szInstallDir = _wgetenv(L"SDV_FRAMEWORK_RUNTIME"); + if (szInstallDir) m_pathRootDir = szInstallDir; +#elif defined __unix__ + const char* szInstallDir = getenv("SDV_COMPONENT_INSTALL"); + if (!szInstallDir) szInstallDir = getenv("SDV_FRAMEWORK_RUNTIME"); + if (szInstallDir) m_pathRootDir = std::filesystem::u8path(szInstallDir); +#else + #error OS not supported! +#endif + } + if (!m_pathRootDir.empty() && m_pathRootDir.is_relative()) + m_pathRootDir = GetExecDirectory() / m_pathRootDir; + if (m_pathRootDir.empty()) + m_pathRootDir = GetExecDirectory(); + + // Derive installation directory from the root directory + m_pathInstallDir = m_pathRootDir / std::to_string(m_uiInstanceID); + try + { + std::filesystem::create_directories(m_pathInstallDir); + } + catch (const std::filesystem::filesystem_error& rexcept) + { + if (!IsConsoleSilent()) + { + std::cerr << "Cannot create installation directory: " << m_pathInstallDir << std::endl; + std::cerr << " Reason: " << rexcept.what() << std::endl; + } + return false; + } + } + + // Set the default communication provider to shared memory. This might be overwritten by the settings. + m_ssDefaultComProvider = "DefaultSharedMemory"; + + // Read the settings... if existing. + // Maintenance, main and isolated applications cannot load specific configs. The others can specify a configuration file, but + // not auto-updateable. + switch (m_eAppContextType) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::external: + case sdv::app::EAppContext::maintenance: + if (!LoadSettings()) return false; + break; + default: + m_pathUserConfig = tableStartupConfig.GetDirect("Application.Config").GetValueAsPath(); + break; + } + + // The applications use the SDV_FRAMEWORK_RUNTIME, SDV_COMPONENT_INSTALL, SDV_FRAMEWORK_DEV_TOOLS and SDV_FRAMEWORK_DEV_INCLUDE + // environment variables to locate the core components. The core can, however, be executed in an executable from a different + // location. If the environment variable contains a relative path, the path to the core components is relative to the + // executable. Since the core can start other executables, the path would be relative to the other executables, which mights + // reside in a different location than the executable starting the core. Change the paths in the environment variables to an + // absolute path to prevent an invalid path to the core components. + auto fnMakeAbsolutePathEnvVariable = [](const std::string& rssVariable) + { + std::filesystem::path pathEnv; +#ifdef _WIN32 + const wchar_t* szEnvPath = _wgetenv(sdv::MakeWString(rssVariable).c_str()); + if (szEnvPath) + pathEnv = szEnvPath; +#elif defined __unix__ + const char* szEnvPath = getenv(rssVariable.c_str()); + if (szEnvPath) + pathEnv = szEnvPath; +#else + #error The OS is not supported! +#endif + if (!pathEnv.empty() && pathEnv.is_relative()) + { + pathEnv = (GetExecDirectory() / pathEnv).lexically_normal(); +#ifdef _WIN32 + // NOTE: In windows there are two environment variable stacks which need to be updated. + std::ignore = SetEnvironmentVariable(sdv::MakeWString(rssVariable).c_str(), pathEnv.native().c_str()); + std::ignore = _wputenv((sdv::MakeWString(rssVariable) + L"=" + pathEnv.native()).c_str()); +#elif defined __unix__ + std::ignore = setenv(rssVariable.c_str(), pathEnv.generic_u8string().c_str(), 1); +#else + #error The OS is not supported! +#endif + } + }; + fnMakeAbsolutePathEnvVariable("SDV_FRAMEWORK_RUNTIME"); + fnMakeAbsolutePathEnvVariable("SDV_COMPONENT_INSTALL"); + fnMakeAbsolutePathEnvVariable("SDV_FRAMEWORK_DEV_TOOLS"); + fnMakeAbsolutePathEnvVariable("SDV_FRAMEWORK_DEV_INCLUDE"); + + return true; +} + +bool CAppSettings::LoadSettings() +{ + // Check for the proper context + switch (m_eAppContextType) + { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::external: + case sdv::app::EAppContext::maintenance: + break; + default: + return true; // Not an error... + } + + // If the template is not existing, this is not an error... use the default template + std::string ssSettings; + if (!std::filesystem::exists(m_pathInstallDir / "settings.toml")) + ssSettings = g_ssSettingsTemplate; + else + { + std::ifstream fstream(m_pathInstallDir / "settings.toml"); + ssSettings = std::string((std::istreambuf_iterator(fstream)), std::istreambuf_iterator()); + fstream.close(); + } + + try + { + // Read the configuration + toml_parser::CParser parserSettings(ssSettings); + + // If there is no "Settings" table, this is not an e rror... + sdv::toml::CNodeCollection tableSettings(parserSettings.Root().GetNodeDirect("Settings")); + if (!tableSettings) return true; + + // Check for the version + uint32_t uiVersion = tableSettings.GetDirect("Version").GetValue(); + if (uiVersion != SDVFrameworkInterfaceVersion) + { + if (!IsConsoleSilent()) + std::cerr << "ERROR: Invalid version of application settings file (expected version " + << SDVFrameworkInterfaceVersion << ", but available version " << uiVersion << ")" << std::endl; + return false; + } + + // Get the platform config - but only when not specified over the app-control-config. + if (m_pathPlatformConfig.empty()) + m_pathPlatformConfig = tableSettings.GetDirect("PlatformConfig").GetValueAsPath(); + + // Get the vehicle interface config - but only when not specified over the app-control-config. + if (m_pathVehIfcConfig.empty()) + m_pathVehIfcConfig = tableSettings.GetDirect("VehIfcConfig").GetValueAsPath(); + + // Get the vehicle abstraction config - but only when not specified over the app-control-config. + if (m_pathVehAbstrConfig.empty()) + m_pathVehAbstrConfig = tableSettings.GetDirect("VehAbstrConfig").GetValueAsPath(); + + // Get the application config - but only when not specified over the app-control-config. + if (m_pathUserConfig.empty()) + m_pathUserConfig = tableSettings.GetDirect("AppConfig").GetValueAsPath(); + + // Reset the default communication provider. + m_ssDefaultComProvider.clear(); + + // Read the listener configurations + m_mapListeners.clear(); + sdv::toml::CNodeCollection arrayListeners = tableSettings.GetDirect("Listener"); + for (size_t nIndex = 0; nIndex < arrayListeners.GetCount(); nIndex++) + { + // Get the listener configuration + sdv::toml::CNodeCollection tableListener = arrayListeners.Get(nIndex); + if (!tableListener.IsValid()) + { + if (!IsConsoleSilent()) std::cerr << "ERROR: Invalid listener configuration element." << std::endl; + continue; + } + + // Get the configuration elements + std::string ssName = tableListener.GetDirect("Name").GetValueAsString(); + std::string ssProvider = tableListener.GetDirect("Provider").GetTOML(); + std::string ssProviderName = tableListener.GetDirect("Provider.Name").GetValueAsString(); + std::string ssChannelConfig = tableListener.GetDirect("IpcChannel").GetTOML(); + if (ssName.empty()) + { + if (!IsConsoleSilent()) std::cerr << "ERROR: Missing listener name." << std::endl; + continue; + } + if (ssProvider.empty() || ssProviderName.empty()) + { + if (!IsConsoleSilent()) + std::cerr << "ERROR: Missing listener provider name." << std::endl; + continue; + } + std::stringstream sstreamConfig; + sstreamConfig << "[Provider]" << std::endl << ssProvider << std::endl << "[IpcChannel]" << std::endl << + ssChannelConfig << std::endl; + if (!AddListenerConfig(ssName, sstreamConfig.str())) + { + if (!IsConsoleSilent()) + std::cerr << "ERROR: Could not add listener configuration (likely duplicate configuration)." << std::endl; + continue; + } + + // If running as main application, use the default config to extract the default communication provider. + if (m_eAppContextType == sdv::app::EAppContext::main && ssName == "Default") + m_ssDefaultComProvider = ssProviderName; + } + + // Is there a default listener. If not, add one based on the default shared memory component. + if (m_mapListeners.find("Default") == m_mapListeners.end()) + AddListenerConfig("Default", DefaultListenerConfig()); + + // Read the connection configurations + m_vecConnections.clear(); + sdv::toml::CNodeCollection arrayConnections = tableSettings.GetDirect("Connection"); + for (size_t nIndex = 0; nIndex < arrayConnections.GetCount(); nIndex++) + { + // Get the listener configuration + sdv::toml::CNodeCollection tableConnection = arrayConnections.Get(nIndex); + if (!tableConnection.IsValid()) + { + if (!IsConsoleSilent()) + std::cerr << "ERROR: Invalid connection configuration element." << std::endl; + continue; + } + + // Get the configuration elements + std::string ssName = tableConnection.GetDirect("Name").GetValueAsString(); + std::string ssProvider = tableConnection.GetDirect("Provider").GetTOML(); + std::string ssProviderName = tableConnection.GetDirect("Provider.Name").GetValueAsString(); + std::string ssChannelConfig = tableConnection.GetDirect("IpcChannel").GetTOML(); + if (ssName.empty()) + { + if (!IsConsoleSilent()) + std::cerr << "ERROR: Missing connection name." << std::endl; + continue; + } + if (ssProvider.empty() || ssProviderName.empty()) + { + if (!IsConsoleSilent()) + std::cerr << "ERROR: Missing connection provider name." << std::endl; + continue; + } + std::stringstream sstreamConfig; + sstreamConfig << "[Provider]" << std::endl << ssProvider << std::endl << "[IpcChannel]" << std::endl + << ssChannelConfig << std::endl; + if (!AddConnectionConfig(ssName, sstreamConfig.str())) + { + if (!IsConsoleSilent()) + std::cerr << "ERROR: Could not add connection configuration (likely duplicate configuration)." << std::endl; + continue; + } + + // If not running as main application, use the default config to extract the default communication provider. + if (m_eAppContextType != sdv::app::EAppContext::main && ssName == "Default") + m_ssDefaultComProvider = ssProviderName; + + } + + // Is there a default connection (only valid for external applications). If not, add one based on the default shared memory. + auto itConnection = std::find_if(m_vecConnections.begin(), + m_vecConnections.end(), + [](const auto& rprConnection) { return rprConnection.first == "Default"; }); + if (itConnection == m_vecConnections.end()) + AddConnectionConfig("Default", DefaultConnectionConfig(), + m_vecConnections.size() ? m_vecConnections.front().first : ""); + + // If there is no default communication provider, use the default shared memory provider. + if (m_ssDefaultComProvider.empty()) + m_ssDefaultComProvider = "DefaultSharedMemory"; + } + catch (const sdv::toml::XTOMLParseException& rexcept) + { + if (!IsConsoleSilent()) + std::cerr << "ERROR: Failed to parse application settings: " << rexcept.what() << std::endl; + return false; + } + return true; +} + +bool CAppSettings::SaveSettings() +{ + // Check for the proper context + switch (m_eAppContextType) + { + case sdv::app::EAppContext::maintenance: + break; + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + default: + return true; // Not an error... + } + + // Protect against multiple write actions at the same time. + ipc::named_mutex mtx("LockSdvAppSettings_" + std::to_string(m_uiInstanceID)); + // Warning of cppcheck for locking a local mutex, which doesn't have any effect. Since this is a named mutex between + // applciations, the warning is not correct. Suppress warning. + // cppcheck-suppress localMutex + std::unique_lock lock(mtx); + // If the template is not existing, create a default settings file... std::string ssSettings; bool bChangeDetected = false; if (!std::filesystem::exists(m_pathInstallDir / "settings.toml")) { bChangeDetected = true; - ssSettings = std::move(ssSettingsTemplate); - m_bPlatformConfig = true; - m_bVehIfcConfig = true; - m_bVehAbstrConfig = true; - m_bUserConfig = true; + ssSettings = std::move(g_ssSettingsTemplate); + m_bUpdatePlatformConfig = true; + m_bUpdateVehIfcConfig = true; + m_bUpdateVehAbstrConfig = true; + m_bUpdateUserConfig = true; } else { @@ -313,7 +584,7 @@ AppConfig = "" std::ifstream fstream(m_pathInstallDir / "settings.toml"); if (!fstream.is_open()) { - if (!m_bSilent) + if (!IsConsoleSilent()) std::cerr << "ERROR: Cannot open the application settings file." << std::endl; return false; } @@ -322,9 +593,9 @@ AppConfig = "" ssSettings = std::string((std::istreambuf_iterator(fstream)), std::istreambuf_iterator()); if (ssSettings.empty()) { - if (!m_bSilent) + if (!IsConsoleSilent()) std::cerr << "ERROR: Cannot read the application settings file; will use default." << std::endl; - ssSettings = std::move(ssSettingsTemplate); + ssSettings = std::move(g_ssSettingsTemplate); bChangeDetected = true; } } @@ -338,7 +609,7 @@ AppConfig = "" sdv::toml::CNodeCollection tableRoot(&parserSettings.Root()); if (!tableRoot) { - if (!m_bSilent) + if (!IsConsoleSilent()) std::cerr << "ERROR: Invalid TOML file '" << (m_pathInstallDir / "settings.toml").generic_u8string() << "'" << std::endl; @@ -349,7 +620,7 @@ AppConfig = "" tableSettings = tableRoot.AddTable("Settings"); if (!tableSettings) { - if (!m_bSilent) + if (!IsConsoleSilent()) std::cerr << "ERROR: Invalid 'Settings' table." << std::endl; return false; } @@ -357,7 +628,7 @@ AppConfig = "" uint32_t uiVersion = tableSettings.GetDirect("Version").GetValue(); if (uiVersion != SDVFrameworkInterfaceVersion) { - if (!m_bSilent) + if (!IsConsoleSilent()) std::cerr << "ERROR: Invalid version of application settings file (expected version " << SDVFrameworkInterfaceVersion << ", but available version " << uiVersion << ")" << std::endl; return false; @@ -366,26 +637,26 @@ AppConfig = "" // Generic update config file function auto fnUpdateConfig = [&](const std::string &rssConfigKey, const std::filesystem::path& rpathConfigFile) { - sdv::toml::CNode nodeUserConfig = tableSettings.GetDirect(rssConfigKey); - if (nodeUserConfig.GetValue().empty()) + sdv::toml::CNode nodeConfig = tableSettings.GetDirect(rssConfigKey); + if (nodeConfig.GetValue().empty()) { bChangeDetected = true; - if (nodeUserConfig) - nodeUserConfig.Delete(); - nodeUserConfig = tableSettings.AddValue(rssConfigKey, rpathConfigFile); - if (!nodeUserConfig) + if (nodeConfig) + nodeConfig.Delete(); + nodeConfig = tableSettings.AddValue(rssConfigKey, rpathConfigFile); + if (!nodeConfig) { - if (!m_bSilent) std::cerr << "ERROR: Cannot insert the \"Settings." << rssConfigKey << + if (!IsConsoleSilent()) std::cerr << "ERROR: Cannot insert the \"Settings." << rssConfigKey << "\" value; cannot process further." << std::endl; return false; } } - else if (nodeUserConfig.GetValue() != rpathConfigFile) + else if (nodeConfig.GetValue() != rpathConfigFile) { bChangeDetected = true; - if (!nodeUserConfig.SetValue(rpathConfigFile)) + if (!nodeConfig.SetValue(rpathConfigFile)) { - if (!m_bSilent) std::cerr << "ERROR: Cannot update the \"Settings." << rssConfigKey << + if (!IsConsoleSilent()) std::cerr << "ERROR: Cannot update the \"Settings." << rssConfigKey << "\" value; cannot process further." << std::endl; return false; } @@ -394,10 +665,203 @@ AppConfig = "" }; // Update the configuration path values. - if (m_bPlatformConfig && !fnUpdateConfig("PlatformConfig", m_pathPlatformConfig)) return false; - if (m_bVehIfcConfig && !fnUpdateConfig("VehIfcConfig", m_pathVehIfcConfig)) return false; - if (m_bVehAbstrConfig && !fnUpdateConfig("VehAbstrConfig", m_pathVehAbstrConfig)) return false; - if (m_bUserConfig && !fnUpdateConfig("AppConfig", m_pathUserConfig)) return false; + if (m_bUpdatePlatformConfig && !fnUpdateConfig("PlatformConfig", m_pathPlatformConfig)) return false; + if (m_bUpdateVehIfcConfig && !fnUpdateConfig("VehIfcConfig", m_pathVehIfcConfig)) return false; + if (m_bUpdateVehAbstrConfig && !fnUpdateConfig("VehAbstrConfig", m_pathVehAbstrConfig)) return false; + if (m_bUpdateUserConfig && !fnUpdateConfig("AppConfig", m_pathUserConfig)) return false; + + // Update the listener configuration - do not add "Default" config + sdv::toml::CNode nodeListeners = tableSettings.GetDirect("Listener"); + if (nodeListeners && nodeListeners.GetType() != sdv::toml::ENodeType::node_array) + { + nodeListeners.Delete(); + bChangeDetected = true; + } + + // Check whether each listener in the array is also present in the map. + // - if not or if different, remove it from the array + // - remove it from the map if identical + sdv::toml::CNodeCollection arrayListeners(nodeListeners); + auto mapListenersCopy = m_mapListeners; + size_t nListenerCount = arrayListeners.GetCount(); // Do not request anything from arrayListeners during the for + // loop. When deleting the last entry, the array is deleted as well, + // rendering the the interface pointer to void. + for (size_t nIndex = 0; nIndex < nListenerCount; ++nIndex) + { + // Find the listener + sdv::toml::CNode nodeListener = arrayListeners.Get(nIndex); + + // Check for a correct listener + if (nodeListener.GetType() != sdv::toml::ENodeType::node_table) + { + nodeListener.Delete(); + nIndex--; + nListenerCount--; + bChangeDetected = true; + continue; + } + + // Check for an identical listener + sdv::toml::CNodeCollection tableListener = nodeListener; + if (!tableListener) break; // Should not occur + + // Get the configuration elements + std::string ssStoredName = tableListener.GetDirect("Name").GetValueAsString(); + std::string ssStoredProvider = tableListener.GetDirect("Provider").GetTOML(); + std::string ssStoredChannelConfig = tableListener.GetDirect("IpcChannel").GetTOML(); + std::stringstream sstreamStoredConfig; + sstreamStoredConfig << "[Provider]" << std::endl << ssStoredProvider << std::endl << "[IpcChannel]" << std::endl + << ssStoredChannelConfig << std::endl; + auto itListener = mapListenersCopy.find(ssStoredName); + std::string ssConfig; + if (itListener != mapListenersCopy.end()) + ssConfig = itListener->second; + + // Check for identical configuration + if (!toml_parser::CompareEqual(ssConfig, sstreamStoredConfig.str())) + { + // Config is different the entry and try again. + nodeListener.Delete(); + nIndex--; + nListenerCount--; + bChangeDetected = true; + continue; + } + + // The node and the map entry are identical. Remove this entry from the map. + mapListenersCopy.erase(itListener); + } + + // Add each leftover listener from the map to the array. + for (const auto& rvtListener : mapListenersCopy) + { + // Skip the "Default" configuration + if (rvtListener.first == "Default" && rvtListener.second == DefaultListenerConfig()) continue; + sdv::toml::CNodeCollection tableListener = tableSettings.AddTableArray("Listener"); + + // Add config + tableListener.AddValue("Name", rvtListener.first); + tableListener.AddTOML(rvtListener.second); + bChangeDetected = true; + } + + // Update the connection configuration - do not add "Default" config + sdv::toml::CNode nodeConnections = tableSettings.GetDirect("Connection"); + if (nodeConnections && nodeConnections.GetType() != sdv::toml::ENodeType::node_array) + { + nodeConnections.Delete(); + bChangeDetected = true; + } + + // Check whether each connection in the array is also present in the vector + // - if not or if different, remove it from the array + sdv::toml::CNodeCollection arrayConnections(nodeConnections); + size_t nConnectionCount = arrayConnections.GetCount(); // Do not request anything from arrayConnections during the + // for-loop. When deleting the last entry, the array is deleted as + // well, rendering the the interface pointer to void. + for (size_t nIndex = 0; nIndex < nConnectionCount; ++nIndex) + { + // Find the connection + sdv::toml::CNode nodeConnection = arrayConnections.Get(nIndex); + + // Check for a correct connection + if (nodeConnection.GetType() != sdv::toml::ENodeType::node_table) + { + nodeConnection.Delete(); + nIndex--; + nConnectionCount--; + bChangeDetected = true; + continue; + } + + // Check for an identical connection + sdv::toml::CNodeCollection tableConnection = nodeConnection; + if (!tableConnection) break; // Should not occur + + // Get the configuration elements + std::string ssStoredName = tableConnection.GetDirect("Name").GetValueAsString(); + std::string ssStoredProvider = tableConnection.GetDirect("Provider").GetTOML(); + std::string ssStoredChannelConfig = tableConnection.GetDirect("IpcChannel").GetTOML(); + std::stringstream sstreamStoredConfig; + sstreamStoredConfig << "[Provider]" << std::endl << ssStoredProvider << std::endl << "[IpcChannel]" << std::endl << + ssStoredChannelConfig << std::endl; + auto itConnection = std::find_if(m_vecConnections.begin(), m_vecConnections.end(), [&](const auto& rprConnection) + { return rprConnection.first == ssStoredName; }); + std::string ssConfig; + if (itConnection != m_vecConnections.end()) + ssConfig = itConnection->second; + + // Check for identical configuration + if (!toml_parser::CompareEqual(ssConfig, sstreamStoredConfig.str())) + { + // Config is different. Remove the entry and try again. + nodeConnection.Delete(); + nIndex--; + nConnectionCount--; + bChangeDetected = true; + continue; + } + } + + // Request the array once more. Could be deleted in the mean time. + nodeConnections = tableSettings.GetDirect("Connection"); + arrayConnections = sdv::toml::CNodeCollection(nodeConnections); + + // Check for each connection in the vector if present at the correct position in the map + // - if not correct position, take the TOML and remove and reinsert at the correct position + // - if not available, add to the array + //size_t nParentIndex = 0; + size_t nOffset = 0; + for (size_t nIndex = 0; nIndex < m_vecConnections.size(); ++nIndex) + { + // Skip the "Default" configuration + if (m_vecConnections[nIndex].first == "Default" && m_vecConnections[nIndex].second == DefaultConnectionConfig()) + { + nOffset++; + continue; + } + + + // Get the connection name + std::string ssName = m_vecConnections[nIndex].first; + + // Check index + std::string ssToml; + bool bFoundAndCorrect = false; + for (size_t nIndex2 = nIndex - nOffset; nIndex2 < arrayConnections.GetCount(); ++nIndex2) + { + sdv::toml::CNodeCollection tableConnection(arrayConnections.Get(nIndex2)); + std::string ssStoredName = tableConnection.GetDirect("Name").GetValueAsString(); + if (ssStoredName == ssName) + { + if (nIndex2 != nIndex - nOffset) + ssToml = tableConnection.GetTOML(); + else + bFoundAndCorrect = true; + break; + } + } + + // Found and correct? Process next + if (bFoundAndCorrect) continue; + + // Is there a TOML? Then add a connection with the TOML + // Otherwise create a new connection + if (!ssToml.empty()) + arrayConnections.InsertTOML("", ssToml); + else + { + sdv::toml::CNodeCollection tableConnection; + if(arrayConnections) + tableConnection = arrayConnections.InsertTable(arrayConnections.GetNodeNameByIndex(0), "Connection"); + else + tableConnection = tableSettings.InsertTableArray("", "Connection"); + tableConnection.AddValue("Name", ssName); + tableConnection.AddTOML(m_vecConnections[nIndex].second); + } + + bChangeDetected = true; + } // Save the settings file if needed if (bChangeDetected) @@ -405,21 +869,21 @@ AppConfig = "" std::ofstream fstream(m_pathInstallDir / "settings.toml", std::ios::trunc); if (!fstream.is_open()) { - if (!m_bSilent) + if (!IsConsoleSilent()) std::cerr << "ERROR: Cannot write the application settings file." << std::endl; return false; } fstream << parserSettings.GenerateTOML(); - m_bPlatformConfig = false; - m_bVehIfcConfig = false; - m_bVehAbstrConfig = false; - m_bUserConfig = false; + m_bUpdatePlatformConfig = false; + m_bUpdateVehIfcConfig = false; + m_bUpdateVehAbstrConfig = false; + m_bUpdateUserConfig = false; } } catch (const sdv::toml::XTOMLParseException& rexcept) { - if (!m_bSilent) + if (!IsConsoleSilent()) std::cerr << "ERROR: Failed to parse application settings: " << rexcept.what() << std::endl; return false; } @@ -428,37 +892,47 @@ AppConfig = "" bool CAppSettings::IsMainApplication() const { - return m_eContextMode == sdv::app::EAppContext::main; + return m_eAppContextType == sdv::app::EAppContext::main; } bool CAppSettings::IsIsolatedApplication() const { - return m_eContextMode == sdv::app::EAppContext::isolated; + return m_eAppContextType == sdv::app::EAppContext::isolated; } bool CAppSettings::IsStandaloneApplication() const { - return m_eContextMode == sdv::app::EAppContext::standalone; + return m_eAppContextType == sdv::app::EAppContext::standalone; } bool CAppSettings::IsEssentialApplication() const { - return m_eContextMode == sdv::app::EAppContext::essential; + return m_eAppContextType == sdv::app::EAppContext::essential; } bool CAppSettings::IsMaintenanceApplication() const { - return m_eContextMode == sdv::app::EAppContext::maintenance; + return m_eAppContextType == sdv::app::EAppContext::maintenance; } bool CAppSettings::IsExternalApplication() const { - return m_eContextMode == sdv::app::EAppContext::external; + return m_eAppContextType == sdv::app::EAppContext::external; +} + +void CAppSettings::SetContextType(sdv::app::EAppContext eContextType) +{ + m_eAppContextType = eContextType; } sdv::app::EAppContext CAppSettings::GetContextType() const { - return m_eContextMode; + return m_eAppContextType; +} + +void CAppSettings::SetInstanceID(uint32_t uiID) +{ + m_uiInstanceID = uiID; } uint32_t CAppSettings::GetInstanceID() const @@ -466,11 +940,6 @@ uint32_t CAppSettings::GetInstanceID() const return m_uiInstanceID; } -uint32_t CAppSettings::GetRetries() const -{ - return m_uiRetries; -} - std::string CAppSettings::GetLoggerClass() const { return m_ssLoggerClass; @@ -498,12 +967,32 @@ sdv::core::ELogSeverity CAppSettings::GetConsoleSeverityFilter() const bool CAppSettings::IsConsoleSilent() const { - return m_bSilent; + return m_eConsoleReporting == EAppConsoleReporting::silent; } bool CAppSettings::IsConsoleVerbose() const { - return m_bVerbose; + return m_eConsoleReporting == EAppConsoleReporting::verbose; +} + +void CAppSettings::SetConsoleReporting(EAppConsoleReporting eReporting) +{ + m_eConsoleReporting = eReporting; +} + +CAppSettings::EAppConsoleReporting CAppSettings::GetConsoleReporting() const +{ + return m_eConsoleReporting; +} + +bool CAppSettings::RedirectMonitorToConsole() const +{ + return m_bRedirectMon; +} + +std::filesystem::path CAppSettings::GetFrameworkDir() const +{ + return m_pathFrameworkDir; } std::filesystem::path CAppSettings::GetRootDir() const @@ -530,29 +1019,28 @@ std::vector CAppSettings::GetSystemConfigPaths() const std::filesystem::path CAppSettings::GetConfigPath(EConfigType eType) const { - // Is running as main application? + // Is running as main or maintenance application? if (!IsMainApplication() && !IsMaintenanceApplication()) return {}; switch (eType) { case EConfigType::platform_config: if (!m_pathPlatformConfig.empty()) return m_pathPlatformConfig; - return "platform.toml"; + return m_bUpdatePlatformConfig ? "" : "platform.toml"; case EConfigType::vehicle_interface_config: if (!m_pathVehIfcConfig.empty()) return m_pathVehIfcConfig; - return "vehicle_ifc.toml"; + return m_bUpdateVehIfcConfig ? "" : "vehicle_ifc.toml"; case EConfigType::vehicle_abstraction_config: if (!m_pathVehAbstrConfig.empty()) return m_pathVehAbstrConfig; - return "vehicle_abstract.toml"; + return m_bUpdateVehAbstrConfig ? "" : "vehicle_abstract.toml"; case EConfigType::user_config: if (!m_pathUserConfig.empty()) return m_pathUserConfig; - return "app_config.toml"; + return m_bUpdateUserConfig ? "" : "app_config.toml"; default: return {}; } } - bool CAppSettings::EnableConfig(EConfigType eType) { // Is running as main application? @@ -561,22 +1049,22 @@ bool CAppSettings::EnableConfig(EConfigType eType) switch (eType) { case EConfigType::platform_config: - m_bPlatformConfig = true; + m_bUpdatePlatformConfig = true; if (!m_pathPlatformConfig.empty()) return true; m_pathPlatformConfig = "platform.toml"; break; case EConfigType::vehicle_interface_config: - m_bVehIfcConfig = true; + m_bUpdateVehIfcConfig = true; if (!m_pathVehIfcConfig.empty()) return true; m_pathVehIfcConfig = "vehicle_ifc.toml"; break; case EConfigType::vehicle_abstraction_config: - m_bVehAbstrConfig = true; + m_bUpdateVehAbstrConfig = true; if (!m_pathVehAbstrConfig.empty()) return true; m_pathVehAbstrConfig = "vehicle_abstract.toml"; break; case EConfigType::user_config: - m_bUserConfig = true; + m_bUpdateUserConfig = true; if (!m_pathUserConfig.empty()) return true; m_pathUserConfig = "app_config.toml"; break; @@ -596,19 +1084,19 @@ bool CAppSettings::DisableConfig(EConfigType eType) { case EConfigType::platform_config: m_pathPlatformConfig.clear(); - m_bPlatformConfig = true; + m_bUpdatePlatformConfig = true; break; case EConfigType::vehicle_interface_config: m_pathVehIfcConfig.clear(); - m_bVehIfcConfig = true; + m_bUpdateVehIfcConfig = true; break; case EConfigType::vehicle_abstraction_config: m_pathVehAbstrConfig.clear(); - m_bVehAbstrConfig = true; + m_bUpdateVehAbstrConfig = true; break; case EConfigType::user_config: m_pathUserConfig.clear(); - m_bUserConfig = true; + m_bUpdateUserConfig = true; break; default: return false; @@ -625,11 +1113,18 @@ std::filesystem::path CAppSettings::GetUserConfigPath() const bool CAppSettings::SetUserConfigPath(const std::filesystem::path& rpathConfig) { // Is running as server application? Then the user configuration is limited to a filename only. - if (IsMainApplication() || IsMaintenanceApplication() || IsIsolatedApplication()) + switch (m_eAppContextType) { + case sdv::app::EAppContext::main: + case sdv::app::EAppContext::isolated: + case sdv::app::EAppContext::external: + case sdv::app::EAppContext::maintenance: // Must be a filename only. if (!rpathConfig.has_filename() || rpathConfig.has_parent_path()) return false; + break; + default: + break; } // Assign the path @@ -649,63 +1144,165 @@ bool CAppSettings::RemoveUserConfigPath() return true; } -sdv::sequence CAppSettings::GetNames() const +const std::string& CAppSettings::GetDefaultComProvider() const { - sdv::sequence seqNames = {"app.instance_id", "console.info_level"}; - return seqNames; + return m_ssDefaultComProvider; } -sdv::any_t CAppSettings::Get(/*in*/ const sdv::u8string& ssAttribute) const +sdv::sequence CAppSettings::GetListeners() const { - if (ssAttribute == "app.instance_id") - return sdv::any_t(m_uiInstanceID); - if (ssAttribute == "console.info_level") - { - if (m_bSilent) - return "silent"; - if (m_bVerbose) - return "verbose"; - return "normal"; - } - return {}; + // Is running as main or maintenance application? + if (!IsMainApplication() && !IsMaintenanceApplication()) return {}; + + // Add the listener names + sdv::sequence seqListeners; + for (const auto& rvtListener : m_mapListeners) + seqListeners.push_back(rvtListener.first); + + return seqListeners; } -bool CAppSettings::Set(/*in*/ const sdv::u8string& /*ssAttribute*/, /*in*/ sdv::any_t /*anyAttribute*/) +sdv::u8string CAppSettings::GetListenerConfig(/*in*/ const sdv::u8string& ssName) const { - // Currently there are not setting attributes... - return false; + // Is running as main or maintenance application? + if (!IsMainApplication() && !IsMaintenanceApplication()) return {}; + + // Find the listener + auto itListener = m_mapListeners.find(ssName); + if (itListener == m_mapListeners.end()) return {}; + return itListener->second; } -uint32_t CAppSettings::GetFlags(/*in*/ const sdv::u8string& ssAttribute) const +bool CAppSettings::AddListenerConfig(/*in*/ const sdv::u8string& ssName, /*in*/ const sdv::u8string& ssConfig) { - if (ssAttribute == "app.instance_id") - return hlpr::flags(sdv::EAttributeFlags::read_only); - if (ssAttribute == "console.info_level") - return hlpr::flags(sdv::EAttributeFlags::read_only); - return 0u; + // Is running as main or maintenance application? + if (!IsMainApplication() && !IsMaintenanceApplication()) return {}; + + // Add the listener + m_mapListeners[ssName] = ssConfig; + + return true; +} + +bool CAppSettings::RemoveListenerConfig(/*in*/ const sdv::u8string& ssName) +{ + // Is running as main or maintenance application? + if (!IsMainApplication() && !IsMaintenanceApplication()) return false; + + // Erase a listener if existing + return m_mapListeners.erase(ssName) ? true : false; +} + +sdv::sequence CAppSettings::GetConnections() const +{ + // Is running as main, external or maintenance application? + if (!IsMainApplication() && !IsExternalApplication() && !IsMaintenanceApplication()) return {}; + + // Add the connection names + sdv::sequence seqConnections; + for (const auto& rprConnection : m_vecConnections) + seqConnections.push_back(rprConnection.first); + + return seqConnections; +} + +sdv::u8string CAppSettings::GetConnectionConfig(/*in*/ const sdv::u8string& ssName) const +{ + // Is running as main, external or maintenance application? + if (!IsMainApplication() && !IsExternalApplication() && !IsMaintenanceApplication()) return {}; + + // Find the connection + auto itConnection = std::find_if(m_vecConnections.begin(), m_vecConnections.end(), [&](const auto& rprConnection) + { return rprConnection.first == ssName; }); + if (itConnection == m_vecConnections.end()) return {}; + return itConnection->second; +} + +bool CAppSettings::AddConnectionConfig(/*in*/ const sdv::u8string& ssName, /*in*/ const sdv::u8string& ssConfig, + /*in*/ const sdv::u8string& ssInsertBefore /*= std::string()*/) +{ + // Is running as main, external or maintenance application? + if (!IsMainApplication() && !IsExternalApplication() && !IsMaintenanceApplication()) return {}; + + // Check for an existing connection; erase if existing. + auto itConnection = std::find_if(m_vecConnections.begin(), m_vecConnections.end(), [&](const auto& rprConnection) + { return rprConnection.first == ssName; }); + if (itConnection != m_vecConnections.end()) m_vecConnections.erase(itConnection); + + // Find the connection to insert before + auto itInsertBefore = std::find_if(m_vecConnections.begin(), m_vecConnections.end(), [&](const auto& rprConnection) + { return rprConnection.first == ssInsertBefore; }); + + // Add the connection + m_vecConnections.insert(itInsertBefore, std::make_pair(ssName, ssConfig)); + + return true; +} + +bool CAppSettings::RemoveConnectionConfig(/*in*/ const sdv::u8string& ssName) +{ + // Is running as main, external or maintenance application? + if (!IsMainApplication() && !IsExternalApplication() && !IsMaintenanceApplication()) return false; + + // Check for an existing connection + auto itConnection = std::find_if(m_vecConnections.begin(), m_vecConnections.end(), [&](const auto& rprConnection) + { return rprConnection.first == ssName; }); + if (itConnection != m_vecConnections.end()) + m_vecConnections.erase(itConnection); + else + return false; + return true; +} + +uint32_t CAppSettings::GetConnectRetries() const +{ + return m_uiConnectRetries; } void CAppSettings::Reset() { - m_eContextMode = sdv::app::EAppContext::no_context; + m_eAppContextType = sdv::app::EAppContext::no_context; m_pathLoggerModule.clear(); m_ssLoggerClass.clear(); m_ssProgramTag.clear(); m_eSeverityFilter = sdv::core::ELogSeverity::info; m_eSeverityViewFilter = sdv::core::ELogSeverity::error; m_uiInstanceID = 0u; - m_bSilent = false; - m_bVerbose = false; + m_uiConnectRetries = 5u; + m_eConsoleReporting = EAppConsoleReporting::normal; + m_bRedirectMon = false; m_pathRootDir.clear(); m_pathInstallDir.clear(); m_pathPlatformConfig.clear(); m_pathVehIfcConfig.clear(); m_pathVehAbstrConfig.clear(); m_pathUserConfig.clear(); - m_bPlatformConfig = false; - m_bVehIfcConfig = false; - m_bVehAbstrConfig = false; - m_bUserConfig = false; + m_bUpdatePlatformConfig = false; + m_bUpdateVehIfcConfig = false; + m_bUpdateVehAbstrConfig = false; + m_bUpdateUserConfig = false; + m_mapListeners.clear(); + m_vecConnections.clear(); + m_ssDefaultComProvider.clear(); +} + +std::string CAppSettings::DefaultListenerConfig() const +{ + return R"toml([Provider] +Name = "DefaultSharedMemory" +[IpcChannel] +Name = "DEFAULT_CHANNEL_)toml" + std::to_string(m_uiInstanceID) + R"toml(" +Size = 1024 +)toml"; +} + +std::string CAppSettings::DefaultConnectionConfig() const +{ + return R"toml([Provider] +Name = "DefaultSharedMemory" +[IpcChannel] +Name = "DEFAULT_CHANNEL_)toml" + std::to_string(m_uiInstanceID) + R"toml(" +)toml"; } CAppSettings& CAppSettingsService::GetAppSettings() diff --git a/sdv_services/core/app_settings.h b/sdv_services/core/app_settings.h index 1e53a87..716e719 100644 --- a/sdv_services/core/app_settings.h +++ b/sdv_services/core/app_settings.h @@ -62,6 +62,10 @@ * #Console output * [Console] * Report = "Silent" # Either "Silent", "Normal" or "Verbose" for no, normal or extensive messages. + * RedirectMon = true # When set, redirects messages to the console instead of the monitor application in main mode. + * + * [Connections] + * Retries = 5 # The amount of retries when trying to connect (value between 3 and 30, default is 5). * * # Search directories * @endcode @@ -103,11 +107,40 @@ * # Example * # AppConfig = "app_config.toml" * AppConfig = "" + * + * # A list of zero or more listener definitions that should be instantiated during startup of the + * # main application. If no listener definition is available, the default shared-memory listener + * # is being instantiated. + * # + * # [[Settings.Listener]] + * # Name = "" + * # [Settings.Listerner.Provider] + * # Name = "" + * # [Settings.Listener.IpcChannel] + * + * # A list of zero or more client connections that should be instantiated during startup. + * # + * # [[Settings.Connection]] + * # Name = "" + * # [Settings.Connection.Provider] + * # Name = "" + * # [Settings.Listener.IpcChannel] * @endcode */ -class CAppSettings : public sdv::IInterfaceAccess, public sdv::app::IAppContext, public sdv::IAttributes +class CAppSettings : public sdv::CSdvParamMap, public sdv::IInterfaceAccess, public sdv::app::IAppContext, + public sdv::app::IAppSettingsPersist, public sdv::app::IAppConnections { public: + /** + * @brief Console reporting. + */ + enum class EAppConsoleReporting + { + silent, ///< No reporting by application control (default) + normal, ///< Normal reporting by application control + verbose, ///< Extensive reporting by application control + }; + /** * @brief Constructor */ @@ -118,11 +151,71 @@ public: */ ~CAppSettings(); +#ifndef DOXYGEN_IGNORE // Interface map BEGIN_SDV_INTERFACE_MAP() SDV_INTERFACE_ENTRY(sdv::app::IAppContext) END_SDV_INTERFACE_MAP() + // Application mode labels + BEGIN_SDV_LABEL_MAP(sdv::app::EAppContext) + SDV_LABEL_ENTRY(sdv::app::EAppContext::no_context, "Undefined") + SDV_LABEL_ENTRY(sdv::app::EAppContext::standalone, "Standalone") + SDV_LABEL_ENTRY(sdv::app::EAppContext::external, "External") + SDV_LABEL_ENTRY(sdv::app::EAppContext::isolated, "Isolated") + SDV_LABEL_ENTRY(sdv::app::EAppContext::main, "Main") + SDV_LABEL_ENTRY(sdv::app::EAppContext::essential, "Essential") + SDV_LABEL_ENTRY(sdv::app::EAppContext::maintenance, "Maintenance") + END_SDV_LABEL_MAP() + + // Log severity labels + BEGIN_SDV_LABEL_MAP(sdv::core::ELogSeverity) + SDV_LABEL_ENTRY(sdv::core::ELogSeverity::trace, "Trace") + SDV_LABEL_ENTRY(sdv::core::ELogSeverity::debug, "Debug") + SDV_LABEL_ENTRY(sdv::core::ELogSeverity::info, "Info") + SDV_LABEL_ENTRY(sdv::core::ELogSeverity::warning, "Warning") + SDV_LABEL_ENTRY(sdv::core::ELogSeverity::error, "Error") + SDV_LABEL_ENTRY(sdv::core::ELogSeverity::fatal, "Fatal") + END_SDV_LABEL_MAP() + + // Console reporting labels + BEGIN_SDV_LABEL_MAP(EAppConsoleReporting) + SDV_LABEL_ENTRY(EAppConsoleReporting::normal, "Normal") + SDV_LABEL_ENTRY(EAppConsoleReporting::silent, "Silent") + SDV_LABEL_ENTRY(EAppConsoleReporting::verbose, "Verbose") + END_SDV_LABEL_MAP() + + // Parameter map + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_SET_READONLY() + SDV_PARAM_GROUP("LogHandler") + SDV_PARAM_ENTRY(m_ssLoggerClass, "Class", "", "", "Component class name of a custom logger") + SDV_PARAM_ENTRY(m_pathLoggerModule, "Path", "", "", "Component module path of a custom logger") + SDV_PARAM_ENTRY(m_ssProgramTag, "Tag", "", "", "Program tag to use instead of the name SDV_LOG_") + SDV_PARAM_ENUM_ENTRY(m_eSeverityFilter, "Filter", sdv::core::ELogSeverity::info, + "Lowest severity filter to use when logging (Trace, Debug, Info, Warning, Error, Fatal)") + SDV_PARAM_ENUM_ENTRY(m_eSeverityViewFilter, "ViewFilter", sdv::core::ELogSeverity::error, + "Lowest severity filter to use when logging (Trace, Debug, Info, Warning, Error, Fatal)") + + SDV_PARAM_GROUP("Application") + SDV_PARAM_ENUM_ENTRY(m_eAppContextType, "Mode", sdv::app::EAppContext::no_context, "Application mode") + SDV_PARAM_ENTRY(m_uiInstanceID, "Instance", 0u, "", "System instance ID") + SDV_PARAM_ENTRY(m_pathRootDir, "RootDir", "", "", "Location of user component root directory") + SDV_PARAM_ENTRY(m_pathInstallDir, "InstallDir", "", "", "Location of user component installations") + SDV_PARAM_ENTRY(m_pathPlatformConfig, "PlatformConfig", "", "", "The platform abstraction configuration file") + SDV_PARAM_ENTRY(m_pathVehIfcConfig, "VehIfcConfig", "", "", "The vehicle interface configuration file") + SDV_PARAM_ENTRY(m_pathVehAbstrConfig, "VehAbstrConfig", "", "", "The vehicle abstraction configuration file") + SDV_PARAM_ENTRY(m_pathUserConfig, "AppConfig", "", "", "The application configuration file") + + SDV_PARAM_GROUP("Console") + SDV_PARAM_ENUM_ENTRY(m_eConsoleReporting, "Reporting", EAppConsoleReporting::normal, "Console reporting (Normal, Silent, Verbose)") + SDV_PARAM_ENTRY(m_bRedirectMon, "RedirectMon", false, "", "Redirect messages from the monitor onto the console.") + + SDV_PARAM_GROUP("Communication") + SDV_PARAM_ENTRY(m_ssDefaultComProvider, "DefaultProvider", "", "", "Name of the default communication provider") + END_SDV_PARAM_MAP() +#endif // !defined DOXYGEN_IGNORE + /** * @brief Process the application starrtup configuration. * @param[in] rssConfig Reference to the configuration content (TOML format). @@ -131,19 +224,20 @@ public: bool ProcessAppStartupConfig(const sdv::u8string& rssConfig); /** - * @brief Load the application settings file. + * @brief Load the application settings file. Overload of sdv::app::IAppSettingsPersist::LoadSettings. * @attention Only works if the application is running in main, isolation or maintenance mode. * @remarks When there is no settings file, this is not an error. Default settings will be assumed. * @return Returns whether the loading was successful. */ - bool LoadSettingsFile(); + bool LoadSettings() override; /** * @brief Save the application settings file (or create when not existing yet). - * @attention Only works if the application is running in main, isolation or maintenance mode. + * Overload of sdv::app::IAppSettingsPersist::SaveSettings. + * @attention Only works if the application is running in maintenance mode. * @return Returns whether the saving was successful. */ - bool SaveSettingsFile(); + bool SaveSettings() override; /** * @brief Return whether the current application is the main application. @@ -181,27 +275,33 @@ public: */ bool IsExternalApplication() const; + /** + * @brief Set the application context type. + * @param[in] eContextType The application context type to set. + */ + void SetContextType(sdv::app::EAppContext eContextType); + /** * @brief Return the application context mode. Overload of sdv::app::IAppContext::GetContextType. * @return The context mode. */ sdv::app::EAppContext GetContextType() const override; + /** + * @brief Set the instance ID. + * @param uiID The instance ID to set. + */ + void SetInstanceID(uint32_t uiID); + /** * @brief Return the core instance ID. Overload of sdv::app::IAppContext::GetContextType. * @details Get the instance. If not otherwise specified, the current instance depends on whether the application is running - * as main or isolated application, in which case the instance is 1000. In all other cases the instance is 0. An instance - * ID can be supplied through the app startup configuration. + * as main, isolated or maintenance application, in which case the instance is 1000. In all other cases the instance is 0. An + * instance ID can be supplied through the app startup configuration. * @return The core instance ID. */ uint32_t GetInstanceID() const override; - /** - * @brief Return the number of retries to establish a connection. Overload of sdv::app::IAppContext::GetRetries. - * @return Number of retries. - */ - uint32_t GetRetries() const override; - /** * @brief Get the class name of a logger service, if specified in the application startup configuration. * @return The logger class name. @@ -244,6 +344,30 @@ public: */ bool IsConsoleVerbose() const; + /** + * @brief Set the application console reporting status. + * @param eReporting The console reporting status to set. + */ + void SetConsoleReporting(EAppConsoleReporting eReporting); + + /** + * @brief Get the current application console reporting status. + * @return The console reporting status of the application. + */ + EAppConsoleReporting GetConsoleReporting() const; + + /** + * @brief Redirect the messages monitored for main application onto the console. + * @return Returns whether redirection is switched on. + */ + bool RedirectMonitorToConsole() const; + + /** + * @brief Get the framework directory for the application. + * @return The location of framework directory. + */ + std::filesystem::path GetFrameworkDir() const; + /** * @brief Get the root directory for the application. * @remarks Is only valid when used in main, isolated and maintenance applications. @@ -279,6 +403,7 @@ public: /** * @brief Get the stored or default configuration path name. * @attention Setting a path is only valid when running as main application. + * @param[in] eType The configuration type to get the path for. * @return The path name dependent on the configuration type. If no path name was configured, the default path name is returned. */ std::filesystem::path GetConfigPath(EConfigType eType) const; @@ -326,33 +451,80 @@ public: bool RemoveUserConfigPath(); /** - * @brief Get a sequence with the available attribute names. Overload of sdv::IAttributes::GetNames. - * @return The sequence of attribute names. + * @brief Get the default communication provider (extracted from listener and connection settings). + * @return Name of the default communication provider. */ - virtual sdv::sequence GetNames() const override; + const std::string& GetDefaultComProvider() const; /** - * @brief Get the attribute value. Overload of sdv::IAttributes::Get. - * @param[in] ssAttribute Name of the attribute. - * @return The attribute value or an empty any-value if the attribute wasn't found or didn't have a value. + * @brief Get a sequence with listener names. Overload of sdv::app::IAppConnections::GetListeners. + * @return Sequence with listener name strings. */ - virtual sdv::any_t Get(/*in*/ const sdv::u8string& ssAttribute) const override; + sdv::sequence GetListeners() const override; /** - * @brief Set the attribute value. Overload of sdv::IAttributes::Set. - * @param[in] ssAttribute Name of the attribute. - * @param[in] anyAttribute Attribute value to set. - * @return Returns 'true' when setting the attribute was successful or 'false' when the attribute was not found or the - * attribute is read-only or another error occurred. + * @brief Get the listener configuration. Overload of sdv::app::IAppConnections::GetListenerConfig. + * @param[in] ssName Name of the listener. + * @return String containing the listener configuration. */ - virtual bool Set(/*in*/ const sdv::u8string& ssAttribute, /*in*/ sdv::any_t anyAttribute) override; + sdv::u8string GetListenerConfig(/*in*/ const sdv::u8string& ssName) const override; /** - * @brief Get the attribute flags belonging to a certain attribute. Overload of sdv::IAttributes::GetFlags. - * @param[in] ssAttribute Name of the attribute. - * @return Returns the attribute flags (zero or more EAttributeFlags flags) or 0 when the attribute could not be found. + * @brief Add or update a listener configuration. Overload of sdv::app::IAppConnections::AddListenerConfig. + * @remarks Only accessible when the application runs in maintenance mode. + * @param[in] ssName Name of the listener configuration. + * @param[in] ssConfig The configuration string for the listener. + * @return Returns whether the listener could be added (fails when the listener already exists). */ - virtual uint32_t GetFlags(/*in*/ const sdv::u8string& ssAttribute) const override; + bool AddListenerConfig(/*in*/ const sdv::u8string& ssName, /*in*/ const sdv::u8string& ssConfig) override; + + /** + * @brief Remove a listener configuration with the provided name. Overload of sdv::app::IAppConnections::RemoveListenerConfig. + * @remarks Only accessible when the application runs in maintenance mode. + * @param[in] ssName Name of the listener configuration. + * @return Returns whether the removal was successful. + */ + bool RemoveListenerConfig(/*in*/ const sdv::u8string& ssName) override; + + /** + * @brief Get a sequence with connection names. Overload of sdv::app::IAppConnections::GetConnections. + * @return Sequence with connection name strings. + */ + sdv::sequence GetConnections() const override; + + /** + * @brief Get the connection configuration. Overload of sdv::app::IAppConnections::GetConnectionConfig. + * @param[in] ssName Name of the connection. + * @return String containing the connection configuration. + */ + sdv::u8string GetConnectionConfig(/*in*/ const sdv::u8string& ssName) const override; + + /** + * @brief Add or update a connection configuration. Overload of sdv::app::IAppConnections::AddConnectionConfig. + * @remarks Only accessible when the application runs in maintenance mode. + * @param[in] ssName Name of the connection configuration. + * @param[in] ssConfig The configuration string for the connection. + * @param[in] ssInsertBefore Reference to the string to connection to insert the the new connection before, or empty when the + * the new connection should be placed at the end. + * @return Returns whether the connection could be added (fails when the connection already exists). + */ + bool AddConnectionConfig(/*in*/ const sdv::u8string& ssName, /*in*/ const sdv::u8string& ssConfig, + /*in*/ const sdv::u8string& ssInsertBefore = std::string()) override; + + /** + * @brief Remove a connection configuration with the provided name. Overload of + * sdv::app::IAppConnections::RemoveConnectionConfig. + * @remarks Only accessible when the application runs in maintenance mode. + * @param[in] ssName Name of the connection configuration. + * @return Returns whether the removal was successful. + */ + bool RemoveConnectionConfig(/*in*/ const sdv::u8string& ssName) override; + + /** + * @brief Return the number of retries to establish a connection. Overload of sdv::app::IAppConnections::GetRetries. + * @return Number of retries. + */ + uint32_t GetConnectRetries() const override; /** * @brief Reset the settings after a shutdown. @@ -360,26 +532,42 @@ public: void Reset(); private: - sdv::app::EAppContext m_eContextMode = sdv::app::EAppContext::no_context; ///< The application is running as... - uint32_t m_uiInstanceID = 0u; ///< Instance number. - uint32_t m_uiRetries = 0u; ///< Number of retries to establish a connection. + /** + * @brief Get the default listener config. + * @return String containing the TOML with the listener config. + */ + std::string DefaultListenerConfig() const; + + /** + * @brief Get the default connection config. + * @return String containing the TOML with the connection config. + */ + std::string DefaultConnectionConfig() const; + + sdv::app::EAppContext m_eAppContextType = sdv::app::EAppContext::no_context; ///< The application is running as... + uint32_t m_uiInstanceID = 0u; ///< Instance number (default 1000, but only after startup). + uint32_t m_uiConnectRetries = 5u; ///< Number of retries to establish a connection. std::string m_ssLoggerClass; ///< Class name of a logger service. std::filesystem::path m_pathLoggerModule; ///< Module name of a custom logger. std::string m_ssProgramTag; ///< Program tag to use when logging. sdv::core::ELogSeverity m_eSeverityFilter = sdv::core::ELogSeverity::info; ///< Severity level filter while logging. sdv::core::ELogSeverity m_eSeverityViewFilter = sdv::core::ELogSeverity::error; ///< Severity level filter while logging. - bool m_bSilent = false; ///< When set, no console reporting takes place. - bool m_bVerbose = false; ///< When set, extensive console reporting takes place. + EAppConsoleReporting m_eConsoleReporting = EAppConsoleReporting::normal; ///< Console reporting + bool m_bRedirectMon = false; ///< When set, redirect the message from the monitor onto console. + std::filesystem::path m_pathFrameworkDir; ///< Location of framework component directory. std::filesystem::path m_pathRootDir; ///< Location of user component root directory. std::filesystem::path m_pathInstallDir; ///< Location of user component installations (root with instance). std::filesystem::path m_pathPlatformConfig; ///< The platform configuration from the settings file. std::filesystem::path m_pathVehIfcConfig; ///< The vehicle interface configuration from the settings file. std::filesystem::path m_pathVehAbstrConfig; ///< The vehicle abstraction configuration from the settings file. std::filesystem::path m_pathUserConfig; ///< The user configuration from the settings file. - bool m_bPlatformConfig = false; ///< Platform config was explicitly enabled/disabled. - bool m_bVehIfcConfig = false; ///< Vehicle interface config was explicitly enabled/disabled. - bool m_bVehAbstrConfig = false; ///< Vehicle abstraction config was explicitly enabled/disabled. - bool m_bUserConfig = false; ///< User config was explicitly enabled/disabled. + bool m_bUpdatePlatformConfig = false; ///< Platform config was explicitly marked for update. + bool m_bUpdateVehIfcConfig = false; ///< Vehicle interface config was explicitly marked for update. + bool m_bUpdateVehAbstrConfig = false; ///< Vehicle abstraction config was explicitly marked for update. + bool m_bUpdateUserConfig = false; ///< User config was explicitly marked for update. + std::map m_mapListeners; ///< Map with listener configurations. + std::vector> m_vecConnections; ///< Vector with connection configurations. + std::string m_ssDefaultComProvider; ///< Name of the default communication provider. }; /** @@ -398,7 +586,11 @@ public: // Interface map BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY_MEMBER(sdv::IAttributes, GetAppSettings()) + SDV_INTERFACE_SET_SECTION_CONDITION(GetAppSettings().IsMaintenanceApplication(), 1) + SDV_INTERFACE_SECTION(1) + SDV_INTERFACE_ENTRY_MEMBER(sdv::app::IAppSettingsPersist, GetAppSettings()) + SDV_INTERFACE_ENTRY_MEMBER(sdv::app::IAppConnections, GetAppSettings()) + SDV_INTERFACE_DEFAULT_SECTION() END_SDV_INTERFACE_MAP() // Object declarations @@ -406,6 +598,11 @@ public: DECLARE_OBJECT_CLASS_NAME("AppSettingsService") DECLARE_OBJECT_SINGLETON() + // Parameter map + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_CHAIN_MEMBER(GetAppSettings()) + END_SDV_PARAM_MAP() + /** * @brief Get access to the application settings. * @return Returns the one global instance of the application config. diff --git a/sdv_services/core/installation_composer.cpp b/sdv_services/core/installation_composer.cpp index ce518b0..8cf336b 100644 --- a/sdv_services/core/installation_composer.cpp +++ b/sdv_services/core/installation_composer.cpp @@ -38,6 +38,12 @@ #error OS is not supported! #endif +namespace +{ + constexpr float kMaxBLOBSize = (24*1024*1024) + 25000000; +} + + CInstallComposer::~CInstallComposer() {} @@ -1019,10 +1025,16 @@ uint32_t CInstallComposer::SerializeModuleBLOB(uint32_t uiChecksumInit, sdv::poi // Calculate the size of the BLOB (including padding and checksum) sdv::installation::SPackageBLOBChecksum sBLOBChecksum{}; size_t nSize = 0; - sdv::ser_size(sBLOB, nSize); + sdv::ser_size(sBLOB, nSize); sdv::ser_size(sBLOBChecksum, nSize); if (nSize % 8) nSize += 8 - nSize % 8; sBLOB.uiBLOBSize = static_cast(nSize); + if(sBLOB.uiBLOBSize > kMaxBLOBSize) + { + sdv::installation::XPackageSizeExceeded exception; + exception.ssFileName = sBLOB.sFileDesc.ssFileName; + throw exception; + } // Serialize the BLOB sdv::serializer serializer; @@ -1246,7 +1258,7 @@ sdv::installation::SPackageBLOB CInstallComposer::DeserializeBLOB(std::ifstream& // Extend the buffer and read the BLOB data (after the resize, the psPartialBLOB pointer could be invalidated). uint32_t uiBLOBSize = psPartialBLOB->uiBLOBSize; - if (uiBLOBSize > 26*1024*1024) // 26 instead of 24 becasue of door example + if (uiBLOBSize > kMaxBLOBSize) throw sdv::installation::XIncompatiblePackage(); // Safety ptrBLOB.resize(uiBLOBSize); if (!ptrBLOB) diff --git a/sdv_services/core/iso_monitor.cpp b/sdv_services/core/iso_monitor.cpp index e356797..dcf7d5e 100644 --- a/sdv_services/core/iso_monitor.cpp +++ b/sdv_services/core/iso_monitor.cpp @@ -23,11 +23,11 @@ CIsoMonitor::~CIsoMonitor() GetAppControl().RequestShutdown(); } -void CIsoMonitor::Initialize(/*in*/ const sdv::u8string& ssObjectConfig) +void CIsoMonitor::Initialize(/*in*/ const sdv::SObjectInfo& sObjectInfo) { if (m_pObjectControl) { - m_pObjectControl->Initialize(ssObjectConfig); + m_pObjectControl->Initialize(sObjectInfo); m_eObjectState = m_pObjectControl->GetObjectState(); } else diff --git a/sdv_services/core/iso_monitor.h b/sdv_services/core/iso_monitor.h index 1a24393..761c49f 100644 --- a/sdv_services/core/iso_monitor.h +++ b/sdv_services/core/iso_monitor.h @@ -42,9 +42,9 @@ public: /** * @brief Initialize the object. Overload of sdv::IObjectControl::Initialize. - * @param[in] ssObjectConfig Optional configuration string. + * @param[in] sObjectInfo The registration information of this object. */ - virtual void Initialize(/*in*/ const sdv::u8string& ssObjectConfig) override; + virtual void Initialize(/*in*/ const sdv::SObjectInfo& sObjectInfo) override; /** * @brief Get the current state of the object. Overload of sdv::IObjectControl::GetObjectState. diff --git a/sdv_services/core/local_shutdown_request.h b/sdv_services/core/local_shutdown_request.h index 04323c4..6bd515e 100644 --- a/sdv_services/core/local_shutdown_request.h +++ b/sdv_services/core/local_shutdown_request.h @@ -61,6 +61,8 @@ inline bool RequestShutdown(uint32_t uiInstanceID = 1000u) CloseHandle(hEvent); return true; } + else + std::cerr << "ERROR: Shutdown event handle is not available: " << m_ssSignalName << std::endl; #elif defined __unix__ sem_t* pSemaphore = sem_open(m_ssSignalName.c_str(), 0); if (pSemaphore && pSemaphore != SEM_FAILED) diff --git a/sdv_services/core/logger.cpp b/sdv_services/core/logger.cpp index f46337f..b3bd1ca 100644 --- a/sdv_services/core/logger.cpp +++ b/sdv_services/core/logger.cpp @@ -14,6 +14,7 @@ #include "logger.h" #include #include "../../global/exec_dir_helper.h" +#include "app_settings.h" #ifdef __unix__ #include @@ -38,7 +39,7 @@ CLogger::~CLogger() void CLogger::Log(sdv::core::ELogSeverity eSeverity, /*in*/ const sdv::u8string& ssSrcFile, /*in*/ uint32_t uiSrcLine, /*in*/ sdv::process::TProcessID tProcessID, /*in*/ const sdv:: u8string& ssObjectName, /*in*/ const sdv::u8string& ssMessage) { - if (static_cast(eSeverity) >= static_cast(m_eViewFilter)) + if (GetAppSettings().IsConsoleVerbose() && static_cast(eSeverity) >= static_cast(m_eViewFilter)) { if (tProcessID) std::clog << "[PID#" << static_cast(tProcessID) << "] "; if (!ssObjectName.empty()) std::clog << ssObjectName << " "; diff --git a/sdv_services/core/module.cpp b/sdv_services/core/module.cpp index 2f6cf54..827779f 100644 --- a/sdv_services/core/module.cpp +++ b/sdv_services/core/module.cpp @@ -297,6 +297,10 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept } m_mapClassInfo[sClass.ssName] = sClass; + + // Add aliases? + for (const auto& rssAlias : sClass.seqClassAliases) + m_mapClassInfo[rssAlias] = sClass; } } catch (const sdv::toml::XTOMLParseException&) diff --git a/sdv_services/core/module_control.cpp b/sdv_services/core/module_control.cpp index 72f4ed0..636ee70 100644 --- a/sdv_services/core/module_control.cpp +++ b/sdv_services/core/module_control.cpp @@ -250,7 +250,7 @@ std::shared_ptr CModuleControl::FindModuleByClass(const std::string } } - // For main and isolated applications, check whether the module is in one of the installation manifests. + // For main, isolated and maintenance applications, check whether the module is in one of the installation manifests. auto optManifest = GetAppConfig().FindInstalledComponent(rssClass); if (!optManifest) return nullptr; std::filesystem::path pathModule = std::filesystem::u8path(static_cast(optManifest->ssModulePath)); @@ -378,8 +378,6 @@ std::string CModuleControl::SaveConfig(const std::set& /* sdv::core::TModuleID CModuleControl::ContextLoad(const std::filesystem::path& rpathModule, const std::string& rssManifest) { - if (GetAppSettings().IsMaintenanceApplication()) return 0; // Not allowed - // Run through the manifest and check for complex services, applications and utilities. // TODO EVE: Temporary suppression of cppcheck warning. // cppcheck-suppress variableScope @@ -462,7 +460,7 @@ void CModuleControl::AddCurrentPath() if (!m_lstSearchPaths.empty()) return; // Add the core directory - std::filesystem::path pathCoreDir = GetCoreDirectory().lexically_normal(); + std::filesystem::path pathCoreDir = GetAppSettings().GetFrameworkDir().lexically_normal(); m_lstSearchPaths.push_back(pathCoreDir); // Add the exe dir diff --git a/sdv_services/core/permission_control.cpp b/sdv_services/core/permission_control.cpp new file mode 100644 index 0000000..5687a44 --- /dev/null +++ b/sdv_services/core/permission_control.cpp @@ -0,0 +1,167 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include "permission_control.h" + +#if defined __GNUC__ && defined _WIN32 +thread_local std::list>& + CPermissionControl::m_lstPermissionTracker = CreatePermissionList(); +thread_local std::map& + CPermissionControl::m_mapPermissions = CreatePermissionMap(); +#else +thread_local std::list> CPermissionControl::m_lstPermissionTracker; +thread_local std::map CPermissionControl::m_mapPermissions; +#endif + +CPermissionControl& GetPermissionControl() +{ + static CPermissionControl control; + return control; +} + +CAccessPermission::CAccessPermission(CPermissionControl& rPermissionControl, sdv::core::EAccessPermission ePermission) : + m_rPermissionControl(rPermissionControl), m_ePermission(ePermission) +{ + if (m_ePermission != sdv::core::EAccessPermission::not_set) + rPermissionControl.IncrementPermissionCount(ePermission); +} + +CAccessPermission::CAccessPermission(const CAccessPermission& rPermission) : + m_rPermissionControl(rPermission.m_rPermissionControl), m_ePermission(rPermission.m_ePermission) +{ + if (m_ePermission != sdv::core::EAccessPermission::not_set) + m_rPermissionControl.IncrementPermissionCount(m_ePermission); +} + +CAccessPermission::CAccessPermission(CAccessPermission&& rPermission) : + m_rPermissionControl(rPermission.m_rPermissionControl), m_ePermission(rPermission.m_ePermission) +{ + rPermission.m_ePermission = sdv::core::EAccessPermission::not_set; +} + +CAccessPermission& CAccessPermission::operator=(const CAccessPermission& rPermission) +{ + // Remove current permission and copy the new permission + if (m_ePermission != sdv::core::EAccessPermission::not_set) + m_rPermissionControl.DecrementPermissionCount(m_ePermission); + m_ePermission = rPermission.m_ePermission; + if (m_ePermission != sdv::core::EAccessPermission::not_set) + m_rPermissionControl.IncrementPermissionCount(m_ePermission); + return *this; +} + +CAccessPermission& CAccessPermission::operator=(CAccessPermission && rPermission) +{ + // Remove current permission and move the new permission + if (m_ePermission != sdv::core::EAccessPermission::not_set) + m_rPermissionControl.DecrementPermissionCount(m_ePermission); + m_ePermission = rPermission.m_ePermission; + rPermission.m_ePermission = sdv::core::EAccessPermission::not_set; + return *this; +} + +CAccessPermission ::~CAccessPermission() +{ + if (m_ePermission != sdv::core::EAccessPermission::not_set) + m_rPermissionControl.DecrementPermissionCount(m_ePermission); +} + +sdv::core::EAccessPermission CAccessPermission::Permission() const +{ + return m_ePermission; +} + +sdv::core::TPermissionID CPermissionControl::RestrictAccessPermission(/*in*/ sdv::core::EAccessPermission ePermission) +{ + // Unlike the CreateAccessPermission this function can only be used to restrict an already available restriction. It cannot be + // used to set the initial restriction. + if (m_lstPermissionTracker.empty()) return false; + + sdv::core::TPermissionID tPermissionID = m_idgen.Generate(); + if (!tPermissionID) return 0u; + m_mapPermissions.emplace(tPermissionID, CreatePermissionObject(ePermission)); + return tPermissionID; +} + +bool CPermissionControl::ReleaseAccessPermission(/*in*/ sdv::core::TPermissionID tPermissionID) +{ + auto itPermission = m_mapPermissions.find(tPermissionID); + if (itPermission == m_mapPermissions.end()) return false; + m_mapPermissions.erase(itPermission); + return true; +} + +sdv::core::TPermissionTransferID CPermissionControl::TransferCurrentPermission() +{ + std::unique_lock lock(m_mtxPermissionTransfer); + sdv::core::TPermissionTransferID tTransferID = m_idgen.Generate(); + m_mapPermissionTransfer[tTransferID] = GetCurrentPermission(); + return tTransferID; +} + +sdv::core::TPermissionID CPermissionControl::SetAccessPermission(/*in*/ sdv::core::TPermissionTransferID tTransferID) +{ + // Get the access restriction level from the transfer permission map. + std::unique_lock lock(m_mtxPermissionTransfer); + auto itTransferPermission = m_mapPermissionTransfer.find(tTransferID); + if (itTransferPermission == m_mapPermissionTransfer.end()) return 0u; + sdv::core::EAccessPermission ePermission = itTransferPermission->second; + m_mapPermissionTransfer.erase(itTransferPermission); + + // Unlike the RestrictAccessPermission function, the SetAccessPermission function can be used to set an initial permission. + sdv::core::TPermissionID tPermissionID = m_idgen.Generate(); + if (!tPermissionID) return 0u; + m_mapPermissions.emplace(tPermissionID, CreatePermissionObject(ePermission)); + return tPermissionID; +} + +sdv::core::EAccessPermission CPermissionControl::GetCurrentPermission() const +{ + return m_lstPermissionTracker.empty() ? sdv::core::EAccessPermission::restricted_access : m_lstPermissionTracker.front().first; +} + +CAccessPermission CPermissionControl::CreatePermissionObject(sdv::core::EAccessPermission ePermission) +{ + sdv::core::EAccessPermission ePermissionLocal = std::max(ePermission, sdv::core::EAccessPermission::restricted_access); + return CAccessPermission(*this, ePermissionLocal); +} + +void CPermissionControl::IncrementPermissionCount(sdv::core::EAccessPermission ePermission) +{ + // In the permission tracker list, search for the counter for this permission or, if not existing, the next higher permission. + auto itPermissionCounter = m_lstPermissionTracker.begin(); + for (; itPermissionCounter != m_lstPermissionTracker.end(); ++itPermissionCounter) + { + if (itPermissionCounter->first >= ePermission) + break; + } + + // If counter doesn't exist, insert a new counter. + if (itPermissionCounter == m_lstPermissionTracker.end() || itPermissionCounter->first > ePermission) + itPermissionCounter = m_lstPermissionTracker.insert(itPermissionCounter, std::make_pair(ePermission, 1)); + else // exists, increase the counter + itPermissionCounter->second++; +} + +void CPermissionControl::DecrementPermissionCount(sdv::core::EAccessPermission ePermission) +{ + // Find the permission counter + auto itPermissionCounter = std::find_if(m_lstPermissionTracker.begin(), m_lstPermissionTracker.end(), [&](const auto& rprPermissionCount) + { return rprPermissionCount.first == ePermission; }); + if (itPermissionCounter == m_lstPermissionTracker.end()) return; + + // Reduce the counter and if reaching zero, erase the counter + itPermissionCounter->second--; + if (!itPermissionCounter->second) + m_lstPermissionTracker.erase(itPermissionCounter); +} diff --git a/sdv_services/core/permission_control.h b/sdv_services/core/permission_control.h new file mode 100644 index 0000000..177b267 --- /dev/null +++ b/sdv_services/core/permission_control.h @@ -0,0 +1,263 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#ifndef PERMISSION_CONTROL_H +#define PERMISSION_CONTROL_H + +#include +#include +#include + +#include "../../global/unique_id.h" + +// Forward declaration +class CPermissionControl; + +/** + * @brief Use a list to create a stack, which allows elements to be deleted. + */ +using TPermissionStack = std::list; + +/** + * @brief Permission class for the requested access. This class manages the lifetime of the permissions. + */ +class CAccessPermission +{ +private: + /// Permission control is allowed to create an access permission object. + friend CPermissionControl; + + /** + * @brief Constructor + * @param[in] rPermissionControl Reference to permission control. + * @param[in] ePermission The requested access permission. + */ + CAccessPermission(CPermissionControl& rPermissionControl, sdv::core::EAccessPermission ePermission); + +public: + /** + * @brief Destructor + */ + ~CAccessPermission(); + + /** + * @brief Copy constructor + * @param[in] rPermission Reference to the permission object to copy from. + */ + CAccessPermission(const CAccessPermission& rPermission); + + /** + * @brief Move constructor. + * @param[in] rPermission Reference to the permission object to move from. + */ + CAccessPermission(CAccessPermission&& rPermission); + + /** + * @brief Copy assignment operator. + * @param[in] rPermission Reference to the permission object to move from. + * @return Reference to this access permission object. + */ + CAccessPermission& operator=(const CAccessPermission& rPermission); + + /** + * @brief Move assignment operator. + * @param[in] rPermission Reference to the permission object to move from. + * @return Reference to this access permission object. + */ + CAccessPermission& operator=(CAccessPermission&& rPermission); + + /** + * @brief Get the permission level assigned to this object. + * @remarks This might not correspond to the current permission level available for this thread. + * @return The permission level. + */ + sdv::core::EAccessPermission Permission() const; + +private: + CPermissionControl& m_rPermissionControl; ///< Reference to the permission control. + sdv::core::EAccessPermission m_ePermission; ///< Requested permission. +}; + +/** + * @brief Permission control provides a simple access mechanism to allow the management of access to system functions. + */ +class CPermissionControl : public sdv::IInterfaceAccess, public sdv::core::IPermissionControl +{ +public: + /// Access permission class can access the permission control. + friend CAccessPermission; + + /** + * @brief Default constructor + */ + CPermissionControl() = default; + + // Interface map + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::core::IPermissionControl) + END_SDV_INTERFACE_MAP() + + /** + * @brief Restrict the access permission for the current thread. Overload of + * sdv::core.:IPermissionControl::RestrictAccessPermission. + * @remarks The access restriction will be assigned to the current thread and combined with previous and future + * permissions. The lowest assigned permission will determine the actual access permission for the current thread. + * @remarks The access restriction will stay in effect until it is released by the function ReleaseAccessPermission. + * @remarks A newly created thread has fully restricted access. This cannot be changed using this function. Use an access + * restriction transfer from one thread to this thread to set a higher level of access permission. + * @param[in] ePermission The permission to restrict to. + * @return The permission ID for this restriction or 0 when the access permission could not be set. Use the + * ReleaseAccessPermission to release the restriction again. + */ + virtual sdv::core::TPermissionID RestrictAccessPermission(/*in*/ sdv::core::EAccessPermission ePermission) override; + + /** + * @brief Release a previously set access restriction for the current thread. Overload of + * sdv::core::IPermissionControl::ReleaseAccessPermission. + * @param[in] tPermissionID The ID of the access restriction previously set for the current thread. + * @return Returns whether the restriction could be released successfully. + */ + virtual bool ReleaseAccessPermission(/*in*/ sdv::core::TPermissionID tPermissionID) override; + + /** + * @brief Prepare to transfer the access restriction from the current thread.Overload of + * sdv::core.:IPermissionControl::TransferCurrentPermission. + * @return The ID of the transfer object containing the current access permissions or 0 when the transfer preparation has + * failed. + */ + virtual sdv::core::TPermissionTransferID TransferCurrentPermission() override; + + /** + * @brief Set the access permission using a transfer object from one thread to another. Overload of + * sdv::core.:IPermissionControl::SetAccessPermission. + * @remarks An new thread has fully restricted access per default. Use this function to set the required access level. If a + * thread has already initialized with the proper access level, this function will set identical or lower access permissions + * for the current thread. + * @param[in] tTransferID The IS of the prepared access permission transfer. + * @return The permission ID for this restriction or 0 when the access permission could not be transferred. Use the + * ReleaseAccessPermission to release the restriction again. + */ + virtual sdv::core::TPermissionID SetAccessPermission(/*in*/ sdv::core::TPermissionTransferID tTransferID) override; + + /** + * @brief Get the access permission level for the current thread. This will be the lowest restriction set for the current + * thread. Overload of sdv::core.:IPermissionControl::GetCurrentPermission. + * @return The current access permission level. + */ + virtual sdv::core::EAccessPermission GetCurrentPermission() const override; + + /** + * @brief Create a permission object that assigns the access permissions to the call thread. The permissions stay in place + * until a last copy of the object is destroyed. + * @param[in] ePermission The new permission to set for the thread. If the permission has a value smaller than + * sdv::core::EAccessPermission::restricted_access, the object will be created with restricted access permission. + * @return The permission object. The permission will stay in place during the lifetime of the object. + */ + CAccessPermission CreatePermissionObject(sdv::core::EAccessPermission ePermission); + +private: + /** + * @brief Increment the counter for a specific permission. + * @param[in] ePermission The permission the count should be incremented for. + */ + void IncrementPermissionCount(sdv::core::EAccessPermission ePermission); + + /** + * @brief Increment the counter for a specific permission. + * @param[in] ePermission The permission the count should be incremented for. + */ + void DecrementPermissionCount(sdv::core::EAccessPermission ePermission); + + CUniqueID m_idgen; ///< Unique ID generator. + +#if defined __GNUC__ && defined _WIN32 + // https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/610009 + // In MINGW GCC implementation of thread_local, there is an issue with complex classes being used after the destruction. This + // is caused by an architectural bug (https://github.com/msys2/MINGW-packages/issues/2519) causing the emutls (GCC Emulated TLS) + // managing the memory allocation to be executed before the __cxa_thread_exit, the thread cleanup callback of Windows. + // The solution is to use trivial data only (pointer, reference, integers, etc.). A workaround is implemented to use a reference + // of the permission list and map instead of an instance to the list and map. + + /** + * @brief Create a permission list object. + * @return Reference to the created list object. + */ + static std::list>& CreatePermissionList() + { + static std::mutex mtxPermissionLists; + static std::list>> lstPermissionLists; + std::unique_lock lock(mtxPermissionLists); + lstPermissionLists.resize(lstPermissionLists.size() + 1); + return lstPermissionLists.back(); + } + + /// Thread specific permission tracker list (permission, lowest first and the count for each permission second). + static thread_local std::list>& m_lstPermissionTracker; + + /** + * @brief Create a permission map object. + * @return Reference to the created map object. + */ + static std::map& CreatePermissionMap() + { + static std::mutex mtxPermissionMaps; + static std::list> lstPermissionMaps; + std::unique_lock lock(mtxPermissionMaps); + lstPermissionMaps.resize(lstPermissionMaps.size() + 1); + return lstPermissionMaps.back(); + } + + /// Thread specific permission access map. + static thread_local std::map& m_mapPermissions; +#else + /// Thread specific permission tracker list (permission, lowest first and the count for each permission second). + static thread_local std::list> m_lstPermissionTracker; + + /// Thread specific permission access map. + static thread_local std::map m_mapPermissions; +#endif + + /// Permission transfer object map (allow shifting permission from one thread to another). + std::map m_mapPermissionTransfer; + + std::mutex m_mtxPermissionTransfer; ///< Protecting the permission transfer map. +}; + +/** + * @brief Return the permission control. + * @return Reference to the permission control. + */ +CPermissionControl& GetPermissionControl(); + +/** + * @brief Permission control service + */ +class CPermissionControlService : public sdv::CSdvObject +{ +public: + CPermissionControlService() = default; + + // Interface map + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_CHAIN_MEMBER(GetPermissionControl()) + END_SDV_INTERFACE_MAP() + + // Object declarations + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) + DECLARE_OBJECT_CLASS_NAME("PermissionControlService") + DECLARE_OBJECT_SINGLETON() +}; + +DEFINE_SDV_OBJECT(CPermissionControlService) + +#endif // !defined PERMISSION_CONTROL_H \ No newline at end of file diff --git a/sdv_services/core/repository.cpp b/sdv_services/core/repository.cpp index 62df231..f0deacb 100644 --- a/sdv_services/core/repository.cpp +++ b/sdv_services/core/repository.cpp @@ -16,12 +16,14 @@ #include #include #include +#include #include "object_lifetime_control.h" #include "../../global/base64.h" #include "module_control.h" #include "app_config.h" #include "app_control.h" #include "app_settings.h" +#include "permission_control.h" // GetRepository might be redirected for unit tests. #ifndef GetRepository @@ -60,39 +62,154 @@ void CRepository::SetRunningMode() sdv::IInterfaceAccess* CRepository::GetObject(const sdv::u8string& ssObjectName) { + auto eAccessPermission = GetPermissionControl().GetCurrentPermission(); + if (eAccessPermission < sdv::core::EAccessPermission::remote_access) + return nullptr; + std::shared_lock lock(m_mtxObjects); auto itService = m_mapServiceObjects.find(ssObjectName); - if (itService != m_mapServiceObjects.end()) return (*itService->second)->ptrObject; + if (itService != m_mapServiceObjects.end()) + { + // Check access permissions + switch ((*itService->second)->sClassInfo.eType) + { + case sdv::EObjectType::complex_service: + case sdv::EObjectType::vehicle_function: + case sdv::EObjectType::basic_service: + case sdv::EObjectType::sensor: + case sdv::EObjectType::actuator: + // Allowed for remote access or higher + //if (eAccessPermission < sdv::core::EAccessPermission::remote_access) + // return nullptr; + break; + case sdv::EObjectType::system_object: + // Allowed for local access or higher + if (eAccessPermission < sdv::core::EAccessPermission::local_access) + return nullptr; + break; + default: + // Allowed for full access only + if (eAccessPermission != sdv::core::EAccessPermission::full_access) + return nullptr; + } + return (*itService->second)->ptrObject; + } lock.unlock(); // In case the object is not in the service map and this is a main or isolated application, create the object if the object is - // known in the installation manifest and is a system object. + // known in the installation manifest. auto optManifest = GetAppConfig().FindInstalledComponent(ssObjectName); - if (optManifest && optManifest->eType == sdv::EObjectType::system_object) - return GetObjectByID(CreateObject(optManifest->ssName, optManifest->ssDefaultObjectName, "")); + if (optManifest) + { + // Check access permissions + bool bAutoCreate = false; + switch (optManifest->eType) + { + case sdv::EObjectType::complex_service: + case sdv::EObjectType::vehicle_function: + case sdv::EObjectType::basic_service: + case sdv::EObjectType::sensor: + case sdv::EObjectType::actuator: + // Automatic component creation only for main application. + if (!GetAppSettings().IsMainApplication()) break; + bAutoCreate = true; + + // Allowed for remote access or higher + if (eAccessPermission < sdv::core::EAccessPermission::remote_access) + return nullptr; + break; + case sdv::EObjectType::system_object: + // Automatic component creation allowed for system objects. The objects themselves have to block in case of unwanted + // access by external or isolated applications. + bAutoCreate = true; - // Forward the request to core repository if one is linked here (this can only occur with isolated and external applications). - if (!m_ptrCoreRepoAccess) return nullptr; - sdv::core::IObjectAccess* pObjectAccess = m_ptrCoreRepoAccess.GetInterface(); - if (!pObjectAccess) return nullptr; - return pObjectAccess->GetObject(ssObjectName); + // Allowed for local access or higher + if (eAccessPermission < sdv::core::EAccessPermission::local_access) + return nullptr; + break; + default: + // Automatic component creation only for main application. + if (!GetAppSettings().IsMainApplication()) + break; + bAutoCreate = true; + + // Allowed for full access only + if (eAccessPermission != sdv::core::EAccessPermission::full_access) + return nullptr; + } + + // Create the object if automatic creation is enabled + if (bAutoCreate) + return GetObjectByID(CreateObject2(optManifest->ssName, optManifest->ssDefaultObjectName, "", true)); + } + + // Copy the linked repository and then unlock; could cause a deadlock instead. + lock.lock(); + auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess; + lock.unlock(); + + // If there is a linked core, use the core to get ther object + for (auto& rprLink : lstCoreRepoAccessCopy) + { + sdv::core::IObjectAccess* pObjectAccess = rprLink.second.GetInterface(); + if (!pObjectAccess) continue; + auto* pObject = pObjectAccess->GetObject(ssObjectName); + if (pObject) return pObject; + } + return nullptr; } sdv::IInterfaceAccess* CRepository::GetObjectByID(/*in*/ sdv::core::TObjectID tObjectID) { + auto eAccessPermission = GetPermissionControl().GetCurrentPermission(); + if (eAccessPermission < sdv::core::EAccessPermission::remote_access) + return nullptr; + // Only controlled objects are allowed to be returned using GetObjectByID. std::shared_lock lock(m_mtxObjects); auto itObject = m_mapObjects.find(tObjectID); if (itObject != m_mapObjects.end()) - return itObject->second->bControlled ? itObject->second->ptrObject : nullptr; + { + // Check access permissions + switch (itObject->second->sClassInfo.eType) + { + case sdv::EObjectType::complex_service: + case sdv::EObjectType::vehicle_function: + case sdv::EObjectType::basic_service: + case sdv::EObjectType::sensor: + case sdv::EObjectType::actuator: + //// Allowed for remote access or higher + //if (eAccessPermission < sdv::core::EAccessPermission::remote_access) + // return nullptr; + break; + case sdv::EObjectType::system_object: + // Allowed for local access or higher + if (eAccessPermission < sdv::core::EAccessPermission::local_access) + return nullptr; + break; + default: + // Allowed for full access only + if (eAccessPermission != sdv::core::EAccessPermission::full_access) + return nullptr; + } + return itObject->second->bControlled ? itObject->second->ptrObject : nullptr; + } // TODO: Deal with overlapping IDs in this and in core process... - // Forward the request to core repository if one is linked here (this can only occur with isolated and external applications). - if (!m_ptrCoreRepoAccess) return nullptr; - sdv::core::IObjectAccess* pObjectAccess = m_ptrCoreRepoAccess.GetInterface(); - if (!pObjectAccess) return nullptr; - return pObjectAccess->GetObjectByID(tObjectID); + // Copy the linked repository and then unlock; could cause a deadlock instead. + auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess; + lock.unlock(); + + // If there is a linked core, use the core to get ther object + for (auto& rprLink : lstCoreRepoAccessCopy) + { + sdv::core::IObjectAccess* pObjectAccess = rprLink.second.GetInterface(); + if (!pObjectAccess) continue; + auto* pObject = pObjectAccess->GetObjectByID(tObjectID); + if (pObject) return pObject; + } + return nullptr; } sdv::IInterfaceAccess* CRepository::CreateUtility(/*in*/ const sdv::u8string& ssClassName, /*in*/ const sdv::u8string& ssObjectConfig) @@ -101,6 +218,11 @@ sdv::IInterfaceAccess* CRepository::CreateUtility(/*in*/ const sdv::u8string& ss std::unique_lock lock(m_mtxObjects); + // TODO EVE: Currently only allowed for local access. When utilities are instantiated using isolation, also allowed for remote + // access. + if (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::local_access) + return nullptr; + // Get a fitting module instance std::shared_ptr ptrModule = GetModuleControl().FindModuleByClass(ssClassName); if (!ptrModule) @@ -161,7 +283,7 @@ sdv::IInterfaceAccess* CRepository::CreateUtility(/*in*/ const sdv::u8string& ss auto* pObjectControl = ptrObject.GetInterface(); if (pObjectControl) { - pObjectControl->Initialize(ssObjectConfig); + pObjectControl->Initialize(*ptrObjectEntry); if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized) { // Destroy the object @@ -193,22 +315,23 @@ sdv::IInterfaceAccess* CRepository::CreateProxyObject(/*in*/ sdv::interface_id i // Get a fitting module instance std::string ssClassName = "Proxy_" + std::to_string(id); std::shared_ptr ptrModule = GetModuleControl().FindModuleByClass(ssClassName); - if (!ptrModule && m_ptrCoreRepoAccess) - { - // Request the server for the name of the module. - const sdv::core::IRepositoryInfo* pRepInfo = m_ptrCoreRepoAccess.GetInterface(); - if (pRepInfo) - { - std::string ssModuleName = - std::filesystem::u8path(static_cast(pRepInfo->FindClass(ssClassName).ssModulePath)). - filename().u8string(); - if (!ssModuleName.empty()) - { - sdv::core::TModuleID tModule = GetModuleControl().Load(ssModuleName); - if (tModule) ptrModule = GetModuleControl().GetModule(tModule); - } - } - } + // TODO EVE + //if (!ptrModule && m_ptrCoreRepoAccess) + //{ + // // Request the server for the name of the module. + // const sdv::core::IRepositoryInfo* pRepInfo = m_ptrCoreRepoAccess.GetInterface(); + // if (pRepInfo) + // { + // std::string ssModuleName = + // std::filesystem::u8path(static_cast(pRepInfo->FindClass(ssClassName).ssModulePath)). + // filename().u8string(); + // if (!ssModuleName.empty()) + // { + // sdv::core::TModuleID tModule = GetModuleControl().Load(ssModuleName); + // if (tModule) ptrModule = GetModuleControl().GetModule(tModule); + // } + // } + //} if (!ptrModule) { SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!"); @@ -270,7 +393,7 @@ sdv::IInterfaceAccess* CRepository::CreateProxyObject(/*in*/ sdv::interface_id i auto* pObjectControl = ptrObject.GetInterface(); if (pObjectControl) { - pObjectControl->Initialize(""); + pObjectControl->Initialize(*ptrObjectEntry); if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized) { // Destroy the object @@ -302,22 +425,23 @@ sdv::IInterfaceAccess* CRepository::CreateStubObject(/*in*/ sdv::interface_id id // Get a fitting module instance std::string ssClassName = "Stub_" + std::to_string(id); std::shared_ptr ptrModule = GetModuleControl().FindModuleByClass(ssClassName); - if (!ptrModule && m_ptrCoreRepoAccess) - { - // Request the server for the name of the module. - const sdv::core::IRepositoryInfo* pRepInfo = m_ptrCoreRepoAccess.GetInterface(); - if (pRepInfo) - { - std::string ssModuleName = - std::filesystem::u8path(static_cast(pRepInfo->FindClass(ssClassName).ssModulePath)). - filename().u8string(); - if (!ssModuleName.empty()) - { - sdv::core::TModuleID tModule = GetModuleControl().Load(ssModuleName); - if (tModule) ptrModule = GetModuleControl().GetModule(tModule); - } - } - } + // TODO EVE + //if (!ptrModule && m_ptrCoreRepoAccess) + //{ + // // Request the server for the name of the module. + // const sdv::core::IRepositoryInfo* pRepInfo = m_ptrCoreRepoAccess.GetInterface(); + // if (pRepInfo) + // { + // std::string ssModuleName = + // std::filesystem::u8path(static_cast(pRepInfo->FindClass(ssClassName).ssModulePath)). + // filename().u8string(); + // if (!ssModuleName.empty()) + // { + // sdv::core::TModuleID tModule = GetModuleControl().Load(ssModuleName); + // if (tModule) ptrModule = GetModuleControl().GetModule(tModule); + // } + // } + //} if (!ptrModule) { SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!"); @@ -378,7 +502,7 @@ sdv::IInterfaceAccess* CRepository::CreateStubObject(/*in*/ sdv::interface_id id auto* pObjectControl = ptrObject.GetInterface(); if (pObjectControl) { - pObjectControl->Initialize(""); + pObjectControl->Initialize(*ptrObjectEntry); if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized) { // Destroy the object @@ -414,11 +538,11 @@ sdv::core::TObjectID CRepository::CreateObject(const sdv::u8string& ssClassName, Build dependency list and do these checks on dependent objects as well */ - return CreateObject2(ssClassName, ssObjectName, ssObjectConfig); + return CreateObject2(ssClassName, ssObjectName, ssObjectConfig, false); } sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName, const sdv::u8string& ssObjectName, - const sdv::u8string& ssObjectConfig) + const sdv::u8string& ssObjectConfig, bool bIndirect) { // TODO EVE: Link to core repo. Allow only creation of one object if not already created before... // Add support for automatic app control shutdown when utility is closed in case of isolated app. @@ -457,21 +581,29 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName switch (optClassInfo->eType) { case sdv::EObjectType::system_object: + // Only allowed with full access + bError = !bIndirect && (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::full_access); break; case sdv::EObjectType::device: case sdv::EObjectType::platform_abstraction: case sdv::EObjectType::vehicle_bus: - bError = !bDeviceAndBasicServiceAllowed; + // Only allowed with full access + bError = !bIndirect && (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::full_access); + bError |= !bDeviceAndBasicServiceAllowed; break; case sdv::EObjectType::basic_service: case sdv::EObjectType::sensor: case sdv::EObjectType::actuator: - bError = !bDeviceAndBasicServiceAllowed; + // Only allowed with full access + bError = !bIndirect && (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::full_access); + bError |= !bDeviceAndBasicServiceAllowed; break; case sdv::EObjectType::complex_service: case sdv::EObjectType::vehicle_function: + // Only allowed with local access + bError = !bIndirect && (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::local_access); bIsolate = GetAppSettings().IsMainApplication(); - bError = !bComplexServiceAllowed; + bError |= !bComplexServiceAllowed; m_bIsoObjectLoaded = true; break; default: @@ -498,8 +630,11 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName auto itPreviousService = m_mapServiceObjects.find(ssObjectName2); if (itPreviousService != m_mapServiceObjects.end()) { + // Get the service pointer + std::shared_ptr ptrService = *itPreviousService->second; + // Object entry is valid? - if (!*itPreviousService->second) + if (!ptrService) { // This should not occur... there is a previous object in the service map, but the object is empty. SDV_LOG_ERROR("Object creation requested for class \"", ssClassName, "\", but object with the same name \"", @@ -508,12 +643,15 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName } // Trying to create an object with the same name is not an error if the classes are identical. - if ((*itPreviousService->second)->sClassInfo.ssName == ssClassName) - return (*itPreviousService->second)->tObjectID; + const auto& rsClassInfo = ptrService->sClassInfo; + if (rsClassInfo.ssName == ssClassName || + std::find(rsClassInfo.seqClassAliases.begin(), rsClassInfo.seqClassAliases.end(), ssClassName) != + rsClassInfo.seqClassAliases.end()) + return ptrService->tObjectID; // Object name was already used by another class. This is an error. SDV_LOG_ERROR("Object creation requested for class \"", ssClassName, "\", but object with the same name \"", - ssObjectName2, "\" was already instantiated for class \"", (*itPreviousService->second)->sClassInfo.ssName, + ssObjectName2, "\" was already instantiated for class \"", rsClassInfo.ssName, "\"!"); return 0; } @@ -549,14 +687,18 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModuleID tModuleID, /*in*/ const sdv::u8string& ssClassName, /*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssObjectConfig) { - if(ssClassName.empty()) return false; + if(ssClassName.empty()) return 0u; + + // Only allowed with full access + if (GetPermissionControl().GetCurrentPermission() != sdv::core::EAccessPermission::full_access) + return 0u; // Get a fitting module instance std::shared_ptr ptrModule = GetModuleControl().GetModule(tModuleID); if (!ptrModule) { SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!"); - return false; + return 0u; } // Check the class type @@ -564,7 +706,7 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu if (!optClassInfo) { SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!"); - return false; + return 0u; } bool bError = false; bool bDeviceAndBasicServiceAllowed = GetAppSettings().IsMainApplication() || GetAppSettings().IsStandaloneApplication() || @@ -602,7 +744,7 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu // Utilities and marshall objects cannot be created using the CreateObject function. SDV_LOG_ERROR("Creation of an object of invalid type \"", sdv::ObjectType2String(optClassInfo->eType), "\" requested for class \"", ssClassName, "\"!"); - return 0; + return 0u; } // Check for an object name. If not existing get the default name (being either one specified by the object or the class name). @@ -626,7 +768,7 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu "\", but object with the same name \"", ssObjectName2, "\" was already instantiated, but cannot be found!"); - return 0; + return 0u; } // Trying to create an object with the same name is not an error if the classes are identical. @@ -641,7 +783,7 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu "\" was already instantiated for class \"", (*itPreviousService->second)->sClassInfo.ssName, "\"!"); - return 0; + return 0u; } // Check with singleton objects if the object was already instantiated. @@ -657,7 +799,7 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu SDV_LOG_ERROR("Object creation requested but object from the same class \"", ssClassName, "\" was already instantiated and only one instance is allowed!"); - return 0; + return 0u; } } } @@ -671,17 +813,41 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu bool CRepository::DestroyObject(/*in*/ const sdv::u8string& ssObjectName) { - return DestroyObject2(ssObjectName); + TDeferredObjectDestructionList lstDeferredObjectDestruction; + bool bRes = DestroyObject2(ssObjectName, false, lstDeferredObjectDestruction); + for (auto& rprObject : lstDeferredObjectDestruction) + rprObject.first->ptrModule->DestroyObject(rprObject.second); + return bRes; } -bool CRepository::DestroyObject2(/*in*/ const sdv::u8string& ssObjectName) +bool CRepository::DestroyObject2(const sdv::u8string& ssObjectName, bool bIndirect, + TDeferredObjectDestructionList& rlstDeferredObjectDestruction) { std::unique_lock lock(m_mtxObjects); auto itService = m_mapServiceObjects.find(ssObjectName); if (itService == m_mapServiceObjects.end()) return false; + auto ptrObjectEntry = *itService->second; + + // When directly called, check the access permissions + if (!bIndirect) + { + switch (ptrObjectEntry->sClassInfo.eType) + { + case sdv::EObjectType::complex_service: + case sdv::EObjectType::vehicle_function: + // Only allowed having local or higher access permission + if (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::local_access) + return false; + break; + default: + // Only allowed having full access permission + if (GetPermissionControl().GetCurrentPermission() != sdv::core::EAccessPermission::full_access) + return false; + break; + } + } // Print info - auto ptrObjectEntry = *itService->second; if (GetAppSettings().IsConsoleVerbose()) std::cout << "Destroy a " << sdv::ObjectType2String(ptrObjectEntry->sClassInfo.eType) << " #" << ptrObjectEntry->tObjectID << " of type " << ptrObjectEntry->sClassInfo.ssName << " with the name " << ssObjectName << std::endl; @@ -717,13 +883,25 @@ bool CRepository::DestroyObject2(/*in*/ const sdv::u8string& ssObjectName) lock.unlock(); // Only controlled objects can be destroyed this way. - if (ptrDependingObject->bControlled) DestroyObject(ptrDependingObject->ssName); + if (ptrDependingObject->bControlled) DestroyObject2(ptrDependingObject->ssName, false, rlstDeferredObjectDestruction); } }; + + // Static dependance from object class fnDestroyDependingObjects(GetDependingObjectInstancesByClass(ptrObjectEntry->sClassInfo.ssName)); if (ptrObjectEntry->sClassInfo.ssName != ptrObjectEntry->sClassInfo.ssDefaultObjectName) fnDestroyDependingObjects(GetDependingObjectInstancesByClass(ptrObjectEntry->sClassInfo.ssDefaultObjectName)); + // Dynamic dependance from object entry + std::vector vecDependingObjects; + for (const std::string& rssDependingObject : ptrObjectEntry->setDependentObjects) + { + auto itObject = m_mapServiceObjects.find(rssDependingObject); + if (itObject != m_mapServiceObjects.end()) + vecDependingObjects.push_back((*itObject->second)->tObjectID); + } + fnDestroyDependingObjects(vecDependingObjects); + // Shutdown the object (not all objects expose IObjectControl). auto* pObjectControl = ptrObjectEntry->ptrObject.GetInterface(); if (pObjectControl && pObjectControl->GetObjectState() != sdv::EObjectState::destruction_pending) @@ -735,7 +913,8 @@ bool CRepository::DestroyObject2(/*in*/ const sdv::u8string& ssObjectName) sdv::IInterfaceAccess* pObject = ptrObjectEntry->ptrObject; if (ptrObjectEntry->ptrIsoMon) pObject = ptrObjectEntry->ptrIsoMon->GetContainedObject(); - ptrObjectEntry->ptrModule->DestroyObject(pObject); + rlstDeferredObjectDestruction.push_back(std::make_pair(ptrObjectEntry, pObject)); + //ptrObjectEntry->ptrModule->DestroyObject(pObject); } return true; @@ -805,15 +984,22 @@ sdv::core::TObjectID CRepository::RegisterObject(IInterfaceAccess* pObjectIfc, c return tObjectID; } -void CRepository::LinkCoreRepository(/*in*/ sdv::IInterfaceAccess* pCoreRepository) +sdv::core::TLinkID CRepository::LinkCoreRepository(/*in*/ sdv::IInterfaceAccess* pCoreRepository) { - if (!pCoreRepository) return; - m_ptrCoreRepoAccess = pCoreRepository; + if (!pCoreRepository) return 0; + std::unique_lock lock(m_mtxObjects); + sdv::core::TLinkID tLinkID = CreateObjectID(); + m_lstCoreRepoAccess.push_back(std::make_pair(tLinkID, pCoreRepository)); + return tLinkID; } -void CRepository::UnlinkCoreRepository() +void CRepository::UnlinkCoreRepository(sdv::core::TLinkID tLinkID) { - m_ptrCoreRepoAccess = nullptr; + std::unique_lock lock(m_mtxObjects); + auto itLink = std::find_if( + m_lstCoreRepoAccess.begin(), m_lstCoreRepoAccess.end(), [&](const auto rprLink) + { return rprLink.first == tLinkID; }); + m_lstCoreRepoAccess.erase(itLink); } sdv::SClassInfo CRepository::FindClass(/*in*/ const sdv::u8string& ssClassName) const @@ -824,16 +1010,34 @@ sdv::SClassInfo CRepository::FindClass(/*in*/ const sdv::u8string& ssClassName) if (GetAppSettings().IsMainApplication() || GetAppSettings().IsIsolatedApplication()) { auto optManifest = GetAppConfig().FindInstalledComponent(ssClassName); - if (!optManifest) return {}; - return *optManifest; + if (optManifest) + return *optManifest; + } + else + { + // Get the information through the module. + auto ptrModule = GetModuleControl().FindModuleByClass(ssClassName); + if (ptrModule) + { + auto optClassInfo = ptrModule->GetClassInfo(ssClassName); + if (optClassInfo) return *optClassInfo; + } } - // Get the information through the module. - auto ptrModule = GetModuleControl().FindModuleByClass(ssClassName); - if (!ptrModule) return {}; - auto optClassInfo = ptrModule->GetClassInfo(ssClassName); - if (!optClassInfo) return {}; - return *optClassInfo; + // Copy the linked repository and then unlock; could cause a deadlock instead. + auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess; + lock.unlock(); + + // If there is a linked core, use the core to get class list + for (auto& rprLink : lstCoreRepoAccessCopy) + { + const sdv::core::IRepositoryInfo* pCoreRepoInfo = rprLink.second.GetInterface(); + sdv::SClassInfo sInfo = pCoreRepoInfo->FindClass(ssClassName); + if (!sInfo.ssName.empty()) return sInfo; + } + + // Nothing found + return {}; } sdv::sequence CRepository::GetObjectList() const @@ -841,13 +1045,6 @@ sdv::sequence CRepository::GetObjectList() const std::shared_lock lock(m_mtxObjects); sdv::sequence seqObjects; - // If there is a linked core, use the core to get object list - if (m_ptrCoreRepoAccess) - { - const sdv::core::IRepositoryInfo* pCoreRepoInfo = m_ptrCoreRepoAccess.GetInterface(); - return pCoreRepoInfo ? pCoreRepoInfo->GetObjectList() : seqObjects; - } - // Add object function auto fnAddObject = [&seqObjects](const std::shared_ptr& rptrObjectEntry) { @@ -878,6 +1075,21 @@ sdv::sequence CRepository::GetObjectList() const for (const auto& prLocalObject : m_mapLocalObjects) fnAddObject(prLocalObject.second); + // Copy the linked repository and then unlock; could cause a deadlock instead. + auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess; + lock.unlock(); + + // If there is a linked core, use the core to get object list + for (auto& rprLink : lstCoreRepoAccessCopy) + { + const sdv::core::IRepositoryInfo* pCoreRepoInfo = rprLink.second.GetInterface(); + if (pCoreRepoInfo) + { + auto seqListRemote = pCoreRepoInfo->GetObjectList(); + seqObjects.insert(seqObjects.end(), seqListRemote.begin(), seqListRemote.end()); + } + } + return seqObjects; } @@ -885,54 +1097,78 @@ sdv::core::SObjectInfo CRepository::GetObjectInfo(/*in*/ sdv::core::TObjectID tO { std::shared_lock lock(m_mtxObjects); - // If there is a linked core, use the core to get object info - if (m_ptrCoreRepoAccess) + // Use the internal object map for this. + sdv::core::SObjectInfo sInfo{}; + auto itObject = m_mapObjects.find(tObjectID); + if (itObject != m_mapObjects.end()) { - const sdv::core::IRepositoryInfo* pCoreRepoInfo = m_ptrCoreRepoAccess.GetInterface(); - return pCoreRepoInfo ? pCoreRepoInfo->GetObjectInfo(tObjectID) : sdv::core::SObjectInfo{}; + if (!itObject->second) return {}; + + // Fill in information + sInfo.tObjectID = itObject->second->tObjectID; + sInfo.tModuleID = itObject->second->ptrModule ? itObject->second->ptrModule->GetModuleID() : 0; + sInfo.sClassInfo = itObject->second->sClassInfo; + sInfo.ssObjectName = itObject->second->ssName; + sInfo.ssObjectConfig = itObject->second->ssConfig; + sInfo.uiFlags = 0; + if (itObject->second->bControlled) + sInfo.uiFlags |= static_cast(sdv::core::EObjectInfoFlags::object_controlled); + if (!itObject->second->ptrModule) + sInfo.uiFlags |= static_cast(sdv::core::EObjectInfoFlags::object_foreign); + if (!itObject->second->bIsolated) + sInfo.uiFlags |= static_cast(sdv::core::EObjectInfoFlags::object_isolated); + + return sInfo; } - // Use the internal object map for this. - auto itObject = m_mapObjects.find(tObjectID); - if (itObject == m_mapObjects.end()) return {}; - if (!itObject->second) return {}; + // Copy the linked repository and then unlock; could cause a deadlock instead. + auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess; + lock.unlock(); - // Fill in information - sdv::core::SObjectInfo sInfo{}; - sInfo.tObjectID = itObject->second->tObjectID; - sInfo.tModuleID = itObject->second->ptrModule ? itObject->second->ptrModule->GetModuleID() : 0; - sInfo.sClassInfo = itObject->second->sClassInfo; - sInfo.ssObjectName = itObject->second->ssName; - sInfo.ssObjectConfig = itObject->second->ssConfig; - sInfo.uiFlags = 0; - if (itObject->second->bControlled) - sInfo.uiFlags |= static_cast(sdv::core::EObjectInfoFlags::object_controlled); - if (!itObject->second->ptrModule) - sInfo.uiFlags |= static_cast(sdv::core::EObjectInfoFlags::object_foreign); - if (!itObject->second->bIsolated) - sInfo.uiFlags |= static_cast(sdv::core::EObjectInfoFlags::object_isolated); + // If there is a linked core, use the core to get object info + for (auto& rprLink : lstCoreRepoAccessCopy) + { + const sdv::core::IRepositoryInfo* pCoreRepoInfo = rprLink.second.GetInterface(); + if (pCoreRepoInfo) + { + sInfo = pCoreRepoInfo->GetObjectInfo(tObjectID); + if (sInfo.tObjectID == tObjectID) return sInfo; + } + } - return sInfo; + // Info not found + return {}; } sdv::core::SObjectInfo CRepository::FindObject(/*in*/ const sdv::u8string& ssObjectName) const { std::shared_lock lock(m_mtxObjects); - // If there is a linked core, use the core to get object info - if (m_ptrCoreRepoAccess) - { - const sdv::core::IRepositoryInfo* pCoreRepoInfo = m_ptrCoreRepoAccess.GetInterface(); - return pCoreRepoInfo ? pCoreRepoInfo->FindObject(ssObjectName) : sdv::core::SObjectInfo{}; - } - // Use internal service map for this. auto itService = m_mapServiceObjects.find(ssObjectName); - if (itService == m_mapServiceObjects.end()) return {}; - auto ptrObjectEntry = *itService->second; - if (!ptrObjectEntry) return {}; + if (itService != m_mapServiceObjects.end()) + { + auto ptrObjectEntry = *itService->second; + if (!ptrObjectEntry) return {}; + return GetObjectInfo(ptrObjectEntry->tObjectID); + } + + // Copy the linked repository and then unlock; could cause a deadlock instead. + auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess; lock.unlock(); - return GetObjectInfo(ptrObjectEntry->tObjectID); + + // If there is a linked core, use the core to get object info + for (auto& rprLink : lstCoreRepoAccessCopy) + { + const sdv::core::IRepositoryInfo* pCoreRepoInfo = rprLink.second.GetInterface(); + if (pCoreRepoInfo) + { + sdv::core::SObjectInfo sInfo = pCoreRepoInfo->FindObject(ssObjectName); + if (sInfo.tObjectID) return sInfo; + } + } + + return {}; } void CRepository::OnDestroyObject(sdv::IInterfaceAccess* pObject) @@ -998,9 +1234,10 @@ void CRepository::DestroyAllObjects(const std::vector& rvecIgnoreOb lock.unlock(); // Destroy the objects in reverse order + TDeferredObjectDestructionList lstDeferredObjectDestruction; while (lstCopy.size()) { - std::string ssObjectName = lstCopy.back() ? lstCopy.back()->ssName : std::string{}; + sdv::u8string ssObjectName = lstCopy.back() ? lstCopy.back()->ssName : sdv::u8string{}; lstCopy.pop_back(); if (ssObjectName.empty()) continue; @@ -1009,7 +1246,7 @@ void CRepository::DestroyAllObjects(const std::vector& rvecIgnoreOb continue; // Destroy the object... -> call the repository function to remove it from the list. - DestroyObject(ssObjectName); + DestroyObject2(ssObjectName, true, lstDeferredObjectDestruction); // Needs force! if (bForce) @@ -1038,6 +1275,34 @@ void CRepository::DestroyAllObjects(const std::vector& rvecIgnoreOb lock.unlock(); } } + for (auto& rprObject : lstDeferredObjectDestruction) + rprObject.first->ptrModule->DestroyObject(rprObject.second); +} + +void CRepository::AddObjectDependency(/*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssDependsOnObject) +{ + std::unique_lock lock(m_mtxObjects); + auto itService = m_mapServiceObjects.find(ssDependsOnObject); + if (itService != m_mapServiceObjects.end()) + { + auto ptrObjectEntry = *itService->second; + if (!ptrObjectEntry) return; + auto itDependsOn = ptrObjectEntry->setDependentObjects.find(ssObjectName); + if (itDependsOn == ptrObjectEntry->setDependentObjects.end()) + ptrObjectEntry->setDependentObjects.insert(ssObjectName); + } +} + +void CRepository::RemoveObjectDependency(/*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssDependsOnObject) +{ + std::unique_lock lock(m_mtxObjects); + auto itService = m_mapServiceObjects.find(ssDependsOnObject); + if (itService != m_mapServiceObjects.end()) + { + auto ptrObjectEntry = *itService->second; + if (!ptrObjectEntry) return; + ptrObjectEntry->setDependentObjects.erase(ssObjectName); + } } void CRepository::ResetConfigBaseline() @@ -1100,8 +1365,9 @@ sdv::core::EConfigProcessResult CRepository::StartFromConfig(const CAppConfigFil // Destroy the objects when an error occurred during loading. if (!bAllowPartialLoad && nFail) { + TDeferredObjectDestructionList lstDeferredObjectDestruction; for (const std::string& rssName : vecLoadedObjects) - DestroyObject(rssName); + DestroyObject2(rssName, true, lstDeferredObjectDestruction); return sdv::core::EConfigProcessResult::failed; } @@ -1185,11 +1451,14 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs sdv::TInterfaceAccessPtr(GetObject("CommunicationControl")).GetInterface(); if (!pConnectionControl) return 0; sdv::u8string ssConnectionString; + std::optional optPermissionObject = + GetPermissionControl().CreatePermissionObject(sdv::core::EAccessPermission::local_access); sdv::com::TConnectionID tConnection = pConnectionControl->CreateServerConnection(sdv::com::EChannelType::local_channel, sdv::core::GetObject("RepositoryService"), 5000, ssConnectionString); if (!tConnection.uiControl) return 0; if (ssConnectionString.empty()) return 0; + optPermissionObject.reset(); // Create the isolation process configuration std::stringstream sstreamConfig; @@ -1298,7 +1567,10 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptrptrModule->DestroyObject(rprObject.second); return 0; } @@ -1306,7 +1578,7 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr(); if (pObjectControl) { - pObjectControl->Initialize(rssObjectConfig); + pObjectControl->Initialize(*ptrObjectEntry); if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized) { // Shutdown the object (even if the initialization didn't work properly). @@ -1315,8 +1587,8 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptrDestroyObject(ptrObject); - // Destroy the object again - DestroyObject(rssObjectName); + //// Destroy the object again + //DestroyObject2(rssObjectName, true); return 0; } switch (GetAppControl().GetOperationState()) @@ -1350,14 +1622,7 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr(time(0))); - tCurrent = 0; - while (!tCurrent) tCurrent = std::rand(); - } - return ++tCurrent; + return m_idgen.Generate(); } std::vector CRepository::GetDependingObjectInstancesByClass(const std::string& rssClass) @@ -1378,6 +1643,7 @@ std::vector CRepository::GetDependingObjectInstancesByClas bool CRepositoryService::EnableRepositoryObjectControl() { return GetAppSettings().IsMainApplication() || + GetAppSettings().IsMaintenanceApplication() || GetAppSettings().IsStandaloneApplication() || GetAppSettings().IsEssentialApplication() || GetAppSettings().IsIsolatedApplication(); diff --git a/sdv_services/core/repository.h b/sdv_services/core/repository.h index 448e099..2bd9d82 100644 --- a/sdv_services/core/repository.h +++ b/sdv_services/core/repository.h @@ -27,6 +27,7 @@ #include "object_lifetime_control.h" #include "iso_monitor.h" #include "app_config_file.h" +#include "../../global/unique_id.h" /** * @brief repository service providing functionality to load modules, create objects and access exiting objects @@ -34,7 +35,8 @@ class CRepository : public sdv::IInterfaceAccess, public sdv::core::IObjectAccess, public sdv::core::IRepositoryUtilityCreate, public sdv::core::IRepositoryMarshallCreate, public sdv::core::IRepositoryControl, public sdv::core::IRegisterForeignObject, - public sdv::core::IRepositoryInfo, public IObjectDestroyHandler, public sdv::core::ILinkCoreRepository + public sdv::core::IRepositoryInfo, public IObjectDestroyHandler, public sdv::core::ILinkCoreRepository, + public sdv::core::IObjectDependency { public: /** @@ -45,6 +47,7 @@ public: // Interface map BEGIN_SDV_INTERFACE_MAP() SDV_INTERFACE_ENTRY(sdv::core::IObjectAccess) + SDV_INTERFACE_ENTRY(sdv::core::IObjectDependency) SDV_INTERFACE_ENTRY(sdv::core::IRepositoryMarshallCreate) SDV_INTERFACE_ENTRY(sdv::core::IRepositoryUtilityCreate) END_SDV_INTERFACE_MAP() @@ -102,7 +105,6 @@ public: */ virtual sdv::IInterfaceAccess* CreateStubObject(/*in*/ sdv::interface_id id) override; -protected: /** * @brief Create an object and all its objects it depends on. Overload of Overload of * sdv::core::IRepositoryControl::CreateObject. @@ -127,7 +129,7 @@ protected: */ virtual sdv::core::TObjectID CreateObject(/*in*/ const sdv::u8string& ssClassName, /*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssObjectConfig) override; -public: + /** * @brief Creates an object from a previously loaded module. Provide the module ID to explicitly define what module to * use during object creation. Overload of sdv::core::IRepositoryControl::CreateObjectFromModule. @@ -145,7 +147,6 @@ public: /*in*/ const sdv::u8string& ssClassName, /*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssObjectConfig) override; -protected: /** * @brief Destroy a previously created object with the supplied name. Overload of sdv::core::IRepositoryControl::DestroyObject. * @details For standalone and essential applications previously created system, device and service objects can be @@ -156,7 +157,29 @@ protected: */ virtual bool DestroyObject(/*in*/ const sdv::u8string& ssObjectName) override; -public: +private: + /** + * @brief Object entry + * @details The object instance information. Objects that are running remotely are represented by their proxy. Objects can have + * multiple references by stubs. + */ + struct SObjectEntry : sdv::SObjectInfo + { + sdv::TInterfaceAccessPtr ptrObject; ///< Object interface (could be proxy). + std::shared_ptr ptrModule; ///< Module instance. + bool bControlled = false; ///< When set, the object is controlled. + bool bIsolated = false; ///< When set, the object is isolated and running in another process. + std::mutex mtxConnect; ///< Mutex used to wait for connection + std::condition_variable cvConnect; ///< Condition variable used to wait for connection + std::shared_ptr ptrIsoMon; ///< Object being monitored for shutdown. + std::set setDependentObjects; ///< List of dependent objects (needed during shutdown). + }; + + /** + * @brief List for collected objects ready for destruction. + */ + using TDeferredObjectDestructionList = std::list, sdv::IInterfaceAccess*>>; + /** * @brief Create an object and all its objects it depends on. Internal function not accessible through the interface. * @param[in] ssClassName The name of the object class to be created. For the main application, the class string could @@ -166,12 +189,13 @@ public: * the class name. Use the returned object ID to request the name of the object. * @param[in] ssObjectConfig Optional configuration handed over to the object upon creation via IObjectControl. Only * valid for standalone, essential and isolated applications. + * @param[in] bIndirect Set when the function was called through a call from GetObject. * @return Returns the object ID when the object creation was successful or 0 when not. On success the object is * available through the IObjectAccess interface. If the object already exists (class and object names are identical), * the object ID of the existing object is returned. */ - sdv::core::TObjectID CreateObject2(/*in*/ const sdv::u8string& ssClassName, /*in*/ const sdv::u8string& ssObjectName, - /*in*/ const sdv::u8string& ssObjectConfig); + sdv::core::TObjectID CreateObject2(const sdv::u8string& ssClassName, const sdv::u8string& ssObjectName, + const sdv::u8string& ssObjectConfig, bool bIndirect); /** * @brief Destroy a previously created object with the supplied name. Internal function not accessible through the interface. @@ -179,10 +203,14 @@ public: * destroyed. For the main and isolated applications, only the complex service can be destroyed. For isolated * applications a destruction of the object will end the application. * @param[in] ssObjectName The name of the object to destroy. + * @param[in] bIndirect Set when the function was not call directly through DestroyObject. + * @param[in] rlstDeferredObjectDestruction Reference to the list of objects to be filled with objects to be destroyed. * @return Returns whether the object destruction was successful. */ - bool DestroyObject2(/*in*/ const sdv::u8string& ssObjectName); + bool DestroyObject2(const sdv::u8string& ssObjectName, bool bIndirect, + TDeferredObjectDestructionList& rlstDeferredObjectDestruction); +public: /** * @brief Register as foreign object and make it public to the system with the given name. Overload of * sdv::core::IRegisterForeignObject::RegisterObject. @@ -196,15 +224,17 @@ public: /*in*/ const sdv::u8string& ssObjectName) override; /** - * @brief Register the core repository. + * @brief Register the core repository. Overload of sdv::core::ILinkCoreRepository::LinkCoreRepository. * @param[in] pCoreRepository Pointer to the proxy interface of the core repository. + * @return Returns a link ID to be used in the Unlink function. Or 0 on failure. */ - virtual void LinkCoreRepository(/*in*/ sdv::IInterfaceAccess* pCoreRepository) override; + virtual sdv::core::TLinkID LinkCoreRepository(/*in*/ sdv::IInterfaceAccess* pCoreRepository) override; /** - * @brief Unlink a previously linked core repository. + * @brief Unlink a previously linked core repository. Overload of sdv::core::ILinkCoreRepository::UnlinkCoreRepository. + * @param[in] tLinkID Link ID of the repository link to remove. */ - virtual void UnlinkCoreRepository() override; + virtual void UnlinkCoreRepository(sdv::core::TLinkID tLinkID) override; /** * @brief Find the class information of an object with the supplied name. Overload of @@ -256,6 +286,20 @@ public: */ void DestroyAllObjects(const std::vector&rvecIgnoreObjects, bool bForce = false); + /** + * @brief Add a dependency for an object. Overload of sdv::core::IObjectDependency::AddObjectDependency. + * @param[in] ssObjectName Name of the object. + * @param[in] ssDependsOnObject Name of the object it depends on. + */ + void AddObjectDependency(/*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssDependsOnObject) override; + + /** + * @brief Remove an object dependency. Overload of sdv::core::IObjectDependency::RemoveObjectDependency. + * @param[in] ssObjectName Name of the object. + * @param[in] ssDependsOnObject Name of the object it depends on. + */ + void RemoveObjectDependency(/*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssDependsOnObject) override; + /** * @brief Reset the current config baseline. */ @@ -306,7 +350,7 @@ private: * @brief Create a new unique object ID. * @return The created object ID. */ - static sdv::core::TObjectID CreateObjectID(); + sdv::core::TObjectID CreateObjectID(); /** * @brief Get a list of depending object instances of a specific class. @@ -315,26 +359,6 @@ private: */ std::vector GetDependingObjectInstancesByClass(const std::string& rssClass); - /** - * @brief Object entry - * @details The object instance information. Objects that are running remotely are represented by their proxy. Objects can have - * multiple references by stubs. - */ - struct SObjectEntry - { - sdv::core::TObjectID tObjectID = 0; ///< Object ID (local to this process). - sdv::SClassInfo sClassInfo; ///< Object class name. - std::string ssName; ///< Object name (can be zero with local objects). - std::string ssConfig; ///< Object configuration. - sdv::TInterfaceAccessPtr ptrObject; ///< Object interface (could be proxy). - std::shared_ptr ptrModule; ///< Module instance. - bool bControlled = false; ///< When set, the object is controlled. - bool bIsolated = false; ///< When set, the object is isolated and running in another process. - std::mutex mtxConnect; ///< Mutex used to wait for connection - std::condition_variable cvConnect; ///< Condition variable used to wait for connection - std::shared_ptr ptrIsoMon; ///< Object being monitored for shutdown. - }; - using TObjectMap = std::map>; using TOrderedObjectList = std::list>; using TObjectIDList = std::list>; @@ -350,11 +374,11 @@ private: TIsolationMap m_mapIsolatedObjects; ///< Map with isolated objects. TObjectMap m_mapObjects; ///< Map with all objects indexed by the object ID. TConfigSet m_setConfigObjects; ///< Set with the objects for storing in the configuration. - sdv::TInterfaceAccessPtr m_ptrCoreRepoAccess; ///< Linked core repository access (proxy interface). + std::list> m_lstCoreRepoAccess; ///< List to Linked core repositories. bool m_bIsoObjectLoaded = false; ///< When set, the isolated object has loaded. Do not allow ///< another object of type complex service or utility to be ///< created. - /// + CUniqueID m_idgen; ///< Unique ID generator. }; /** diff --git a/sdv_services/core/sdv_core.h b/sdv_services/core/sdv_core.h index eee40ef..a613a83 100644 --- a/sdv_services/core/sdv_core.h +++ b/sdv_services/core/sdv_core.h @@ -23,6 +23,7 @@ #include "logger_control.h" #include "logger.h" #include "app_config.h" +#include "permission_control.h" /** * @brief SDV core instance class containing containing the instances for the core services. @@ -47,6 +48,7 @@ public: SDV_INTERFACE_CHAIN_MEMBER(GetMemoryManager()) SDV_INTERFACE_CHAIN_MEMBER(GetRepository()) SDV_INTERFACE_CHAIN_MEMBER(GetLoggerControl()) + SDV_INTERFACE_CHAIN_MEMBER(GetPermissionControl()) END_SDV_INTERFACE_MAP() /** diff --git a/sdv_services/core/toml_parser/code_snippet.cpp b/sdv_services/core/toml_parser/code_snippet.cpp index 9bf1654..1fbfb01 100644 --- a/sdv_services/core/toml_parser/code_snippet.cpp +++ b/sdv_services/core/toml_parser/code_snippet.cpp @@ -37,7 +37,8 @@ namespace toml_parser TTokenListIterator it = m_lstTokens.begin(); bool bCommaAvailable = false; bool bPrintableCharsAvailable = false; - bool bLastTokenIsComment = false; + bool bLastTokenIsCommentOrForceNewline = false; + bool bNewlineAllowed = rContext.NewlineAllowed() || (eMode == EComposeMode::compose_behind && rContext.FinalNewline()); while (it != m_lstTokens.end() && it->Category() != ETokenCategory::token_comment) { // Add comma only when needed. @@ -53,12 +54,12 @@ namespace toml_parser bCommaAvailable = true; break; case ETokenCategory::token_comment: - bSkip = !rContext.CommentAndNewlineAllowed(); - if (!bSkip) bLastTokenIsComment = true; + bSkip = !bNewlineAllowed; + if (!bSkip) bLastTokenIsCommentOrForceNewline = true; break; case ETokenCategory::token_syntax_new_line: - bSkip = !rContext.NewlineAllowed(); - if (!bSkip) bLastTokenIsComment = false; + bSkip = !bNewlineAllowed; + if (!bSkip) bLastTokenIsCommentOrForceNewline = false; break; default: break; @@ -107,11 +108,11 @@ namespace toml_parser break; case ETokenCategory::token_comment: bSkip = !rContext.CommentAndNewlineAllowed(); - if (!bSkip) bLastTokenIsComment = true; + if (!bSkip) bLastTokenIsCommentOrForceNewline = true; break; case ETokenCategory::token_syntax_new_line: - bSkip = !rContext.NewlineAllowed(); - if (!bSkip) bLastTokenIsComment = false; + bSkip = !bNewlineAllowed; + if (!bSkip) bLastTokenIsCommentOrForceNewline = false; break; default: break; @@ -210,11 +211,11 @@ namespace toml_parser break; case ETokenCategory::token_comment: bSkip = !rContext.CommentAndNewlineAllowed(); - if (!bSkip) bLastTokenIsComment = true; + if (!bSkip) bLastTokenIsCommentOrForceNewline = true; break; case ETokenCategory::token_syntax_new_line: - bSkip = !rContext.NewlineAllowed(); - if (!bSkip) bLastTokenIsComment = false; + bSkip = !bNewlineAllowed; + if (!bSkip) bLastTokenIsCommentOrForceNewline = false; break; default: break; @@ -239,7 +240,7 @@ namespace toml_parser } // Default newline needed - if (rContext.FinalNewline() && ((m_lstTokens.empty() && m_ssComment.empty()) || bLastTokenIsComment)) + if (rContext.FinalNewline() && ((m_lstTokens.empty() && m_ssComment.empty()) || bLastTokenIsCommentOrForceNewline)) sstream << std::endl; break; case EComposeMode::compose_before: diff --git a/sdv_services/core/toml_parser/miscellaneous.cpp b/sdv_services/core/toml_parser/miscellaneous.cpp index 02208ac..a06ef5c 100644 --- a/sdv_services/core/toml_parser/miscellaneous.cpp +++ b/sdv_services/core/toml_parser/miscellaneous.cpp @@ -16,6 +16,8 @@ #include "exception.h" #include #include +#include +#include "parser_toml.h" namespace toml_parser { @@ -560,4 +562,21 @@ namespace toml_parser return sstreamQuotedText.str(); } } + + bool CompareEqual(const std::string& rssToml1, const std::string& rssToml2) + { + try + { + CParser parser1(rssToml1); + CParser parser2(rssToml2); + sdv::toml::CNodeCollection collection1(&parser1.Root()); + sdv::toml::CNodeCollection collection2(&parser2.Root()); + return sdv::toml::internal::CompareNodes(collection1, collection2) == sdv::toml::ECompareResult::compare_identical; + } + catch (const toml_parser::XTOMLParseException&) + { + return false; + } + } + } // namespace toml_parser \ No newline at end of file diff --git a/sdv_services/core/toml_parser/miscellaneous.h b/sdv_services/core/toml_parser/miscellaneous.h index cfa2885..b3a6402 100644 --- a/sdv_services/core/toml_parser/miscellaneous.h +++ b/sdv_services/core/toml_parser/miscellaneous.h @@ -108,6 +108,16 @@ namespace toml_parser * @return The quoted key (if applicable); otherwise the key without quotes. */ std::string QuoteText(const std::string& rssText, EQuoteRequest eQuoteRequest = EQuoteRequest::smart_text); + + /** + * @brief Compare the content of TOML string with the content of another TOML string. Use internal TOML parser for the + * comparison. + * @param[in] rssToml1 Reference to the first TOML string to compare with the second TOML string. + * @param[in] rssToml2 Reference to the second TOML string to compare with the first TOML string. + * @return The comparison result. + */ + bool CompareEqual(const std::string& rssToml1, const std::string& rssToml2); + } // namespace toml_parser #endif // !defined MISCELLANEOUS_H \ No newline at end of file diff --git a/sdv_services/core/toml_parser/parser_node_indexer.cpp b/sdv_services/core/toml_parser/parser_node_indexer.cpp new file mode 100644 index 0000000..6276907 --- /dev/null +++ b/sdv_services/core/toml_parser/parser_node_indexer.cpp @@ -0,0 +1,145 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include "parser_node_indexer.h" +#include +#include + +namespace toml_parser +{ + CNodeIndex::CNodeIndex(CIndexList& rIndexList, CIndexListIterator itPos) : m_ptrPos(std::make_shared(rIndexList, itPos)) + {} + + CNodeIndex::~CNodeIndex() + { + m_ptrPos.reset(); + } + + CNodeIndex::CNodeIndex(const CNodeIndex& rIndex) : m_ptrPos(rIndex.m_ptrPos) + {} + + CNodeIndex::CNodeIndex(CNodeIndex&& rIndex) : m_ptrPos(std::move(rIndex.m_ptrPos)) + {} + + CNodeIndex& CNodeIndex::operator=(const CNodeIndex& rIndex) + { + m_ptrPos = rIndex.m_ptrPos; + return *this; + } + + CNodeIndex& CNodeIndex::operator=(CNodeIndex&& rIndex) + { + m_ptrPos = std::move(rIndex.m_ptrPos); + return *this; + } + + bool CNodeIndex::operator==(const CNodeIndex& rIndex) const + { + return rIndex.m_ptrPos == m_ptrPos; + } + + bool CNodeIndex::operator!=(const CNodeIndex& rIndex) const + { + return !operator==(rIndex); + } + + bool CNodeIndex::operator<(const CNodeIndex& rIndex) const + { + if (!m_ptrPos) return false; + if (!rIndex.m_ptrPos) return true; + return m_ptrPos->Index() < rIndex.m_ptrPos->Index(); + } + + bool CNodeIndex::operator<=(const CNodeIndex& rIndex) const + { + if (!m_ptrPos) return false; + if (!rIndex.m_ptrPos) return true; + return rIndex.m_ptrPos == m_ptrPos || m_ptrPos->Index() < rIndex.m_ptrPos->Index(); + } + + bool CNodeIndex::operator>(const CNodeIndex& rIndex) const + { + if (!m_ptrPos) return true; + if (!rIndex.m_ptrPos) return false; + return m_ptrPos->Index() > rIndex.m_ptrPos->Index(); + } + + bool CNodeIndex::operator>=(const CNodeIndex& rIndex) const + { + if (!m_ptrPos) return true; + if (!rIndex.m_ptrPos) return false; + return rIndex.m_ptrPos == m_ptrPos || m_ptrPos->Index() > rIndex.m_ptrPos->Index(); + } + + CNodeIndex::operator bool() const + { + return m_ptrPos ? true : false; + } + + void CNodeIndex::MoveBefore(const CNodeIndex& rIndex) + { + if (m_ptrPos && rIndex.m_ptrPos) + m_ptrPos->MoveBeforeIndex(*rIndex.m_ptrPos); + } + + uint32_t CNodeIndex::Index() const + { + if (!m_ptrPos) return sdv::toml::npos; + return m_ptrPos->Index(); + } + + CNodeIndex::CIteratorWrapper::CIteratorWrapper(CIndexList& rIndexList, CIndexListIterator itPos) : + m_rIndexList(rIndexList), m_itPos(itPos) + {} + + CNodeIndex::CIteratorWrapper ::~CIteratorWrapper() + { + m_rIndexList.erase(m_itPos); + } + + uint32_t CNodeIndex::CIteratorWrapper::Index() const + { + return static_cast(std::distance(m_rIndexList.cbegin(), m_itPos)); + } + + void CNodeIndex::CIteratorWrapper::MoveBeforeIndex(const CIteratorWrapper& ritTarget) + { + m_rIndexList.splice(ritTarget.m_itPos, m_rIndexList, m_itPos); + } + + // Global index list + CIndexList CNodeIndexer::m_lstIndexList; + + CNodeIndexer::CNodeIndexer() + {} + + CNodeIndex CNodeIndexer::CreateIndex() + { + auto itPos = m_lstIndexList.insert(m_lstIndexList.end(), SNodeIndexElement()); + CNodeIndex index(m_lstIndexList, itPos); + return index; + } + + CNodeIndex CNodeIndexer::CreateIndex(const CNodeIndex& rInsertBefore) + { + CNodeIndex node_index = CreateIndex(); + node_index.MoveBefore(rInsertBefore); + return node_index; + } + + size_t CNodeIndexer::Count() + { + return m_lstIndexList.size(); + } + +} // namespace toml_parser \ No newline at end of file diff --git a/sdv_services/core/toml_parser/parser_node_indexer.h b/sdv_services/core/toml_parser/parser_node_indexer.h new file mode 100644 index 0000000..297fd81 --- /dev/null +++ b/sdv_services/core/toml_parser/parser_node_indexer.h @@ -0,0 +1,227 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#ifndef PARSER_NODE_INDEXER_H +#define PARSER_NODE_INDEXER_H + +#include +#include +#include + +namespace toml_parser +{ + class CNodeIndex; + class CNodeIndexer; + /// Placeholder index element + struct SNodeIndexElement {}; + /// Index list + using CIndexList = std::list; + /// Const iterator to an element in the index list + using CIndexListIterator = CIndexList::const_iterator; + + /** + * @brief Node index object used to manage the node order. + */ + class CNodeIndex + { + friend CNodeIndexer; ///< CNodeIndex uses friend access to create the node index and assign the iterator holding the + ///< the position within the index list. + private: + /** + * @brief Constructor of the node index object. Use the CNodeIndexer::CreateIndex function to create the object. + * @param[in] rIndexList Reference to the index list. + * @param[in] itPos Iterator to the position in the index list. + */ + CNodeIndex(CIndexList& rIndexList, CIndexListIterator itPos); + + public: + /** + * @brief Destructor + */ + virtual ~CNodeIndex(); + + /** + * @brief Copy constructor of the node index object. + * @param[in] rIndex Reference to the index object to copy from. + */ + CNodeIndex(const CNodeIndex& rIndex); + + /** + * @brief Move constructor of the node index object. + * @param[in] rIndex Reference to the index object to move from. + */ + CNodeIndex(CNodeIndex&& rIndex); + + /** + * @brief Copy assignment operator of the node index object. + * @param[in] rIndex Reference to the index object to copy from. + * @return Returns a reference to this object. + */ + CNodeIndex& operator=(const CNodeIndex& rIndex); + + /** + * @brief Move assignment operator of the node index object. + * @param[in] rIndex Reference to the index object to move from. + * @return Returns a reference to this object. + */ + CNodeIndex& operator=(CNodeIndex&& rIndex); + + /** + * @brief Compare this node index object to the supplied index object and return whether the position of this object is + * identical to the position of the supplied object. + * @param[in] rIndex Reference to the node index object to compare with. + * @return Returns whether the provided index is identical to this index. + */ + bool operator==(const CNodeIndex& rIndex) const; + + /** + * @brief Compare this node index object to the supplied index object and return whether the position of this object is + * not identical to the position of the supplied object. + * @param[in] rIndex Reference to the node index object to compare with. + * @return Returns whether the provided index is different than this index. + */ + bool operator!=(const CNodeIndex& rIndex) const; + + /** + * @brief Compare this node index object to the supplied index object and return whether the position of this object is + * smaller than the position of the supplied object. + * @param[in] rIndex Reference to the node index object to compare with. + * @return Returns whether the provided index is smaller than this index. + */ + bool operator<(const CNodeIndex& rIndex) const; + + /** + * @brief Compare this node index object to the supplied index object and return whether the position of this object is + * smaller than or equal to the position of the supplied object. + * @param[in] rIndex Reference to the node index object to compare with. + * @return Returns whether the provided index is smaller than or equal to this index. + */ + bool operator<=(const CNodeIndex& rIndex) const; + + /** + * @brief Compare this node index object to the supplied index object and return whether the position of this object is + * larger than the position of the supplied object. + * @param[in] rIndex Reference to the node index object to compare with. + * @return Returns whether the provided index is larger than this index. + */ + bool operator>(const CNodeIndex& rIndex) const; + + /** + * @brief Compare this node index object to the supplied index object and return whether the position of this object is + * larger than or equal to the position of the supplied object. + * @param[in] rIndex Reference to the node index object to compare with. + * @return Returns whether the provided index is larger than or equal to this index. + */ + bool operator>=(const CNodeIndex& rIndex) const; + + /** + * @brief Check for validity. + * @return Returns whether the index object contains a valid index. + */ + operator bool() const; + + /** + * @brief Move the node index object before the index object provided as an argument. + * @param[in] rIndex Reference to the node index object to move the object before. + */ + void MoveBefore(const CNodeIndex& rIndex); + + /** + * @brief Get the index in the list. Returns sdv::toml::npos if the index is not in the list any more. + * @remarks This index os calculated dynamically and can change if indices are added, removed or swapped. + * @return The current index in the list. + */ + uint32_t Index() const; + + private: + /** + * @brief Iterator wrapper object with lifetime management. + * @remarks The wrapper keeps track of the shared index list as well. It is possible that the parser is deleted before the + * nodes are deleted, causing an issue when accessing the index list. + */ + struct CIteratorWrapper + { + /** + * @brief Constructor + * @param[in] rIndexList Reference to the index list. + * @param[in] itPos Position of this index in the index list. + */ + CIteratorWrapper(CIndexList& rIndexList, CIndexListIterator itPos); + + /** + * @brief Destructor removing the index object from the index list. + */ + ~CIteratorWrapper(); + + /** + * @brief Get the index in the list. + * @remarks This index os calculated dynamically and can change if indices are added, removed or swapped. + * @return The current index in the list. + */ + uint32_t Index() const; + + /** + * @brief Move the index object before the supplied index object. + * @param[in] ritTarget Reference to the index object to move the object before. + */ + void MoveBeforeIndex(const CIteratorWrapper& ritTarget); + + private: + CIndexList& m_rIndexList; ///< Reference to the index list. + CIndexListIterator m_itPos; ///< Iterator in the list + }; + + std::shared_ptr m_ptrPos; ///< Position in the index list + }; + + /** + * @brief Node indexer object managing the node index objects allowing determining and changing the order of nodes. + */ + class CNodeIndexer + { + friend CNodeIndex; ///< CNodeIndex has friend relationship allowing access to specific indexing functions using the + ///< index iterator from the index list. + public: + + /** + * @brief Constructor + */ + CNodeIndexer(); + + /** + * @brief Create a node index object. + * @return The new node index object. + */ + CNodeIndex CreateIndex(); + + /** + * @brief Create a new node index object and insert this object before the supplied object. + * @attention There is no protection for supplying an invalid iterator or an iterator from a different indexer. + * @param[in] rInsertBefore Insert the index object before the supplied iterator position. + * @return The new node index object. + */ + CNodeIndex CreateIndex(const CNodeIndex& rInsertBefore); + + /** + * @brief Return the amount of indices allocated. + * @return The amount of indices allocated in the index list. + */ + static size_t Count(); + + private: + static CIndexList m_lstIndexList; ///< List of all node indices. This list is global to allow nodes from one + ///< parser to be inserted into the list of another parser. + }; +} // namespace toml_parser + +#endif // !defined PARSER_NODE_INDEXER_H \ No newline at end of file diff --git a/sdv_services/core/toml_parser/parser_node_toml.cpp b/sdv_services/core/toml_parser/parser_node_toml.cpp index f8be6c7..0637832 100644 --- a/sdv_services/core/toml_parser/parser_node_toml.cpp +++ b/sdv_services/core/toml_parser/parser_node_toml.cpp @@ -18,6 +18,7 @@ #include #include "parser_toml.h" #include +#include /// The TOML parser namespace namespace toml_parser @@ -41,16 +42,13 @@ namespace toml_parser } } - bool CGenContext::PartOfExcludedParents(const std::shared_ptr& rptrNode) const + bool CGenContext::IsPartOfView(const std::shared_ptr& rptrNode) const { + if (!rptrNode) return false; if (!m_ptrTopMostNode) return false; - std::shared_ptr ptrParent = m_ptrTopMostNode->GetParentPtr(); - while (ptrParent) - { - if (ptrParent == rptrNode) return true; - ptrParent = ptrParent->GetParentPtr(); - } - return false; + if (rptrNode->Inline()) return true; + if (!rptrNode->GetParentPtr()) return false; + return m_bTopMost || rptrNode->GetParentPtr() == m_ptrTopMostNode; } CGenContext CGenContext::CopyWithContext(const std::string& rssNewKeyContext, const std::shared_ptr& rptrNode, @@ -230,7 +228,7 @@ namespace toml_parser } CNode::CNode(CParser& rparser, const std::string& rssName, const std::string& rssRawName) : - m_ssName(rssName), m_ssRawName(rssRawName), m_rParser(rparser) + m_index(rparser.Indexer().CreateIndex()), m_ssName(rssName), m_ssRawName(rssRawName), m_refParser(rparser) {} CNode::~CNode() @@ -238,7 +236,32 @@ namespace toml_parser CParser& CNode::Parser() { - return m_rParser; + return m_refParser.get(); + } + + const CParser& CNode::Parser() const + { + return m_refParser.get(); + } + + const CNodeIndex& CNode::NodeIndex() const + { + return m_index; + } + + CNodeIndex& CNode::NodeIndex() + { + return m_index; + } + + bool CNode::operator<(const CNode& rNode) const + { + return m_index < rNode.m_index; + } + + bool CNode::operator<(const std::shared_ptr& rptrNode) const + { + return m_index < rptrNode->m_index; } sdv::u8string CNode::GetName() const @@ -316,11 +339,9 @@ namespace toml_parser uint32_t CNode::GetIndex() const { - std::shared_ptr ptrView = m_ptrView.lock(); - if (!ptrView) ptrView = m_ptrParent.lock(); - if (!ptrView) return sdv::toml::npos; - - return ptrView->FindIndex(std::const_pointer_cast(shared_from_this())); + auto ptrParent = GetParentPtr(); + if (!ptrParent) return sdv::toml::npos; + return ptrParent->FindIndex(std::const_pointer_cast(shared_from_this())); } sdv::IInterfaceAccess* CNode::GetParent() const @@ -347,14 +368,19 @@ namespace toml_parser return CodeSnippet(eType).GetComment(); } - void CNode::AutomaticFormat() + void CNode::AutomaticFormat(/*in*/ bool bRemoveComments) { - // Automatically format the code snippets of the node. - for (auto& rmapSnippets : m_vecCodeSnippets) + if (bRemoveComments) + m_vecCodeSnippets.clear(); + else { - for (auto& rvtSnippet : rmapSnippets) + // Automatically format the code snippets of the node. + for (auto& rmapSnippets : m_vecCodeSnippets) { - rvtSnippet.second.RemoveFormat(); + for (auto& rvtSnippet : rmapSnippets) + { + rvtSnippet.second.RemoveFormat(); + } } } } @@ -421,9 +447,8 @@ namespace toml_parser { bool bRet = false; - // Remove the node from the view (this could also be the parent). - auto ptrView = GetViewPtr(); - if (ptrView) ptrView->RemoveFromView(shared_from_this()); + // Block rebuilding + auto lock = Parser().CreateRebuildLockObject(); // Remove the node from the parent. auto ptrParent = GetParentPtr(); @@ -458,8 +483,14 @@ namespace toml_parser ptrOldParent->m_lstNodes.erase(itNode); } + // Assign the parent m_ptrParent = rptrParent; if (!rptrParent) return; + + // Assign the parser of the parent + ReassignParser(rptrParent->Parser()); + + // Add the node to the node list if not already present. auto itNode = std::find(rptrParent->m_lstNodes.begin(), rptrParent->m_lstNodes.end(), shared_from_this()); if (itNode == rptrParent->m_lstNodes.end()) rptrParent->m_lstNodes.push_back(shared_from_this()); @@ -477,26 +508,6 @@ namespace toml_parser return ptrParent->GetPath(false); } - void CNode::SetViewPtr(const std::shared_ptr& rptrView) - { - m_ptrView = rptrView; - } - - std::shared_ptr CNode::GetViewPtr() const - { - auto ptrView = m_ptrView.lock(); - if (!ptrView) ptrView = m_ptrParent.lock(); - return ptrView; - } - - bool CNode::IsPartOfView(const CGenContext& rContext, const std::shared_ptr& rptrNode) const - { - std::shared_ptr ptrStoredView = m_ptrView.lock(); - if (ptrStoredView && !rContext.PartOfExcludedParents(ptrStoredView)) - return ptrStoredView == rptrNode; - return true; - } - const CCodeSnippet& CNode::CodeSnippet(size_t nIndex, const std::string& rssKey /*= std::string()*/) const { static CCodeSnippet sEmptyCodeSnippet; @@ -635,6 +646,11 @@ namespace toml_parser return ssCustomKeyPath; } + void CNode::ReassignParser(CParser& rParser) + { + m_refParser = rParser; + } + bool CNode::ExplicitlyDefined() const { // Default implementation is explicitly. @@ -657,7 +673,7 @@ namespace toml_parser return true; } - bool CValueNode::Inline(bool bInline) + bool CValueNode::Inline(bool bInline, bool /*bIncludeChildren*/ /*= true*/) { return bInline; // When set to inline, okay; otherwise not. } @@ -919,17 +935,17 @@ namespace toml_parser CNode(rparser, rssName, rssRawName) {} - void CNodeCollection::AutomaticFormat() + void CNodeCollection::AutomaticFormat(/*in*/ bool bRemoveComments) { // Format each child-node for (std::shared_ptr& rptrNode : m_lstNodes) - rptrNode->AutomaticFormat(); - CNode::AutomaticFormat(); + rptrNode->AutomaticFormat(bRemoveComments); + CNode::AutomaticFormat(bRemoveComments); } uint32_t CNodeCollection::GetCount() const { - return static_cast(m_vecNodeOrder.size()); + return static_cast(m_lstNodes.size()); } sdv::IInterfaceAccess* CNodeCollection::GetNode(/*in*/ uint32_t uiIndex) const @@ -940,10 +956,63 @@ namespace toml_parser std::shared_ptr CNodeCollection::Get(uint32_t uiIndex) const { - if (static_cast(uiIndex) >= m_vecNodeOrder.size()) + if (static_cast(uiIndex) >= m_lstNodes.size()) return nullptr; - return m_vecNodeOrder[uiIndex]; + auto it = m_lstNodes.begin(); + std::advance(it, uiIndex); + return *it; + } + + void CNodeCollection::RebuildNodeOrder(bool bForce) + { + // Is rebuild locked? Then do not rebuild. + if (!bForce && Parser().RebuildLocked()) return; + + // Sort the nodes + m_lstNodes.sort([&](const std::shared_ptr& rptrNode1, const std::shared_ptr& rptrNode2) -> bool + { + if (!rptrNode2) return true; + if (!rptrNode1) return false; + + // Check whether only one is inline. If so, node 1 is smaller when node 1 is inline. + if (rptrNode1->Inline() != rptrNode2->Inline()) + return rptrNode1->Inline(); + + // Check for the smallest index + return *rptrNode1 < *rptrNode2; + }); + + // Call rebuild for all sub-node-collections + for (auto& rptrNode : m_lstNodes) + { + std::shared_ptr ptrCollection = rptrNode->Cast(); + if (ptrCollection) + ptrCollection->RebuildNodeOrder(bForce); + } + } + + void CNodeCollection::FillNodeOrderVector(std::vector>& rvecNodes, bool bRootView /*= true*/, + bool bTopLevel /*= true*/) + { + // Run through the nodes and add the nodes that are explicit. + for (const auto& rptrNode : m_lstNodes) + { + // If top level is enabled, count all explicit nodes. If root view is enabled, count all explicit standard tables. + bool bIsTable = rptrNode->Cast() ? true : false; + bool bIsTableArray = rptrNode->Cast() && rptrNode->Cast()->TableArray(); + bool bExplicit = rptrNode->ExplicitlyDefined(); + bool bIsInline = rptrNode->Inline(); + if (bExplicit && (bTopLevel || (bRootView && bIsTable && !bIsInline)) && !(bIsTableArray && !bIsInline)) + rvecNodes.push_back(rptrNode); + + // Add children if the node is not explicitly defined (then all children if the top level flag is set) or root view + // is set and the node is not inline, all the tables within the collection. + auto ptrCollection = rptrNode->Cast(); + std::shared_ptr ptrTargetNode; + if (ptrCollection && ((bRootView && !ptrCollection->Inline()) || !rptrNode->ExplicitlyDefined())) + ptrCollection->FillNodeOrderVector(rvecNodes, bRootView, bTopLevel && !rptrNode->ExplicitlyDefined()); + } } std::shared_ptr CNodeCollection::Direct(const std::string& rssPath) const @@ -974,14 +1043,92 @@ namespace toml_parser return static_cast(ptrNode.get()); } - sdv::IInterfaceAccess* CNodeCollection::InsertValue(uint32_t uiIndex, const sdv::u8string& ssName, sdv::any_t anyValue) + std::pair, std::string> CNodeCollection::SmartParentCreate(const std::string& rssPath, + bool bInsertTableArray /*= false*/) { + auto prKey = SplitNodeKey(rssPath); + if (prKey.first.empty()) return std::make_pair(Cast(), rssPath); + if (prKey.second.empty()) + { + // Check to see if the key exists already. If so, this could be an array. If not, return. + // Special treatment for table arrays needed. + auto ptrNode = Direct(prKey.first); + if (ptrNode && ptrNode->GetType() == sdv::toml::ENodeType::node_array && + (!bInsertTableArray || !ptrNode->Cast()->TableArray())) + return std::make_pair(ptrNode->Cast(), ""); + else + return std::make_pair(Cast(), rssPath); + } + + // Determine whether the node should be a table or an array. For this take the next name part and check for an index. + auto prKeyNext = SplitNodeKey(prKey.second); + sdv::toml::ENodeType eTargetType = + (!prKeyNext.first.empty() && prKeyNext.first.find_first_not_of("0123456789") != std::string::npos) ? + sdv::toml::ENodeType::node_table : sdv::toml::ENodeType::node_array; + + // Find the node + std::shared_ptr ptrNodeCollection; + for (auto& rptrNode : m_lstNodes) + { + if (!rptrNode) continue; + if (rptrNode->GetName() == prKey.first) + { + ptrNodeCollection = rptrNode->Cast(); + if (rptrNode->GetType() != eTargetType) + { + // Special case... the type is an array, but no index was supplied. Use the index of the last element or if none + // exists, the largest index number to indicate a new element. + if (rptrNode->GetType() == sdv::toml::ENodeType::node_array) + return SmartParentCreate( + prKey.first + "[" + std::to_string(ptrNodeCollection->GetCount() - 1) + "]." + prKey.second, + bInsertTableArray); + + // Incorrect type + return std::make_pair(nullptr, rssPath); + } + break; + } + } + + // If not found, create the node collection + if (!ptrNodeCollection) + { + // Determine whether the next key part is a number - then create an array or whether the next key part is a name, then + // create a table. + if (eTargetType == sdv::toml::ENodeType::node_array) + { + auto* pArray = dynamic_cast(InsertArray("", prKey.first)); + if (pArray) ptrNodeCollection = pArray->Cast(); + } + else + { + auto* pTable = dynamic_cast(InsertTable("", prKey.first, + sdv::toml::EInsertPreference::prefer_standard)); + if (pTable) ptrNodeCollection = pTable->Cast(); + } + if (!ptrNodeCollection) return std::make_pair(nullptr, rssPath); // Not found + } + + // Get or create next node + return ptrNodeCollection->SmartParentCreate(prKey.second, bInsertTableArray); + } + + sdv::IInterfaceAccess* CNodeCollection::InsertValue(/*in*/ const sdv::u8string& ssInsertBefore, + /*in*/ const sdv::u8string& ssName, /*in*/ sdv::any_t anyValue) + { + // Get the parent to inser this node into + auto prParent = SmartParentCreate(ssName); + if (!prParent.first) return nullptr; + + // Determine the node to insert before and make sure that the node is not occurring before the parent. + std::shared_ptr ptrInsertBefore = Direct(ssInsertBefore); + if (ptrInsertBefore && (ptrInsertBefore == prParent.first || ptrInsertBefore < prParent.first)) + ptrInsertBefore = prParent.first->Get(0); + + // Create TOML text std::stringstream sstreamTOML; - std::string ssAccessName = ssName; - if (GetType() == sdv::toml::ENodeType::node_array) - ssAccessName = "[" + std::to_string(std::min(uiIndex, GetCount())) + "]"; - else - sstreamTOML << QuoteText(ssName, EQuoteRequest::smart_key) << " = "; + if (prParent.first->GetType() != sdv::toml::ENodeType::node_array) + sstreamTOML << QuoteText(prParent.second, EQuoteRequest::smart_key) << " = "; switch (anyValue.eValType) { case sdv::any_t::EValType::val_type_bool: @@ -1023,114 +1170,139 @@ namespace toml_parser } // Insert the node - if (InsertTOML(uiIndex, sstreamTOML.str(), true) != EInsertResult::insert_success) + auto prResult = prParent.first->InsertTOML(ptrInsertBefore, sstreamTOML.str(), true); + if (prResult.first != EInsertResult::insert_success || prResult.second.empty()) return nullptr; - + // Return the inserted node. - return GetNodeDirect(ssAccessName); + return prResult.second.back().get(); } - sdv::IInterfaceAccess* CNodeCollection::InsertArray(uint32_t uiIndex, const sdv::u8string& ssName) + sdv::IInterfaceAccess* CNodeCollection::InsertArray(/*in*/ const sdv::u8string& ssInsertBefore, + /*in*/ const sdv::u8string& ssName) { + // Get the parent to inser this node into + auto prParent = SmartParentCreate(ssName); + if (!prParent.first) return nullptr; + + // Determine the node to insert before and make sure that the node is not occurring before the parent. + std::shared_ptr ptrInsertBefore = Direct(ssInsertBefore); + if (ptrInsertBefore && (ptrInsertBefore == prParent.first || ptrInsertBefore < prParent.first)) + ptrInsertBefore = prParent.first->Get(0); + // Insert the array node - std::string ssAccessName = ssName; std::string ssTOML; - if (GetType() == sdv::toml::ENodeType::node_array) - { + if (prParent.first->GetType() == sdv::toml::ENodeType::node_array) ssTOML = "[]"; - ssAccessName = "[" + std::to_string(std::min(uiIndex, GetCount())) + "]"; - } else - ssTOML = QuoteText(ssName, EQuoteRequest::smart_key) + " = []"; - if (InsertTOML(uiIndex, ssTOML, true) != EInsertResult::insert_success) + ssTOML = QuoteText(prParent.second, EQuoteRequest::smart_key) + " = []"; + auto prResult = prParent.first->InsertTOML(ptrInsertBefore, ssTOML, true); + if (prResult.first != EInsertResult::insert_success || prResult.second.empty()) return nullptr; // Return the inserted node. - return GetNodeDirect(ssAccessName); + return prResult.second.back().get(); } - sdv::IInterfaceAccess* CNodeCollection::InsertTable(uint32_t uiIndex, const sdv::u8string& ssName, - sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) + sdv::IInterfaceAccess* CNodeCollection::InsertTable(/*in*/ const sdv::u8string& ssInsertBefore, + /*in*/ const sdv::u8string& ssName, /*in*/ sdv::toml::EInsertPreference ePreference) { + // Get the parent to inser this node into + auto ptrArray = Cast(); + auto prParent = SmartParentCreate(ssName, ptrArray && ptrArray->TableArray()); + if (!prParent.first) return nullptr; + + // Determine the node to insert before and make sure that the node is not occurring before the parent. + std::shared_ptr ptrInsertBefore = Direct(ssInsertBefore); + if (ptrInsertBefore && (ptrInsertBefore == prParent.first || ptrInsertBefore < prParent.first)) + ptrInsertBefore = prParent.first->Get(0); + // Insert inline or standard table - std::string ssAccessName = ssName; std::string ssTOML; - if (Inline() || ePreference == sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline) + if (prParent.first->Inline() || ePreference == sdv::toml::EInsertPreference::prefer_inline) { - if (GetType() == sdv::toml::ENodeType::node_array) - { + if (prParent.first->GetType() == sdv::toml::ENodeType::node_array) ssTOML = "{}"; - ssAccessName = "[" + std::to_string(std::min(uiIndex, GetCount())) + "]"; - } else - ssTOML = QuoteText(ssName, EQuoteRequest::smart_key) + " = {}"; + ssTOML = QuoteText(prParent.second, EQuoteRequest::smart_key) + " = {}"; } - else if (GetType() == sdv::toml::ENodeType::node_array) // Node is a table array + else if (prParent.first->GetType() == sdv::toml::ENodeType::node_array) // Node is a table array { ssTOML = "[[" + QuoteText(GetName(), EQuoteRequest::smart_key) + "]]"; - ssAccessName = "[" + std::to_string(std::min(uiIndex, GetCount())) + "]"; } else - ssTOML = "[" + QuoteText(ssName, EQuoteRequest::smart_key) + "]"; + ssTOML = "[" + QuoteText(prParent.second, EQuoteRequest::smart_key) + "]"; // Insert the table node - if (InsertTOML(uiIndex, ssTOML, true) != EInsertResult::insert_success) + auto prResult = prParent.first->InsertTOML(ptrInsertBefore, ssTOML, true); + if (prResult.first != EInsertResult::insert_success || prResult.second.empty()) return nullptr; // Return the inserted node. - return GetNodeDirect(ssAccessName); + return prResult.second.back().get(); } - sdv::IInterfaceAccess* CNodeCollection::InsertTableArray(uint32_t uiIndex, const sdv::u8string& ssName, - sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) + sdv::IInterfaceAccess* CNodeCollection::InsertTableArray(/*in*/ const sdv::u8string& ssInsertBefore, + /*in*/ const sdv::u8string& ssName, /*in*/ sdv::toml::EInsertPreference ePreference) { - // Insert inline or standard table array - std::string ssAccessName = ssName; - std::string ssTOML; + // Get the parent to insert this node into + auto prParent = SmartParentCreate(ssName, true); + if (!prParent.first) return nullptr; - if (Inline() || ePreference == sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline) + // Determine the node to insert before and make sure that the node is not occurring before the parent. + std::shared_ptr ptrInsertBefore = Direct(ssInsertBefore); + if (ptrInsertBefore && (ptrInsertBefore == prParent.first || ptrInsertBefore < prParent.first)) + ptrInsertBefore = prParent.first->Get(0); + + // Insert inline or standard table array + std::string ssTOML; + if (prParent.first->Inline() || ePreference == sdv::toml::EInsertPreference::prefer_inline) { - if (GetType() == sdv::toml::ENodeType::node_array) - { + if (prParent.first->GetType() == sdv::toml::ENodeType::node_array) ssTOML = "[{}]"; - ssAccessName = "[" + std::to_string(std::min(uiIndex, GetCount())) + "][0]"; - } else { // If there is a table array with the same name already, the node will be added to the end. - auto ptrExistingNode = Direct(ssName); + auto ptrExistingNode = Direct(prParent.second); auto ptrExistingArray = ptrExistingNode ? ptrExistingNode->Cast() : std::shared_ptr(); - if (ptrExistingArray && !ptrExistingArray->TableArray()) - return nullptr; - size_t nCount = ptrExistingArray ? ptrExistingArray->GetCount() : 0; - ssTOML = QuoteText(ssName, EQuoteRequest::smart_key) + " = [{}]"; - ssAccessName += "[" + std::to_string(nCount) + "]"; + if (ptrExistingArray && !ptrExistingArray->TableArray()) return nullptr; + ssTOML = QuoteText(prParent.second, EQuoteRequest::smart_key) + " = [{}]"; } } else { // If there are table arrays, the node is added to the table array, but the order is determined by this node. Count the // amount of nodes before the insertion point. - ssTOML = "[[" + QuoteText(ssName, EQuoteRequest::smart_key) + "]]"; - size_t nTableArrayCnt = std::count_if(m_vecNodeOrder.begin(), - m_vecNodeOrder.begin() + std::min(static_cast(uiIndex), m_vecNodeOrder.size()), - [&](const std::shared_ptr& rptrNode) { return rptrNode->GetName() == ssName; }); - ssAccessName += "[" + std::to_string(nTableArrayCnt) + "]"; + ssTOML = "[[" + QuoteText(prParent.second, EQuoteRequest::smart_key) + "]]"; } // Insert the table node - if (InsertTOML(uiIndex, ssTOML, true) != EInsertResult::insert_success) + auto prResult = prParent.first->InsertTOML(ptrInsertBefore, ssTOML, true); + if (prResult.first != EInsertResult::insert_success || prResult.second.empty()) return nullptr; // Return the inserted node. - return GetNodeDirect(ssAccessName); + return prResult.second.back().get(); } - sdv::toml::INodeCollectionInsert::EInsertResult CNodeCollection::InsertTOML(uint32_t uiIndex, const sdv::u8string& ssTOML, - bool bRollbackOnPartly) + sdv::toml::INodeCollectionInsert::EInsertResult CNodeCollection::InsertTOML(/*in*/ const sdv::u8string& ssInsertBefore, + /*in*/ const sdv::u8string& ssTOML, /*in*/ bool bRollbackOnPartly) { + std::shared_ptr ptrInsertBefore = Direct(ssInsertBefore); + + return InsertTOML(ptrInsertBefore, ssTOML, bRollbackOnPartly).first; + } + + std::pair>> + CNodeCollection::InsertTOML(const std::shared_ptr& rptrInsertBefore,const sdv::u8string& ssTOML, + bool bRollbackOnPartly) + { + std::vector> vecRetNodes; try { + // Lock the rebuild of the node order + auto lock = Parser().CreateRebuildLockObject(); + // Parser for the new TOML code CParser parser; @@ -1157,7 +1329,7 @@ namespace toml_parser if (ptrArray) ptrCollection = ptrArray->Cast(); if (!ptrCollection) - return sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail; + return std::make_pair(sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail, vecRetNodes); } else { @@ -1193,13 +1365,13 @@ namespace toml_parser if (ptrNode->Inline()) ptrPotentialNewNode->Cast()->MakeInline(); else - ptrPotentialNewNode->Cast()->MakeStandard(); + ptrPotentialNewNode->Cast()->MakeStandard(true); continue; } // Node exists already. Add to duplicate name nodes vector (or fail if no all nodes should fit). if (bRollbackOnPartly) - return sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail; + return std::make_pair(sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail, vecRetNodes); vecDuplicateNameNodes.push_back(ptrPotentialNewNode); } @@ -1209,115 +1381,67 @@ namespace toml_parser // Any nodes left to insert? if (!ptrCollection->GetCount()) - return sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail; + return std::make_pair(sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail, vecRetNodes); // Add the child nodes to the collection (use a copy of the nodes list, since SetParentPtr changes the node list). auto lstCopyNodes = ptrCollection->m_lstNodes; - std::vector> vecSkipNodes; for (auto ptrNewNode : lstCopyNodes) { // If the node is a table array, it is allowed that the tables are added to an existing table array. auto ptrNewArray = ptrNewNode->Cast(); auto ptrExistingNode = Direct(ptrNewNode->GetName()); auto ptrExistingArray = ptrExistingNode ? ptrExistingNode->Cast() : std::shared_ptr(); - if (ptrNewArray && ptrNewArray->TableArray() && ptrExistingArray && - (ptrExistingArray->TableArray() || !ptrExistingArray->GetCount())) + + // Shift the node to the target list + if (!ptrExistingArray) { - // Determine the current index of the array. This is used to determine to insert the nodes before or behind - // the existing nodes within the array. - uint32_t uiCurrentPos = ptrExistingNode->GetIndex(); - - // Insert each table in the already existing table array. Assign the parent pointer and the location in the - // parent table array. - for (uint32_t uiNewArrayIndex = 0; uiNewArrayIndex < ptrNewArray->GetCount(); uiNewArrayIndex++) - { - auto ptrNewTable = ptrNewArray->Get(uiNewArrayIndex); - ptrNewTable->SetParentPtr(ptrExistingArray); - ptrExistingArray->m_vecNodeOrder.insert( - uiCurrentPos > uiIndex ? (ptrExistingArray->m_vecNodeOrder.begin() + uiNewArrayIndex) : - ptrExistingArray->m_vecNodeOrder.end(), - ptrNewTable); - } - - // Do not process the table array when processing the position of all nodes in the parsed TOML code. - vecSkipNodes.push_back(ptrNewNode); - continue; - } - - // Inserting values from an existing array into an array with the same name (which is not a table array) is not - // automatically supported. For this the array content needs to specifically be added to the existing array. - if (ptrExistingArray) - return sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail; // Should not happen - - // Asign the new parent pointer. - if (ptrNewNode->Cast() && Inline()) - ptrNewNode->Cast()->MakeInline(); - ptrNewNode->SetParentPtr(Cast()); - } - - // Insert the nodes in the node vector at the required index. - size_t nTargetIndex = uiIndex; - for (uint32_t uiNewNodeIndex = 0; uiNewNodeIndex < ptrCollection->GetCount(); uiNewNodeIndex++) - { - auto ptrNewNode = ptrCollection->Get(uiNewNodeIndex); - - // Skip when the node is already inserted previously. - if (std::find(vecSkipNodes.begin(), vecSkipNodes.end(), ptrNewNode) != vecSkipNodes.end()) continue; - - // The target index should not supercede the size of the vector - if (nTargetIndex > GetCount()) - nTargetIndex = GetCount(); - - // If the node collection is - // - inline table or table array - // --> standard nodes will be converted to inline nodes. - // --> table-arrays can be added to additional table arrays - // - standard table or table array - // --> standard nodes will be inserted behind the inline nodes. - // --> inline nodes will be inserted before the standard nodes. - if (Inline()) - { - // Only inline nodes can be inserted in other inline nodes. - if (ptrNewNode->Cast()) + // Asign the new parent pointer. + if (ptrNewNode->Cast() && Inline()) ptrNewNode->Cast()->MakeInline(); + ptrNewNode->SetParentPtr(Cast()); + if (rptrInsertBefore) + ptrNewNode->NodeIndex().MoveBefore(rptrInsertBefore->NodeIndex()); + vecRetNodes.push_back(ptrNewNode); } else { - // Determine the begin of standard nodes in the node vector - size_t nBeginStandardIndex = 0; - for (; nBeginStandardIndex < m_vecNodeOrder.size(); nBeginStandardIndex++) - { - if (!m_vecNodeOrder[nBeginStandardIndex]->Inline()) - break; - } - - // Correct the target index if necessary - if (ptrNewNode->Inline()) - { - if (nTargetIndex > nBeginStandardIndex) - nTargetIndex = nBeginStandardIndex; - } - else - { - if (nTargetIndex < nBeginStandardIndex) - nTargetIndex = nBeginStandardIndex; - } + // Inserting values from an existing array into an array with the same name (which is not a table array) is not + // automatically supported. For this the array content needs to specifically be added to the existing array. + if (!ptrExistingArray->TableArray()) // Should not happen + return std::make_pair(sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail, vecRetNodes); } - // Insert the node - m_vecNodeOrder.insert(m_vecNodeOrder.begin() + nTargetIndex, ptrNewNode); - - // Increase the target index for the next target - nTargetIndex++; + // Set the parent pointer for each table in the table array. + if (ptrNewArray && ptrNewArray->TableArray()) + { + // Insert each table in the already existing table array. Assign the parent pointer and the location in the + // parent table array. + uint32_t uiNewArrayIndex = 0; + while (uiNewArrayIndex < ptrNewArray->GetCount()) + { + auto ptrNewTable = ptrNewArray->Get(uiNewArrayIndex); + // Since the new array might have received a new parent (or in any case a new parser reference), assign the + // parent once more to also assign the parser reference. + // Shift the table to the target array (this removes it from the current array). + if (ptrExistingArray) + ptrNewTable->SetParentPtr(ptrExistingArray); + else + ++uiNewArrayIndex; + if (rptrInsertBefore) + ptrNewTable->NodeIndex().MoveBefore(rptrInsertBefore->NodeIndex()); + vecRetNodes.push_back(ptrNewTable); + } + } } - + // Return the result - return vecDuplicateNameNodes.empty() ? sdv::toml::INodeCollectionInsert::EInsertResult::insert_success : - sdv::toml::INodeCollectionInsert::EInsertResult::insert_partly_success; + return std::make_pair(vecDuplicateNameNodes.empty() ? sdv::toml::INodeCollectionInsert::EInsertResult::insert_success : + sdv::toml::INodeCollectionInsert::EInsertResult::insert_partly_success, vecRetNodes); } - catch (const sdv::toml::XTOMLParseException& /*rexcept*/) + catch (const sdv::toml::XTOMLParseException& rexcept) { - return sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail; + std::cerr << rexcept.what() << std::endl; + return std::make_pair(sdv::toml::INodeCollectionInsert::EInsertResult::insert_fail, vecRetNodes); } } @@ -1336,6 +1460,26 @@ namespace toml_parser return bRet && CNode::DeleteNode(); } + bool CNodeCollection::Inline() const + { + // Will be overridden + return false; + } + + bool CNodeCollection::Inline(bool bInline, bool bIncludeChildren /*= true*/) + { + // Updating children, but only when inline or when include children flags has been set. + if (!bInline && !bIncludeChildren) return true; + + // Update the children + for (auto& rptrNode : m_lstNodes) + { + rptrNode->Inline(bInline, bIncludeChildren); + } + + return true; + } + bool CNodeCollection::CanMakeInline() const { // To make a node inline is always possible. @@ -1344,9 +1488,11 @@ namespace toml_parser bool CNodeCollection::MakeInline() { - if (Inline()) return true; + // Block rebuilding + auto lock = Parser().CreateRebuildLockObject(); + bool bRet = Inline(true); - if (bRet) AutomaticFormat(); + if (bRet) AutomaticFormat(false); return bRet; } @@ -1354,30 +1500,35 @@ namespace toml_parser { // To make a node as standard node, this is only possible when the parent is not inline. auto ptrParent = GetParentPtr(); - if (!ptrParent) return false; - if (ptrParent->Inline()) return false; + if (ptrParent && ptrParent->Inline()) + return false; + + // If this is the root node, check if one of the nodes can be made standard + if (Cast()) + { + bool bRet = false; + for (const auto& rptrNode : m_lstNodes) + bRet |= rptrNode->Cast() ? rptrNode->Cast()->CanMakeStandard() : false; + return bRet; + } + return true; } - bool CNodeCollection::MakeStandard() + bool CNodeCollection::MakeStandard(/*in*/ bool bIncludeChildren) { - if (!Inline()) return true; - bool bRet = Inline(false); - if (bRet) AutomaticFormat(); + // Block rebuilding + auto lock = Parser().CreateRebuildLockObject(); + + if (!CanMakeStandard()) + return false; + bool bRet = Inline(false, bIncludeChildren); + if (bRet) AutomaticFormat(false); return bRet; } bool CNodeCollection::DeleteNode(const std::shared_ptr& rptrNode) { - // Remove from the view (just in case). - RemoveFromView(rptrNode); - - // In case this is an array collection, it could still happen, that the node is in the vector. Explicitly remove the node - // from the vector. - auto itElementVec = std::find(m_vecNodeOrder.begin(), m_vecNodeOrder.end(), rptrNode); - if (itElementVec != m_vecNodeOrder.end()) - m_vecNodeOrder.erase(itElementVec); - // Find the element auto itElementLst = std::find(m_lstNodes.begin(), m_lstNodes.end(), rptrNode); if (itElementLst == m_lstNodes.end()) @@ -1389,171 +1540,12 @@ namespace toml_parser return true; } - bool CNodeCollection::InsertIntoView(uint32_t uiIndex, const std::shared_ptr& rptrNode) - { - // Check for vaidity - if (!rptrNode) return false; - if (Inline() && !rptrNode->Inline()) return false; // Cannot assign a standard node to an inline node - if (!IsDescendant(rptrNode)) return false; // Must descent directly from this node - - // Get the current view - auto ptrView = rptrNode->GetViewPtr(); - if (!ptrView) ptrView = rptrNode->GetParentPtr(); - if (!ptrView) return false; - - // Determine the start of the standard nodes in the vector. - uint32_t uiStartStandard = 0; - while (m_vecNodeOrder.size() > uiStartStandard && !m_vecNodeOrder[uiStartStandard]->IsStandard()) - ++uiStartStandard; - - // Determine the potential new index location. - uint32_t uiNewIndex = uiIndex; - if (rptrNode->IsStandard() && uiIndex < uiStartStandard) uiNewIndex = uiStartStandard; - else if (rptrNode->IsInline() && uiIndex > uiStartStandard) uiNewIndex = uiStartStandard; - if (static_cast(uiNewIndex) > m_vecNodeOrder.size()) uiNewIndex = static_cast(m_vecNodeOrder.size()); - - // Special case, if the current view is identical and the node will be located at the same position, do not do anything. - // This is necessary to keep the code snippets in place. Otherwise they might be relocated. - // Also, if the current position is before the new position, correct the new index (since we remove the current position). - if (ptrView == Cast()) - { - uint32_t uiCurrentIndex = rptrNode->GetIndex(); - if (uiCurrentIndex == uiNewIndex) return true; // Nothing to do... - if (uiCurrentIndex < uiNewIndex) uiNewIndex--; - if (uiCurrentIndex < uiStartStandard) uiStartStandard--; - } - - // Remove a previous view assignment (if necessary this will shift code snippets as well). - ptrView->RemoveFromView(rptrNode); - - // Check whether the node is already part of the view - if (std::find(m_vecNodeOrder.begin(), m_vecNodeOrder.end(), rptrNode) != m_vecNodeOrder.end()) - return true; // Insert only once. - - // Determine the first position and the beyond-last position - uint32_t uiFirst = rptrNode->IsStandard() ? uiStartStandard : 0; - uint32_t uiLast = rptrNode->IsStandard() ? static_cast(m_vecNodeOrder.size()) : uiStartStandard; - - // If the node is inserted at the first position, shift the out-of-scope code snippet from the first node. - if ((uiNewIndex <= uiFirst) && (uiFirst < m_vecNodeOrder.size())) - { - rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) - .Insert(m_vecNodeOrder[uiFirst]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); - m_vecNodeOrder[uiFirst]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before).Clear(); - } - - // If the node is inserted at the last position, shift the out-of-scope code snippet from the last node. - if ((uiNewIndex >= uiLast) && (uiFirst < m_vecNodeOrder.size())) - { - rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) - .Append(m_vecNodeOrder[uiLast - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); - m_vecNodeOrder[uiLast - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before).Clear(); - } - - // Insert the node - m_vecNodeOrder.insert(m_vecNodeOrder.begin() + uiNewIndex, rptrNode); - - return true; - } - - bool CNodeCollection::RemoveFromView(const std::shared_ptr& rptrNode) - { - // Check for validity - if (!rptrNode) return false; - if (rptrNode->GetViewPtr() != Cast()) return false; - - // Get the current position - uint32_t uiCurrentIndex = rptrNode->GetIndex(); - if (uiCurrentIndex >= m_vecNodeOrder.size()) return false; // Not present (likely already removed) - - // Determine the start of the standard nodes in the vector. - uint32_t uiStartStandard = 0; - while (m_vecNodeOrder.size() > uiStartStandard && !m_vecNodeOrder[uiStartStandard]->IsStandard()) - ++uiStartStandard; - - // Determine the first position and the beyond-last position - uint32_t uiFirst = rptrNode->IsStandard() ? uiStartStandard : 0; - uint32_t uiLast = rptrNode->IsStandard() ? static_cast(m_vecNodeOrder.size()) : uiStartStandard; - - // Move the out-of-scope code to another node - if ((uiLast - uiFirst) > 1) - { - // If removing the node and a node with the same classification (standard or inline) is still available, shift the - // out-of-scope code to the left-over nodes with the same classification. - if (uiCurrentIndex <= uiFirst) - { - // The first node will be removed - copy the out-of-scope code to the node behind - m_vecNodeOrder[uiCurrentIndex + 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) - .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); - m_vecNodeOrder[uiCurrentIndex + 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) - .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); - } else if (uiCurrentIndex >= (uiLast - 1)) - { - // The last node will be removed - copy the out-of-scope code to the node before - m_vecNodeOrder[uiCurrentIndex - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) - .Append(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); - m_vecNodeOrder[uiCurrentIndex - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) - .Append(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); - } else - { - // Copy the out-of-scope code from the end of the node to the node following - m_vecNodeOrder[uiCurrentIndex - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) - .Append(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); - m_vecNodeOrder[uiCurrentIndex + 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) - .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); - } - } - else if (m_vecNodeOrder.size() > 1) - { - // If there are no more nodes with the same classification, but there are nodes with another classification, then move - // the out-of-scope code to the node with the other classification. - if (uiCurrentIndex == 0) - { - // The first node will be removed - copy the out-of-scope code to the node behind. - m_vecNodeOrder[uiCurrentIndex + 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) - .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); - m_vecNodeOrder[uiCurrentIndex + 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before) - .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); - } else - { - // The last node will be removed - copy the out-of-scope code to the node before. - m_vecNodeOrder[uiCurrentIndex - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) - .Append(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); - m_vecNodeOrder[uiCurrentIndex - 1]->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) - .Append(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); - } - } - else if (IsStandard()) - { - // If there are no nodes left over, but this node is a standard node, move the out-of-scope code to this node. - CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) - .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind)); - CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind) - .Insert(rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before)); - } - - // If this node is not a standard node, then the out-of-scope code cannot be maintained. - - // Delete the out-of-scope code. - rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before).Clear(); - rptrNode->CodeSnippet(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind).Clear(); - - // Remove the actual pointer. - rptrNode->SetViewPtr(nullptr); - - // And remove from the vector (but only when the collection is not an array). - if (!Cast()) - m_vecNodeOrder.erase(m_vecNodeOrder.begin() + uiCurrentIndex); - - return true; - } - uint32_t CNodeCollection::FindIndex(const std::shared_ptr& rptrNode) const { // Find the element - for (uint32_t uiIndex = 0; uiIndex < m_vecNodeOrder.size(); uiIndex++) + for (uint32_t uiIndex = 0; uiIndex < GetCount(); uiIndex++) { - if (rptrNode == m_vecNodeOrder[uiIndex]) + if (rptrNode == Get(uiIndex)) return uiIndex; } return sdv::toml::npos; @@ -1572,6 +1564,13 @@ namespace toml_parser return false; } + void CNodeCollection::ReassignParser(CParser& rParser) + { + CNode::ReassignParser(rParser); + for (auto& rptrNode : m_lstNodes) + rptrNode->ReassignParser(rParser); + } + CTable::CTable(CParser& rparser, const std::string& rssName, const std::string& rssRawName, bool bDefaultInline, bool bExplicit /*= true*/) : CNodeCollection(rparser, rssName, rssRawName), m_bDefinedExplicitly(bExplicit), m_bInline(bDefaultInline) @@ -1582,6 +1581,37 @@ namespace toml_parser return IsDeleted() ? sdv::toml::ENodeType::node_invalid : sdv::toml::ENodeType::node_table; } + uint32_t CTable::GetCount() const + { + return static_cast(m_vecNodeOrder.size()); + } + + std::shared_ptr CTable::Get(uint32_t uiIndex) const + { + if (static_cast(uiIndex) >= m_vecNodeOrder.size()) + return nullptr; + + return m_vecNodeOrder[uiIndex]; + } + + bool CTable::DeleteNode() + { + // Get parent + auto ptrParent = GetParentPtr(); + + // Check for inline + bool bInline = Inline(); + + // Delete the node + bool bRet = CNodeCollection::DeleteNode(); + + // If the parent node is an array and doesn't have any members any more and is not inline; remove the parent as well. + if (!bInline && ptrParent->GetType() == sdv::toml::ENodeType::node_array && !ptrParent->GetCount()) + ptrParent->DeleteNode(); + + return bRet; + } + std::string CTable::GenerateTOML(const CGenContext& rContext /*= CGenContext()*/) const { if (IsDeleted()) return {}; @@ -1602,7 +1632,7 @@ namespace toml_parser // Special case, table as part of table array - but only if the parent is included in the generation. This can be identified // by the top most flag of the context. bool bTableArray = false; - if (!contextCopy.TopMostNode() || contextCopy.CheckOption(toml_parser::EGenerateOptions::full_header)) + if (!contextCopy.TopMostNode() || contextCopy.CheckOption(EGenerateOptions::full_header)) bTableArray = !contextCopy.Embedded() && ptrParent && ptrParent->Cast() && ptrParent->Cast()->TableArray(); @@ -1614,7 +1644,7 @@ namespace toml_parser { std::shared_ptr ptrNode = Get(uiIndex); if (!ptrNode) continue; - if (bTableArray || !ptrNode->Cast() || ptrNode->Inline()) + if (bTableArray || (!ptrNode->Cast() && !ptrNode->Cast()) || ptrNode->Inline()) bDoNotPrint = false; } @@ -1651,9 +1681,8 @@ namespace toml_parser std::shared_ptr ptrNode = Get(uiIndex); if (!ptrNode) continue; - // If the node has a view pointer, which is not identical to this pointer, do not print the node... it will be printed - // by a different node. - if (!ptrNode->IsPartOfView(contextCopy, Cast())) continue; + // If the node is part of the view in the context, it is printed by the view node and not here. + if (!contextCopy.IsPartOfView(ptrNode)) continue; if (contextCopy.Inline() || ptrNode->Inline()) { @@ -1801,19 +1830,37 @@ namespace toml_parser bool CTable::Inline() const { - auto ptrParent = GetParentPtr(); - if (!ptrParent) return m_bInline; return m_bInline; } - bool CTable::Inline(bool bInline) + bool CTable::Inline(bool bInline, bool bIncludeChildren /*= true*/) { - if (bInline == m_bInline) return true; + // When inline, all children are also inline. + if (m_bInline && bInline) return true; // Nothing to do + + // When not inline and children are not to be considered, shortcut when possible + if (!m_bInline && !bInline && !bIncludeChildren) return true; // Nothing to do + + // Allowed to make standard + if (!Cast() && !bInline && !CanMakeStandard()) return false; // Nothing to do + + // If a parent if inline, the child needs to be inline as well. + bool bTargetInline = bInline; auto ptrParent = GetParentPtr(); if (ptrParent && ptrParent->Inline()) - m_bInline = true; - else - m_bInline = bInline; + bTargetInline = true; + + // If making inline, make children inline first (call to base class). + // If making standard, make standard before making children inline. + if (bTargetInline) + { + if (CNodeCollection::Inline(true)) + m_bInline = true; + } else + { + m_bInline = false; + CNodeCollection::Inline(false, bIncludeChildren); + } return m_bInline == bInline; } @@ -1833,6 +1880,9 @@ namespace toml_parser if (!rptrCollection) return false; // No collection supplied. if (rptrCollection == Cast()) return true; // Collections are identical, nothing to combine. if (rptrCollection->IsDescendant(shared_from_this())) return false; // Circular reference. + + // Lock the rebuild of the node order + auto lock = Parser().CreateRebuildLockObject(); // Run through the provided collection list // If nodes don't exist in the current collection, add the nodes @@ -1850,11 +1900,12 @@ namespace toml_parser auto ptrExistNode = Direct(ptrNewNode->GetName()); // Check whether the node is really new; if it is, add the node - // Exception to the rule... when the existing node is a table array and the node to add is a table, do not do replace the node. + // Exception to the rule... when the existing node is a table array and the node to add is a table, do not do replace + // the node. if (!ptrExistNode || (ptrNewNode->GetType() == sdv::toml::ENodeType::node_table && ptrExistNode->Cast() && ptrExistNode->Cast()->TableArray())) { - bResult &= InsertTOML(sdv::toml::npos, ptrNewNode->GenerateTOML(contextGeneration), true) == + bResult &= InsertTOML("", ptrNewNode->GenerateTOML(contextGeneration), true) == sdv::toml::INodeCollectionInsert::EInsertResult::insert_success; continue; } @@ -1862,11 +1913,10 @@ namespace toml_parser // The node exists. Are the node types identical? If not, replace the existing node with the new node if (ptrNewNode->GetType() != ptrExistNode->GetType()) { - uint32_t uiExistIndex = ptrExistNode->GetIndex(); bool bLocalResult = ptrExistNode->DeleteNode(); bResult &= bLocalResult; if (bLocalResult) - bResult &= InsertTOML(uiExistIndex, ptrNewNode->GenerateTOML(contextGeneration), true) == + bResult &= InsertTOML(ptrExistNode, ptrNewNode->GenerateTOML(contextGeneration), true).first == sdv::toml::INodeCollectionInsert::EInsertResult::insert_success; continue; } @@ -1897,6 +1947,9 @@ namespace toml_parser if (rptrCollection == Cast()) return false; // Collections are identical, this would empty the collection. if (rptrCollection->IsDescendant(shared_from_this())) return false; // Circular reference. + // Lock the rebuild of the node order + auto lock = Parser().CreateRebuildLockObject(); + // Run through the provided collection list. // If nodes don't exist in the current collection, leave the nodes in the collection. // If nodes exist in the current collection, but are different, leave the nodes in the collection. @@ -1910,7 +1963,7 @@ namespace toml_parser if (!ptrReductorNode) continue; auto ptrExistNode = Direct(ptrReductorNode->GetName()); - // Check whether the node is exists; if not, leave the node in the collection. + // Check whether the node exists; if not, leave the node in the collection. if (!ptrExistNode) continue; // Deal with table arrays... @@ -1948,7 +2001,37 @@ namespace toml_parser return bResult; } - CArray::CArray(CParser& rparser, const std::string& rssName, const std::string& rssRawName, + void CTable::RebuildNodeOrder(bool bForce) + { + // Is rebuild locked? Then do not rebuild. + if (!bForce && Parser().RebuildLocked()) return; + + m_vecNodeOrder.clear(); + FillNodeOrderVector(m_vecNodeOrder); + + // Sort the nodes + std::sort(m_vecNodeOrder.begin(), + m_vecNodeOrder.end(), + [&](const std::shared_ptr& rptrNode1, const std::shared_ptr& rptrNode2) -> bool + { + if (!rptrNode2) return true; + if (!rptrNode1) return false; + + // Check whether only one is inline. If so, node 1 is smaller when node 1 is inline. + if (rptrNode1->Inline() != rptrNode2->Inline()) + return rptrNode1->Inline(); + + // Check for the smallest index + return *rptrNode1 < *rptrNode2; + }); + + // Call base class + CNodeCollection::RebuildNodeOrder(bForce); + } + + CArray::CArray(CParser& rparser, + const std::string& rssName, + const std::string& rssRawName, bool bExplicitTableArray /*= false*/) : CNodeCollection(rparser, rssName, rssRawName), m_bDefinedExplicitly(bExplicitTableArray), m_bInline(!bExplicitTableArray) {} @@ -1995,6 +2078,80 @@ namespace toml_parser return ptrNode->Direct(ssSecond); } + std::pair, std::string> CArray::SmartParentCreate(const std::string& rssPath, + bool bInsertTableArray /*= false*/) + { + auto prKey = SplitNodeKey(rssPath); + + // Special situation: if the first part of the name is empty or not a number, this is not an error, but should cause the + // node to be added to the end of the array. + if (rssPath.empty()) return std::make_pair(Cast(), rssPath); + if (prKey.first.empty() || prKey.first.find_first_not_of("0123456789") != std::string::npos) + prKey = std::make_pair(std::to_string(GetCount()), rssPath); + uint32_t uiArrayIndex = 0; + std::from_chars(prKey.first.data(), prKey.first.data() + prKey.first.size(), uiArrayIndex); + + // Determine whether the node should be a table or an array. For this take the next name part and check for an index. + auto prKeyNext = SplitNodeKey(prKey.second); + sdv::toml::ENodeType eTargetType = + (!prKeyNext.first.empty() && prKeyNext.first.find_first_not_of("0123456789") != std::string::npos) + ? sdv::toml::ENodeType::node_table + : sdv::toml::ENodeType::node_array; + + // Find the node + std::shared_ptr ptrNode = Get(uiArrayIndex); + std::shared_ptr ptrNodeCollection; + if (ptrNode) + { + ptrNodeCollection = ptrNode->Cast(); + if (ptrNode->GetType() != eTargetType) + { + // If the node is an array and there is no index, take the last node in the array. + if (ptrNode->GetType() == sdv::toml::ENodeType::node_array && ptrNodeCollection->GetCount()) + { + ptrNode = ptrNodeCollection->Get(ptrNodeCollection->GetCount() - 1); + ptrNodeCollection = ptrNode->Cast(); + } else + return std::make_pair(nullptr, rssPath); + } + } + + // If not found, create the node collection + if (!ptrNodeCollection) + { + // Determine whether the next key part is a number - then create an array or whether the next key part is a name, then + // create a table. + if (eTargetType == sdv::toml::ENodeType::node_array) + { + auto* pArray = dynamic_cast(InsertArray("[" + std::to_string(uiArrayIndex) + "]", "")); + if (pArray) + ptrNodeCollection = pArray->Cast(); + } + else + { + // If this is an array, do not insert another table. Use the last node collection instead. + if (!bInsertTableArray && GetCount()) + { + ptrNode = Get(GetCount() - 1); + if (ptrNode) + ptrNodeCollection = ptrNode->Cast(); + } + else + { + auto* pTable = dynamic_cast( + InsertTable("[" + std::to_string(uiArrayIndex) + "]", "", sdv::toml::EInsertPreference::prefer_standard)); + if (pTable) + ptrNodeCollection = pTable->Cast(); + } + } + if (!ptrNodeCollection) + return std::make_pair(nullptr, rssPath); // Not found + } + + // Get or create next node + return ptrNodeCollection->SmartParentCreate(prKey.second, bInsertTableArray); + } + std::string CArray::GenerateTOML(const CGenContext& rContext /*= CGenContext()*/) const { if (IsDeleted()) return {}; @@ -2036,9 +2193,8 @@ namespace toml_parser std::shared_ptr ptrNode = Get(uiIndex); if (!ptrNode) continue; - // If the node has a view pointer, which is not identical to the this pointer, do not print the node... it will be - // printed by a different node. - if (!ptrNode->IsPartOfView(contextCopy, Cast())) + // If the node is part of the view in the context, it is printed by the view node and not here. + if (!contextCopy.IsPartOfView(ptrNode)) continue; // Generate the TOML for the array node. Copy the context with the full key path (when inline) or the key context of @@ -2128,6 +2284,7 @@ namespace toml_parser return; // Unexpected // Check the last node of the array for a potential comma in the code snippet following the node assignment. + RebuildNodeOrder(true); if (GetCount()) { auto ptrNode = Get(GetCount() - 1); @@ -2188,6 +2345,45 @@ namespace toml_parser return true; } + bool CArray::Inline() const + { + return m_bInline; + } + + bool CArray::Inline(bool bInline, bool bIncludeChildren /*= true*/) + { + // When inline, all children are also inline. + if (m_bInline && bInline) return true; // Nothing to do + + // When not inline and children are not to be considered, shortcut when possible + if (!m_bInline && !bInline && !bIncludeChildren) return true; // Nothing to do + + // Allowed to make standard + if (!bInline && !CanMakeStandard()) return false; // Nothing to do + + // If a parent if inline, the child needs to be inline as well. + bool bTargetInline = bInline; + auto ptrParent = GetParentPtr(); + if (ptrParent && ptrParent->Inline()) + bTargetInline = true; + + // If making inline, make children inline first (call to base class). + // If making standard, make standard before making children inline. + if (bTargetInline) + { + if (CNodeCollection::Inline(true)) + m_bInline = true; + } + else + { + // Make standard + m_bInline = false; + CNodeCollection::Inline(false, bIncludeChildren); + } + + return true; + } + bool CArray::CanMakeStandard() const { // Check with the collection @@ -2198,73 +2394,6 @@ namespace toml_parser return TableArray(); } - bool CArray::Inline() const - { - return m_bInline; - } - - bool CArray::Inline(bool bInline) - { - // Does anything change? - if (bInline == m_bInline) return true; - - // When making standard, the parent should not be inline. - auto ptrParent = GetParentPtr(); - if (!ptrParent) return false; - if (ptrParent->Inline() && !bInline) return false; - - // The array is inline per default. Only in the case of a table array (an array with only tables, and at least one table) - // the array could be standard. - if (!TableArray()) return false; - - // Check the order in the vector of the view or of the parent. When becoming inline, must be located before the standard - // nodes. When becoming standard, must be located behind the inline nodes. - // Special case for a table array: when becoming inline, remove all the tables from the view and add the array to the - // correct location. When becoming standard, remove the array from the location and add all the tables into the view. - m_bInline = bInline; - if (bInline) - { - // If becoming inline, remove the view and make all children inline as well. - uint32_t uiCurrentPos = sdv::toml::npos; - for (uint32_t uiIndex = 0; uiIndex < GetCount(); uiIndex++) - { - auto ptrNode = Get(uiIndex); - if (!ptrNode) continue; - if (uiCurrentPos == sdv::toml::npos) - uiCurrentPos = ptrNode->GetIndex(); - if (ptrNode->Cast()) - ptrNode->Cast()->MakeInline(); - InsertIntoView(uiIndex, ptrNode); - } - - // Move the array into the view of the parent. - ptrParent->InsertIntoView(uiCurrentPos, shared_from_this()); - } else - { - // Get the current position - uint32_t uiCurrentPos = GetIndex(); - - // Make all the children standard as well and move the into the view of the parent. - for (uint32_t uiIndex = 0; uiIndex < GetCount(); uiIndex++) - { - auto ptrNode = Get(uiIndex); - if (!ptrNode) continue; - if (uiCurrentPos == sdv::toml::npos) - uiCurrentPos = ptrNode->GetIndex(); - if (ptrNode->Inline() && ptrNode->Cast()) - ptrNode->Cast()->MakeStandard(); - ptrParent->InsertIntoView(uiCurrentPos, ptrNode); - if (uiCurrentPos != sdv::toml::npos) - ++uiCurrentPos; - } - - // Remove the array from the view of the parent - ptrParent->RemoveFromView(shared_from_this()); - } - - return true; - } - bool CArray::LastNodeWithSucceedingComma() const { return m_bLastChildNodeWithComma; @@ -2276,6 +2405,9 @@ namespace toml_parser if (rptrCollection == Cast()) return true; // Collections are identical, nothing to combine. if (rptrCollection->IsDescendant(shared_from_this())) return false; // Circular reference. + // Lock the rebuild of the node order + auto lock = Parser().CreateRebuildLockObject(); + // Differentiate between a table array and a normal array: // - A normal array should be identical. If not, take over the complete array. // - A table array might contain tables that are identical, extend with tables that are not. @@ -2285,7 +2417,7 @@ namespace toml_parser // In all other cases, update the content if necessary. bool bResult = true; - toml_parser::CGenContext contextGeneration; + CGenContext contextGeneration; contextGeneration.SetOption(EGenerateOptions::reduce_whitespace); contextGeneration.SetOption(EGenerateOptions::full_header); bool bTableArray = TableArray() && rptrCollection->Cast()->TableArray(); @@ -2295,11 +2427,10 @@ namespace toml_parser auto ptrNewNode = rptrCollection->Get(uiIndex); if (!ptrNewNode) continue; std::shared_ptr ptrExistNode; - uint32_t uiTargetIndex = uiIndex; if (bTableArray) { // Go through the current array and compare the generate TOML string for identical values. - toml_parser::CGenContext contextComparison; + CGenContext contextComparison; contextComparison.SetOption(EGenerateOptions::no_comments); std::string ssNewNodeTOML = ptrNewNode->GenerateTOML(contextComparison); bool bIdentical = false; @@ -2311,17 +2442,14 @@ namespace toml_parser bIdentical = ssNewNodeTOML == ssExistTOML; } if (!bIdentical) - { - uiTargetIndex = sdv::toml::npos; // New node will be added at the end of the array. ptrExistNode.reset(); - } } else ptrExistNode = Get(uiIndex); // Check whether there is an existing node. If not, add the node. if (!ptrExistNode) { - bResult &= InsertTOML(uiTargetIndex, ptrNewNode->GenerateTOML(contextGeneration), true) == + bResult &= InsertTOML("", ptrNewNode->GenerateTOML(contextGeneration), true) == sdv::toml::INodeCollectionInsert::EInsertResult::insert_success; continue; } @@ -2333,8 +2461,8 @@ namespace toml_parser bool bLocalResult = ptrExistNode->DeleteNode(); bResult &= bLocalResult; if (bLocalResult) - bResult &= InsertTOML(uiExistIndex, ptrNewNode->GenerateTOML(contextGeneration), true) == - sdv::toml::INodeCollectionInsert::EInsertResult::insert_success; + bResult &= InsertTOML(Get(uiExistIndex), ptrNewNode->GenerateTOML(contextGeneration), true).first + == sdv::toml::INodeCollectionInsert::EInsertResult::insert_success; continue; } @@ -2375,13 +2503,16 @@ namespace toml_parser if (rptrCollection == Cast()) return false; // Collections are identical, this would empty the collection. if (rptrCollection->IsDescendant(shared_from_this())) return false; // Circular reference. + // Lock the rebuild of the node order + auto lock = Parser().CreateRebuildLockObject(); + // DIfferentiate between a table array and a normal array: // - A normal array should be identical. If so, remove the complete array. // - A table array might contain tables that are identical, those are removed. All others stay. // For normal array, simply compare the generate TOML strings. If identical, remove the array content. bool bResult = true; - toml_parser::CGenContext context; + CGenContext context; context.SetOption(EGenerateOptions::no_comments); if (!TableArray() || !rptrCollection->Cast()->TableArray()) { diff --git a/sdv_services/core/toml_parser/parser_node_toml.h b/sdv_services/core/toml_parser/parser_node_toml.h index 1d99701..943e154 100644 --- a/sdv_services/core/toml_parser/parser_node_toml.h +++ b/sdv_services/core/toml_parser/parser_node_toml.h @@ -26,6 +26,7 @@ #include #include "miscellaneous.h" #include "code_snippet.h" +#include "parser_node_indexer.h" /// The TOML parser namespace namespace toml_parser @@ -72,11 +73,12 @@ namespace toml_parser void InitTopMostNode(const std::shared_ptr& rptrNode); /** - * @brief Check whether the provided node is a parent of the top most node. - * @param[in] rptrNode Reference to the node to use for the checking. - * @return Returns true if the node is a parent of the top most node, false otherwise. + * @brief A node is part of the view if either it is an inline node, or it is a standard node and the current parent is the + * root view. + * @param[in] rptrNode Reference to the smart pointer to the node to check for. + * @return Returns whether the node is part of the view. */ - bool PartOfExcludedParents(const std::shared_ptr& rptrNode) const; + bool IsPartOfView(const std::shared_ptr& rptrNode) const; /** * @brief Create a copy of the context class with a new key context. @@ -289,10 +291,42 @@ namespace toml_parser END_SDV_INTERFACE_MAP() /** + * @{ * @brief Get a reference to the TOML parser that generated this node. * @return Reference to the TOML parse. */ CParser& Parser(); + const CParser& Parser() const; + /** + * @} + */ + + /** + * @{ + * @brief Return the index object of the node. + * @return Reference to the node index object. + */ + const CNodeIndex& NodeIndex() const; + CNodeIndex& NodeIndex(); + /** + * @} + */ + + /** + * @brief Compare the index position of this node with the position of another node and return whether this node occurs + * before the other node. + * @param[in] rNode Reference to the node to compare the position with. + * @return Returns whether this node occurs before the other node. + */ + bool operator<(const CNode& rNode) const; + + /** + * @brief Compare the index position of this node with the position of another node and return whether this node occurs + * before the other node. + * @param[in] rptrNode Reference to the node to compare the position with. + * @return Returns whether this node occurs before the other node. + */ + bool operator<(const std::shared_ptr& rptrNode) const; /** * @brief Get the node name (no conversion to a literal or quoted key is made). Overload of sdv::toml::INodeInfo::GetName. @@ -328,9 +362,8 @@ namespace toml_parser virtual sdv::any_t GetValue() const override; /** - * @brief Get the index of this node within the view collection (either the assigned view or the parent). Overload of - * sdv::toml::INodeInfo::GetIndex. - * @return The index of the node within the view collection node or npos when no parent is available. + * @brief Get the index of this node within the parent collection. Overload of sdv::toml::INodeInfo::GetIndex. + * @return The index of the node within the parent collection node or npos when no parent is available. */ virtual uint32_t GetIndex() const override; @@ -370,10 +403,10 @@ namespace toml_parser virtual sdv::u8string GetComment(sdv::toml::INodeInfo::ECommentType eType) override; /** - * @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments - * will not be changed. Overload of sdv::toml::INodeInfo::AutomaticFormat. + * @brief Format the node automatically, remove redundant whitespace. Overload of sdv::toml::INodeInfo::AutomaticFormat. + * @param[in] bRemoveComments When set, the comments are removed from the node. */ - virtual void AutomaticFormat() override; + virtual void AutomaticFormat(/*in*/ bool bRemoveComments) override; /** * @brief Is the node inline? Overload of sdv::toml::INodeInfo::IsInline. @@ -457,10 +490,11 @@ namespace toml_parser std::shared_ptr Cast() const; /** - * @brief Set the parent node. + * @brief Reassign the parent node and if necessary the parser reference. + * @details This function allows shifting nodes from one parser to another. * @param[in] rptrParent Reference to the node to assign to this node as a parent. */ - void SetParentPtr(const std::shared_ptr& rptrParent); + virtual void SetParentPtr(const std::shared_ptr& rptrParent); /** * @brief Gets the parent node pointer. @@ -475,29 +509,6 @@ namespace toml_parser */ std::string GetParentPath() const; - /** - * @brief Set the view definition node. The view definition node is a parent or grand parent that presents the node when - * generating TOML code. When not set, the parent node is taking over this role. - * @param[in] rptrView Reference to the node to assign to this node as a parent or grand parent. - */ - void SetViewPtr(const std::shared_ptr& rptrView); - - /** - * @brief Gets the view definition node pointer. - * @return Returns the store view definition node pointer or an empty pointer when no view was assigned. - */ - std::shared_ptr GetViewPtr() const; - - /** - * @brief Checks whether the node is part of the view. - * @details The node is part of the view if the supplied pointer is identical to the view definition pointer, when the view - * definition pointer is not part of a parent of the topmost node. In all other cases, the node is part of the view. - * @param[in] rContext Reference to the context class to use during TOML code generation. - * @param[in] rptrNode Reference to the node to check whether it registered for a view. - * @return Returns whether this node is part of the view with the supplied pointer. - */ - bool IsPartOfView(const CGenContext& rContext, const std::shared_ptr& rptrNode) const; - /** * @brief Accesses a node by its key in the parse tree. * @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name. @@ -507,7 +518,7 @@ namespace toml_parser * @attention Array indexing starts with 0! * @attention For an array, when no indexing is supplied, the latest entry will be returned. * @param[in] rssPath The path of the node to searched for. - * @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found. + * @return Returns a shared pointer to the wanted node if it was found or a node with invalid content if it was not found. */ virtual std::shared_ptr Direct(const std::string& rssPath) const = 0; @@ -577,13 +588,20 @@ namespace toml_parser */ std::string GetCustomPath(const std::string& rssPrefixKey, const std::string& rssContext) const; + /** + * @brief When the parent changes (e.g. when moving items from one parser to the other), the items and all its sub-items + * need to reasign the parser. + * @param[in] rParser Reference to the parser to assign. + */ + virtual void ReassignParser(CParser& rParser); + private: - std::weak_ptr m_ptrParent; ///< Weak pointer to the parent node (if existing). - std::weak_ptr m_ptrView; ///< Weak pointer to the view node (if existing and explicitly set). - std::string m_ssName; ///< Name of the node. - std::string m_ssRawName; ///< Raw name of the node. - bool m_bDeleted = false; ///< Enabled when the node was marked for deletion. - CParser& m_rParser; ///< Reference to the TOML parser. + CNodeIndex m_index; ///< Node index object holding the overall node position. + std::weak_ptr m_ptrParent; ///< Weak pointer to the parent node (if existing). + std::string m_ssName; ///< Name of the node. + std::string m_ssRawName; ///< Raw name of the node. + bool m_bDeleted = false; ///< Enabled when the node was marked for deletion. + std::reference_wrapper m_refParser; ///< Reference to the TOML parser. std::vector> m_vecCodeSnippets; ///< Vector with comments/code snippets. public: @@ -596,11 +614,16 @@ namespace toml_parser /** * @brief With some node collections it is possible to switch between inline and normal. * @remarks Additional node composition information will be removed and the order within the parent node might be changed. + * @remarks When made inline, all child nodes must be made inline as well. When made standard, only this node is made + * standard. + * @remarks Making this node a standard node, this is only possible when the parent is not an inline mode. * @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal. + * @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables + * and table-arrays can be defined as standard). Making a node inline is always including the children. * @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is * always successful). When returning false, the switching might not be supported for this type. */ - virtual bool Inline(bool bInline) = 0; + virtual bool Inline(bool bInline, bool bIncludeChildren = true) = 0; /** * @brief Checks whether the table was explicitly defined. @@ -654,10 +677,12 @@ namespace toml_parser * @brief With some node collections it is possible to switch between inline and normal. Overload of CNode::Inline. * @remarks Additional node composition information will be removed and the order within the parent node might be changed. * @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal. + * @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables + * and table-arrays can be defined as standard). Making a node inline is always including the children. * @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is * always successful). When returning false, the switching might not be supported for this type. */ - virtual bool Inline(bool bInline) override; + virtual bool Inline(bool bInline, bool bIncludeChildren = true) override; /** * @brief Accesses a node by its key in the parse tree. Overload of CNode::Direct. @@ -668,7 +693,7 @@ namespace toml_parser * @attention Array indexing starts with 0! * @attention For an array, when no indexing is supplied, the latest entry will be returned. * @param[in] rssPath The path of the node to searched for. - * @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found. + * @return Returns a shared pointer to the wanted node if it was found or a node with invalid content if it was not found. */ virtual std::shared_ptr Direct(const std::string& rssPath) const override; @@ -943,10 +968,10 @@ namespace toml_parser END_SDV_INTERFACE_MAP() /** - * @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments - * will not be changed. Overload of sdv::toml::INodeInfo::AutomaticFormat. + * @brief Format the node automatically, remove redundant whitespace. Overload of sdv::toml::INodeInfo::AutomaticFormat. + * @param[in] bRemoveComments When set, the comments are removed from the node. */ - virtual void AutomaticFormat() override; + virtual void AutomaticFormat(/*in*/ bool bRemoveComments) override; /** * @brief Returns the amount of nodes. Overload of sdv::toml::INodeCollection::GetCount. @@ -966,7 +991,28 @@ namespace toml_parser * @param[in] uiIndex Index of the node to get. * @return Smart pointer to the node object. */ - std::shared_ptr Get(uint32_t uiIndex) const; + virtual std::shared_ptr Get(uint32_t uiIndex) const; + + /** + * @brief After every insert, deletion and shift, the node order of this and all sub- tables need to be rebuild. + * @param[in] bForce Force rebuild, even if locked. + */ + virtual void RebuildNodeOrder(bool bForce); + + /** + * @brief After every insert, deletion and shift, the node order needs to be rebuild. + * @details Adding is done iteratively through the node list. In case the top level flag is set, all nodes are added that + * are marked explicit or if a node is implicit, the explicit sub-nodes are added. In case this is root view, all sub-nodes + * which are standard tables or standard table arrays are added. This flattens the hierarchy. When the root view is not set, + * only the tables and table arrays are added that are a direct child of the node collection. At the end of building the + * root view, the nodes are sorted using their index. Furthermore, inline nodes are moved to the beginning of the vector and + * standard nodes to the end. + * @param[in, out] rvecNodes Reference to the vector being filled with the nodes for the view. + * @param[in] bRootView Set when the count is top level. + * @param[in] bTopLevel Set when this is the top level node for adding sub nodes. + */ + virtual void FillNodeOrderVector(std::vector>& rvecNodes, bool bRootView = true, + bool bTopLevel = true); /** * @brief Accesses a node by its key in the parse tree. Overload of CNode::Direct. @@ -977,7 +1023,7 @@ namespace toml_parser * @attention Array indexing starts with 0! * @attention For an array, when no indexing is supplied, the latest entry will be returned. * @param[in] rssPath The path of the node to searched for. - * @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found. + * @return Returns a shared pointer to the wanted node if it was found or a node with invalid content if it was not found. */ virtual std::shared_ptr Direct(const std::string& rssPath) const override; @@ -993,85 +1039,124 @@ namespace toml_parser */ virtual sdv::IInterfaceAccess* GetNodeDirect(/*in*/ const sdv::u8string& ssPath) const override; + /** + * @brief Get or create the parent nodes automotatically from the path. + * @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name. + * E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be accessed and + * traversed by using the index number in brackets. E.g. 'array[3]' would access the fourth element of the array 'array'. + * These access conventions can also be chained like 'table.array[2][1].subtable.integerElement'. + * @attention Array indexing starts with 0! + * @attention For an array, when no indexing or a too large index number is supplied, a new entry will be created and + * returned. + * @param[in] rssPath The path of the node to searched for. + * @param[in] bInsertTableArray Since a table array consists of an array and a table, this is different than the other + * insertions that only insert one element. Some special treatment is needed at certain points. + * @return Returns a pair with the shared pointer to the parent node and the leftover name. If the creation could not be + * done, a NULL pointer is returned. + */ + virtual std::pair, std::string> SmartParentCreate(const std::string& rssPath, + bool bInsertTableArray = false); + /** * @brief Insert a value into the collection at the location before the supplied index. Overload of * sdv::toml::INodeCollectionInsert::InsertValue. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Value nodes cannot be inserted behind external - * tables and table arrays. If the index is referencing a position behind an external table or a table array, the index - * is automatically corrected. - * @param[in] ssName Name of the node to insert. Will be ignored for an array collection. The name must adhere to the - * key names defined by the TOML specification. Defining the key multiple times is not allowed. Quotation of key names - * is done automatically; the parser decides itself whether the key is bare-key, a literal key or a quoted key. - * @param[in] anyValue The value of the node, being either an integer, floating point number, virtual bool value or a string. + * @remarks In TOML, inline nodes are located before standard nodes. Since values are presented as inline node, they + * will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. + * @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if + * not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty + * string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML + * specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the + * parser decides itself whether the key is bare-key, a literal key or a quoted key. + * @param[in] anyValue The value of the node, being either an integer, floating point number, virtual bool value or a + * string. * Conversion is automatically done to int64, double float, bool or u8string. * @return On success the interface to the newly inserted node is returned or NULL otherwise. */ - virtual sdv::IInterfaceAccess* InsertValue(uint32_t uiIndex, const sdv::u8string& ssName, sdv::any_t anyValue) override; + virtual sdv::IInterfaceAccess* InsertValue(/*in*/ const sdv::u8string& ssInsertBefore, /*in*/ const sdv::u8string& ssName, + /*in*/ sdv::any_t anyValue) override; /** * @brief Insert an array into the collection at the location before the supplied index. Overload of * sdv::toml::INodeCollectionInsert::InsertArray. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Array nodes cannot be inserted behind external - * tables and table arrays. If the index is referencing a position behind an external table or a table array, the index - * is automatically corrected. - * @param[in] ssName Name of the array node to insert. Will be ignored if the current node is also an array collection. - * The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not - * allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a - * literal key or a quoted key. + * @remarks In TOML, inline nodes are located before standard nodes. Since arrays are presented as inline node, they + * will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. + * @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if + * not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty + * string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML + * specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the + * parser decides itself whether the key is bare-key, a literal key or a quoted key. * @return On success the interface to the newly inserted node is returned or NULL otherwise. */ - virtual sdv::IInterfaceAccess* InsertArray(uint32_t uiIndex, const sdv::u8string& ssName) override; + virtual sdv::IInterfaceAccess* InsertArray(/*in*/ const sdv::u8string& ssInsertBefore, + /*in*/ const sdv::u8string& ssName) override; /** * @brief Insert a table into the collection at the location before the supplied index. Overload of * sdv::toml::INodeCollectionInsert::InsertTable. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Table nodes cannot be inserted before value nodes - * or arrays. If the index is referencing a position before a value node or an array, the index is automatically - * corrected. - * @param[in] ssName Name of the table node to insert. Will be ignored if the current node is an array collection. - * The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not - * allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a - * literal key or a quoted key. + * @remarks In TOML, inline nodes are located before standard nodes. Tables can be inserted as inline node, in which + * case they will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. + * @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if + * not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty + * string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML + * specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the + * parser decides itself whether the key is bare-key, a literal key or a quoted key. * @param[in] ePreference The preferred form of the node to be inserted. * @return On success the interface to the newly inserted node is returned or NULL otherwise. */ - virtual sdv::IInterfaceAccess* InsertTable(uint32_t uiIndex, const sdv::u8string& ssName, - sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) override; + virtual sdv::IInterfaceAccess* InsertTable(/*in*/ const sdv::u8string& ssInsertBefore, /*in*/ const sdv::u8string& ssName, + /*in*/ sdv::toml::EInsertPreference ePreference) override; /** * @brief Insert a table array into the collection at the location before the supplied index. Overload of * sdv::toml::INodeCollectionInsert::InsertTableArray. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value - * nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically - * corrected. - * @param[in] ssName Name of the array node to insert. Will be ignored if the current node is also an array collection. - * The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not - * allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a - * literal key or a quoted key. + * @remarks In TOML, inline nodes are located before standard nodes. Table arrays can be inserted as inline node, in + * which case they will be inserted before any standard node (table or table array if defined as standard node). + * @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. + * @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if + * not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty + * string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML + * specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the + * parser decides itself whether the key is bare-key, a literal key or a quoted key. * @param[in] ePreference The preferred form of the node to be inserted. * @return On success the interface to the newly inserted node is returned or NULL otherwise. */ - virtual sdv::IInterfaceAccess* InsertTableArray(uint32_t uiIndex, const sdv::u8string& ssName, - sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) override; + virtual sdv::IInterfaceAccess* InsertTableArray(/*in*/ const sdv::u8string& ssInsertBefore, /*in*/ const sdv::u8string& ssName, + /*in*/ sdv::toml::EInsertPreference ePreference) override; /** * @brief Insert a TOML string as a child of the current collection node. If the collection is a table, the TOML string * should contain values and inline/external/array-table nodes with names. If the collection is an array, the TOML * string should contain and inline table nodes without names. Overload of sdv::toml::INodeCollectionInsert::InsertTOML. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value - * nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically - * corrected. + * @remarks In TOML, inline nodes are located before standard nodes. Dependable on the nodes defined in the TOML they + * might be transferred to inline or they might be inserted at a different location. + * @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between + * square brackets. Can be empty, causing the node to be inserted at the end. * @param[in] ssTOML The TOML string to insert. * @param[in] bRollbackOnPartly If only part of the nodes could be inserted, no node will be inserted. * @return The result of the insertion. */ - virtual sdv::toml::INodeCollectionInsert::EInsertResult InsertTOML(uint32_t uiIndex, const sdv::u8string& ssTOML, - bool bRollbackOnPartly) override; + virtual sdv::toml::INodeCollectionInsert::EInsertResult InsertTOML(/*in*/ const sdv::u8string& ssInsertBefore, + /*in*/ const sdv::u8string& ssTOML, /*in*/ bool bRollbackOnPartly) override; + + /** + * @brief Insert a TOML string as a child of the current collection node. If the collection is a table, the TOML string + * should contain values and inline/external/array-table nodes with names. If the collection is an array, the TOML + * string should contain and inline table nodes without names. + * @param[in] rptrInsertBefore The node to insert the TOML nodes before (if possible). Can be NULL, causing the TOML nodes + * to be inserted at the end. + * @param[in] ssTOML The TOML string to insert. + * @param[in] bRollbackOnPartly If only part of the nodes could be inserted, no node will be inserted. + * @return A pair structure with the result of the insertion and a vector of all the inserted nodes. + */ + std::pair>> InsertTOML( + const std::shared_ptr& rptrInsertBefore, const sdv::u8string& ssTOML, bool bRollbackOnPartly); /** * @brief Delete the current node. Overload of sdv::toml::INodeUpdate::DeleteNode. @@ -1080,6 +1165,23 @@ namespace toml_parser */ virtual bool DeleteNode() override; + /** + * @brief The derived class from the node collection can be inline or not. Overload of CNode::Inline. + * @return Returns whether the node is an inline node. + */ + virtual bool Inline() const override; + + /** + * @brief With some node collections it is possible to switch between inline and normal. Overload of CNode::Inline. + * @remarks Additional node composition information will be removed and the order within the parent node might be changed. + * @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal. + * @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables + * and table-arrays can be defined as standard). Making a node inline is always including the children. + * @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is + * always successful). When returning false, the switching might not be supported for this type. + */ + virtual bool Inline(bool bInline, bool bIncludeChildren = true) override; + /** * @brief Can the node convert to an inline definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeInline. * @return Returns whether the conversion to inline is possible. Returns 'true' when the node is already inline. @@ -1101,10 +1203,12 @@ namespace toml_parser /** * @brief Convert the node to a standard node. Overload of sdv::toml::INodeCollectionConvert::MakeStandard. + * @param[in] bIncludeChildren When set, applicable child nodes are made are converted to standard nodes as well (only + * tables and table-arrays can be defined as standard). * @return Returns whether the conversion was successful. Returns 'true' when the node was already defined as standard * node. */ - virtual bool MakeStandard() override; + virtual bool MakeStandard(/*in*/ bool bIncludeChildren) override; /** * @brief Delete a node from the collection. @@ -1114,28 +1218,6 @@ namespace toml_parser */ bool DeleteNode(const std::shared_ptr& rptrNode); - /** - * @brief Insert the node into the view. - * @remarks The node must be a descendant (direct child or an indirect child) of the node. - * @details Insert the node into the vector (and remove it from any previous vector if still assigned). Inline nodes can be - * assigned to stadard and inline nodes. Standard nodes can only be assigned to standard nodes. In the vector, first the - * inline nodes and then the standard nodes are located. This means that an inline node will be placed before standard nodes - * and standard nodes behind inline nodes, regardless of the index provided. - * @param[in] uiIndex Location within the vector to insert the node. Could be sdv::toml::npos to insert as last node. - * @param[in] rptrNode Reference to the smart pointer to the node to set the view for. If the node is not a direct child - * node, the view pointer of the node will be set. - * @return Returns true when the insertion was successful, false when node (likely because the node to insert is a standard - * node, whereas this node is an inline node. - */ - bool InsertIntoView(uint32_t uiIndex, const std::shared_ptr& rptrNode); - - /** - * @brief Remove a node from a view. - * @param[in] rptrNode Reference to the smart pointer pointing to the node to remove. - * @return Returns whether the removal was successful. - */ - bool RemoveFromView(const std::shared_ptr& rptrNode); - /** * @brief Find the index belonging to the provided node. * @param[in] rptrNode Reference to the smart pointer holding the node to return the index for. @@ -1151,7 +1233,7 @@ namespace toml_parser bool IsDescendant(const std::shared_ptr& rptrNode) const; /** - * @brief Generic inserting function for nodes. + * @brief Generic add function for nodes. * @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name. * E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be accessed and * traversed by using the index number in brackets. E.g. 'array[3]' would access the fourth element of the array 'array'. @@ -1160,10 +1242,6 @@ namespace toml_parser * @attention For an array, when no indexing is supplied, the latest entry will be returned. * @remarks If the node to insert exists already, but is marked implicit, the node will be returned and made explicit. In * all other cases the an error will occur that the node already exists. - * @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the - * collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value - * nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically - * corrected. * @param[in] rrangeKeyPath Reference to the token range containing the path to the node to insert. * @param[in] rtArgs Zero or more references to arguments passed to the constructor of the node classes being created by * this function. @@ -1171,7 +1249,7 @@ namespace toml_parser * there the returned node is a table within the table array. */ template - std::shared_ptr Insert(uint32_t uiIndex, const CTokenRange& rrangeKeyPath, const TArgs&... rtArgs); + std::shared_ptr AddNodeFromRange(const CTokenRange& rrangeKeyPath, const TArgs&... rtArgs); /** * @brief Combine the collection with the provided content (mathematical union). @@ -1191,6 +1269,13 @@ namespace toml_parser */ virtual bool Reduce(const std::shared_ptr& rptrCollection) = 0; + /** + * @brief When the parent changes (e.g. when moving items from one parser to the other), the items and all its sub-items + * need to reasign the parser. Overload of CNode::ReassignParser. + * @param[in] rParser Reference to the parser to assign. + */ + virtual void ReassignParser(CParser& rParser) override; + private: /** * @brief When set, the child nodes need grouping (values following each other, tables and table arrays at the end). @@ -1228,8 +1313,6 @@ namespace toml_parser // The child node uses the parent node pointer to indicate which node holds node. // The child node uses the view node pointer to indicate which node displays the node content. - std::vector> m_vecNodeOrder; ///< Vector holding the child nodes (could contain grand children as - ///< well). std::list> m_lstNodes; ///< List holding the direct child nodes. std::list> m_lstRecycleBin; ///< List holding the child elements that were deleted. This will ///< prevent destruction of the node class, which would otherwise @@ -1283,6 +1366,26 @@ namespace toml_parser */ virtual sdv::toml::ENodeType GetType() const override; + /** + * @brief Returns the amount of nodes. Overload of sdv::toml::INodeCollection::GetCount. + * @return The amount of nodes. + */ + virtual uint32_t GetCount() const override; + + /** + * @brief Get the node. Overload of CNodeCollection::Get. + * @param[in] uiIndex Index of the node to get. + * @return Smart pointer to the node object. + */ + virtual std::shared_ptr Get(uint32_t uiIndex) const override; + + /** + * @brief Delete the current node. Overload of sdv::toml::INodeUpdate::DeleteNode. + * @attention A successful deletion will cause all interfaces to the current node to become inoperable. + * @return Returns whether the deletion was successful. + */ + virtual bool DeleteNode() override; + /** * @brief Create the TOML text based on the content using an optional prefix node. Overload of CNode::GenerateTOML. * @param[in] rContext Reference to the context class to use during TOML code generation. @@ -1308,10 +1411,12 @@ namespace toml_parser * table doesn't have a name. * @remarks Additional node composition information will be removed and the order within the parent node might be changed. * @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal. + * @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables + * and table-arrays can be defined as standard). Making a node inline is always including the children. * @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is * always successful). When returning false, the switching might not be supported for this type. */ - virtual bool Inline(bool bInline) override; + virtual bool Inline(bool bInline, bool bIncludeChildren = true) override; /** * @brief Checks whether the table was explicitly defined. Overload of CNodeCollection::ExplicitlyDefined. @@ -1342,9 +1447,17 @@ namespace toml_parser */ virtual bool Reduce(const std::shared_ptr& rptrCollection) override; + /** + * @brief After every insert, deletion and shift, the node order of this and all sub- tables need to be rebuild. + * @param[in] bForce Force rebuild, even if locked. + */ + virtual void RebuildNodeOrder(bool bForce) override; + private: - bool m_bDefinedExplicitly = true; ///< When set, the table is defined explicitly. - bool m_bInline = false; ///< Flag determining whether the table is inline or not. + bool m_bDefinedExplicitly = true; ///< When set, the table is defined explicitly. + bool m_bInline = false; ///< Flag determining whether the table is inline or not. + std::vector> m_vecNodeOrder; ///< Vector holding the child nodes (could contain grand children as + ///< well). }; /** @@ -1426,10 +1539,28 @@ namespace toml_parser * @attention Array element access indices starts with 0! * @attention For an array element inserting, when no indexing is supplied, the latest entry will be returned. * @param[in] rssPath Reference to the path of the node to searched for. - * @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found. + * @return Returns a shared pointer to the wanted node if it was found or a node with invalid content if it was not found. */ virtual std::shared_ptr Direct(const std::string& rssPath) const override; + /** + * @brief Get or create the parent nodes automotatically from the path. Overload of CNodeCollection::SmartParentCreate. + * @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name. + * E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be accessed and + * traversed by using the index number in brackets. E.g. 'array[3]' would access the fourth element of the array 'array'. + * These access conventions can also be chained like 'table.array[2][1].subtable.integerElement'. + * @attention Array indexing starts with 0! + * @attention For an array, when no indexing or a too large index number is supplied, a new entry will be created and + * returned. + * @param[in] rssPath The path of the node to searched for. + * @param[in] bInsertTableArray Since a table array consists of an array and a table, this is different than the other + * insertions that only insert one element. Some special treatment is needed at certain points. + * @return Returns a pair with the shared pointer to the parent node and the leftover name. If the creation could not be + * done, a NULL pointer is returned. + */ + virtual std::pair, std::string> SmartParentCreate(const std::string& rssPath, + bool bInsertTableArray = false) override; + /** * @brief Create the TOML text based on the content using an optional prefix node. Overload of CNode::GenerateTOML. * @param[in] rContext Reference to the context class to use during TOML code generation. @@ -1450,13 +1581,6 @@ namespace toml_parser */ bool TableArray() const; - /** - * @brief Can the node convert to a standard definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeStandard. - * @return Returns whether the conversion to standard is possible. Returns 'true' when the node is already defined as - * standard node. - */ - virtual bool CanMakeStandard() const override; - /** * @brief The derived class from the node collection can be inline or not. Overload of CNode::Inline. * @return Returns whether the node is an inline node. @@ -1472,10 +1596,19 @@ namespace toml_parser * doesn't have a name that can be used to define the explicit table array. * @remarks Additional node composition information will be removed and the order within the parent node might be changed. * @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal. + * @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables + * and table-arrays can be defined as standard). Making a node inline is always including the children. * @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is * always successful). When returning false, the switching might not be supported for this type. */ - virtual bool Inline(bool bInline) override; + virtual bool Inline(bool bInline, bool bIncludeChildren = true) override; + + /** + * @brief Can the node convert to a standard definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeStandard. + * @return Returns whether the conversion to standard is possible. Returns 'true' when the node is already defined as + * standard node. + */ + virtual bool CanMakeStandard() const override; /** * @brief Does the last child node need a comma following the node? @@ -1534,7 +1667,7 @@ namespace toml_parser * @brief Constructor * @param[in] rparser Reference to the TOML parser. */ - CRootTable(CParser& rparser) : CTable(rparser, "root", "", false) + CRootTable(CParser& rparser) : CTable(rparser, "root", "", false, true) {} /** @@ -1550,20 +1683,25 @@ namespace toml_parser */ virtual bool Inline() const override { + // The root node can never be inline. return false; } /** - * @brief With some node collections it is possible to switch between inline and normal. Overload of - * CNodeCollection::Inline. + * @brief Switch between inline and explicit table definition. Overload of CNodeCollection::Inline. + * @attention It is not possible to switch to an explicit table definition if the table is part of an array, since the + * table doesn't have a name. * @remarks Additional node composition information will be removed and the order within the parent node might be changed. * @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal. + * @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables + * and table-arrays can be defined as standard). Making a node inline is always including the children. * @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is * always successful). When returning false, the switching might not be supported for this type. */ - virtual bool Inline(bool bInline) override + virtual bool Inline(bool bInline, bool bIncludeChildren = true) override { - return bInline == false; + // Pass call to table implementation. + return CTable::Inline(bInline, bIncludeChildren); } }; @@ -1580,7 +1718,7 @@ namespace toml_parser } template - inline std::shared_ptr CNodeCollection::Insert(uint32_t uiIndex, const CTokenRange& rrangeKeyPath, const TArgs&... rtArgs) + inline std::shared_ptr CNodeCollection::AddNodeFromRange(const CTokenRange& rrangeKeyPath, const TArgs&... rtArgs) { // Get the first part of the node auto prKey = SplitNodeKey(rrangeKeyPath); @@ -1613,9 +1751,8 @@ namespace toml_parser "' exists already, but is not a table array."); // Create the table. - ptrNode = ptrTableArray->Insert(sdv::toml::npos, CTokenRange(prKey.first, prKey.first.get().Next()), + ptrNode = ptrTableArray->AddNodeFromRange(CTokenRange(prKey.first, prKey.first.get().Next()), false, true); - ptrNode->SetViewPtr(Cast()); } else if (!Cast() && itNode != m_lstNodes.end()) { // If existing... this might be a duplicate if not explicitly defined before. @@ -1644,7 +1781,7 @@ namespace toml_parser // If the current node is implicit, take over the inline flag (this determines whether a sub-table definition is // allowed or not). if (!ExplicitlyDefined()) - Inline(ptrNode->Inline()); + Inline(ptrNode->Inline(), false); } } else // Intermediate node @@ -1663,6 +1800,9 @@ namespace toml_parser auto prNextKey = SplitNodeKey(prKey.second); if (prNextKey.first.get().Category() != ETokenCategory::token_integer) { + // The table array should have an ordered vector. This is not the case yet. Rebuild the table array. + ptrNode->Cast()->RebuildNodeOrder(true); + // Get the last table if (!ptrNode->Cast()->GetCount()) throw XTOMLParseException("The parent table array node '" + prKey.first.get().StringValue() + @@ -1700,23 +1840,14 @@ namespace toml_parser std::shared_ptr ptrNodeCollection = ptrNode->Cast(); if (!ptrNodeCollection) throw XTOMLParseException("Parent node is not an array or table '" + ptrNode->GetPath(true) + "'."); - ptrNode = ptrNodeCollection->Insert(sdv::toml::npos, prKey.second, rtArgs...); + ptrNode = ptrNodeCollection->AddNodeFromRange(prKey.second, rtArgs...); if (!ptrNode) throw XTOMLParseException("Could not create the node '" + prKey.first.get().StringValue() + "'."); } - // Insert the node at the requested location if this node is inline or this is the view of the node. - auto itPos = (static_cast(uiIndex) >= m_vecNodeOrder.size()) ? m_vecNodeOrder.end() : - m_vecNodeOrder.begin() + static_cast(uiIndex); - m_vecNodeOrder.insert(itPos, ptrNode); - if (ptrNode->GetParentPtr() != Cast()) - ptrNode->SetViewPtr(Cast()); - // Return the result return ptrNode; } - - } // namespace toml_parser #endif // !defined PARSER_NODE_TOML_H \ No newline at end of file diff --git a/sdv_services/core/toml_parser/parser_toml.cpp b/sdv_services/core/toml_parser/parser_toml.cpp index 226825d..6bf0600 100644 --- a/sdv_services/core/toml_parser/parser_toml.cpp +++ b/sdv_services/core/toml_parser/parser_toml.cpp @@ -54,6 +54,9 @@ namespace toml_parser try { + // Lock the rebuild of the node order + auto lock = CreateRebuildLockObject(); + // Run through all tokens of the lexer and process the tokens. bool bEOF = false; // Explicit test, since the peek could return EOF, but the cursor might not be at the end yet. while (!bEOF && !m_lexer.IsEnd()) @@ -95,7 +98,7 @@ namespace toml_parser } catch (const sdv::toml::XTOMLParseException& e) { - std::cout << e.what() << '\n'; + std::cerr << e.what() << std::endl; throw; } @@ -115,6 +118,11 @@ namespace toml_parser return m_lexer; } + CNodeIndexer& CParser::Indexer() + { + return m_indexer; + } + const CNodeCollection& CParser::Root() const { auto ptrCollection = m_ptrRoot->Cast(); @@ -139,6 +147,49 @@ namespace toml_parser return m_ptrRoot->GenerateTOML(rssPrefixKey); } + CParser::CLockRebuild::CLockRebuild(CParser& rParser) : m_rParser(rParser) + { + rParser.IncrRebuildLockCnt(); + } + + CParser::CLockRebuild::CLockRebuild(const CLockRebuild& rLockRebuild) : m_rParser(rLockRebuild.m_rParser) + { + m_rParser.IncrRebuildLockCnt(); + } + + CParser::CLockRebuild::CLockRebuild(CLockRebuild&& rLockRebuild) : m_rParser(rLockRebuild.m_rParser) + { + m_rParser.IncrRebuildLockCnt(); + } + + CParser::CLockRebuild::~CLockRebuild() + { + m_rParser.DecrRebuildLockCnt(); + } + + CParser::CLockRebuild CParser::CreateRebuildLockObject() + { + return CLockRebuild(*this); + } + + bool CParser::RebuildLocked() const + { + return m_nRebuildLockCnt ? true : false; + } + + void CParser::IncrRebuildLockCnt() + { + ++m_nRebuildLockCnt; + } + + void CParser::DecrRebuildLockCnt() + { + if (!m_nRebuildLockCnt) + return; // Should not occur + --m_nRebuildLockCnt; + if (!m_nRebuildLockCnt) Root().RebuildNodeOrder(false); + } + void CParser::ProcessTable(CNodeTokenRange& rNodeRange) { // Get the table path (table name preceded by parent tables separated with dots). @@ -154,7 +205,7 @@ namespace toml_parser m_lexer.SmartExtendNodeRange(rNodeRange); // Add the table to the root - auto ptrTable = m_ptrRoot->Insert(sdv::toml::npos, rangeKeyPath, false); + auto ptrTable = m_ptrRoot->AddNodeFromRange(rangeKeyPath, false); if (ptrTable) { m_ptrCurrentCollection = ptrTable->Cast(); @@ -176,7 +227,7 @@ namespace toml_parser m_lexer.SmartExtendNodeRange(rNodeRange); // Add the table array to the root - auto ptrTableArray = m_ptrRoot->Insert(sdv::toml::npos, rangeKeyPath); + auto ptrTableArray = m_ptrRoot->AddNodeFromRange(rangeKeyPath); if (ptrTableArray) { m_ptrCurrentCollection = ptrTableArray->Cast(); @@ -226,46 +277,43 @@ namespace toml_parser switch (rAssignmentValue.Category()) { case ETokenCategory::token_boolean: - ptrNode = m_ptrCurrentCollection->Insert(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.BooleanValue(), + ptrNode = m_ptrCurrentCollection->AddNodeFromRange(rrangeKeyPath, rAssignmentValue.BooleanValue(), rAssignmentValue.RawString()); break; case ETokenCategory::token_integer: - ptrNode = m_ptrCurrentCollection->Insert(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.IntegerValue(), + ptrNode = m_ptrCurrentCollection->AddNodeFromRange(rrangeKeyPath, rAssignmentValue.IntegerValue(), rAssignmentValue.RawString()); break; case ETokenCategory::token_float: - ptrNode = m_ptrCurrentCollection->Insert(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.FloatValue(), + ptrNode = m_ptrCurrentCollection->AddNodeFromRange(rrangeKeyPath, rAssignmentValue.FloatValue(), rAssignmentValue.RawString()); break; case ETokenCategory::token_string: switch (rAssignmentValue.StringType()) { case ETokenStringType::literal_string: - ptrNode = m_ptrCurrentCollection->Insert(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), + ptrNode = m_ptrCurrentCollection->AddNodeFromRange(rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::literal_string, rAssignmentValue.RawString()); break; case ETokenStringType::multi_line_literal: - ptrNode = m_ptrCurrentCollection->Insert( - sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::multi_line_literal, - rAssignmentValue.RawString()); + ptrNode = m_ptrCurrentCollection->AddNodeFromRange(rrangeKeyPath, rAssignmentValue.StringValue(), + CStringNode::EQuotationType::multi_line_literal, rAssignmentValue.RawString()); break; case ETokenStringType::multi_line_quoted: - ptrNode = m_ptrCurrentCollection->Insert( - sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::multi_line_quoted, - rAssignmentValue.RawString()); + ptrNode = m_ptrCurrentCollection->AddNodeFromRange(rrangeKeyPath, rAssignmentValue.StringValue(), + CStringNode::EQuotationType::multi_line_quoted, rAssignmentValue.RawString()); break; case ETokenStringType::quoted_string: default: - ptrNode = m_ptrCurrentCollection->Insert( - sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::quoted_string, - rAssignmentValue.RawString()); + ptrNode = m_ptrCurrentCollection->AddNodeFromRange(rrangeKeyPath, rAssignmentValue.StringValue(), + CStringNode::EQuotationType::quoted_string, rAssignmentValue.RawString()); break; } break; case ETokenCategory::token_syntax_array_open: { auto ptrCurrentCollectionStored = m_ptrCurrentCollection; - ptrNode = m_ptrCurrentCollection->Insert(sdv::toml::npos, rrangeKeyPath); + ptrNode = m_ptrCurrentCollection->AddNodeFromRange(rrangeKeyPath); m_ptrCurrentCollection = ptrNode->Cast(); m_stackEnvironment.push(EEnvironment::array); ProcessArray(rNodeRange); @@ -276,7 +324,7 @@ namespace toml_parser case ETokenCategory::token_syntax_inline_table_open: { auto ptrCurrentCollectionStored = m_ptrCurrentCollection; - ptrNode = m_ptrCurrentCollection->Insert(sdv::toml::npos, rrangeKeyPath, true); + ptrNode = m_ptrCurrentCollection->AddNodeFromRange(rrangeKeyPath, true); m_ptrCurrentCollection = ptrNode->Cast(); m_stackEnvironment.push(EEnvironment::inline_table); ProcessInlineTable(rNodeRange); diff --git a/sdv_services/core/toml_parser/parser_toml.h b/sdv_services/core/toml_parser/parser_toml.h index a390d66..f953190 100644 --- a/sdv_services/core/toml_parser/parser_toml.h +++ b/sdv_services/core/toml_parser/parser_toml.h @@ -18,6 +18,7 @@ #include "lexer_toml.h" #include "parser_node_toml.h" #include "miscellaneous.h" +#include "parser_node_indexer.h" #include #include #include @@ -25,7 +26,9 @@ /// The TOML parser namespace namespace toml_parser { + // Forward declarations class CNode; + class CNodeCollection; /** * @brief Creates a tree structure from input of UTF-8 encoded TOML source data @@ -33,6 +36,10 @@ namespace toml_parser class CParser : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser { public: + // Forward declaration + class CLockRebuild; + friend CLockRebuild; ///< Friend class can trigger manage rebuild lock counter. + /** * @brief Construct a new Parser object * @param[in] rssString UTF-8 encoded data of a TOML source @@ -66,6 +73,12 @@ namespace toml_parser */ CLexer& Lexer(); + /** + * @brief Get the indexer object managing the overall order of the nodes. + * @return Reference to the index object. + */ + CNodeIndexer& Indexer(); + /** * @{ * @brief Return the root node. @@ -85,7 +98,65 @@ namespace toml_parser */ std::string GenerateTOML(const std::string& rssPrefixKey = std::string()) const; + /** + * @brief Lock rebuild object preventing rebuilding the node order of all the tables. + */ + class CLockRebuild + { + friend CParser; ///< Parser can access the constructor + + /** + * @brief Constructor + * @param[in] rParser Reference to the parser object + */ + CLockRebuild(CParser& rParser); + + public: + /** + * @brief Copy constructor + * @param[in] rLockRebuild Reference to another rebuild lock object. + */ + CLockRebuild(const CLockRebuild& rLockRebuild); + + /** + * @brief Move constructor + * @param[in] rLockRebuild Reference to another rebuild lock object. + */ + CLockRebuild(CLockRebuild&& rLockRebuild); + + /** + * @brief Destructor + */ + ~CLockRebuild(); + + private: + CParser& m_rParser; ///< Reference to the parser object + }; + + /** + * @brief Create a rebuild lock object. During the lifetime of the object rebuilding the node order is locked using the lock + * counter method. + * @return An instance of the rebuild lock object. + */ + CLockRebuild CreateRebuildLockObject(); + + /** + * @brief Returns whether rebuild is locked at the moment. + * @return Set when rebuild is locked. + */ + bool RebuildLocked() const; + private: + /** + * @brief Increase the rebuild lock counter. A lock count larger than 0 will prevent a rebuild. + */ + void IncrRebuildLockCnt(); + + /** + * @brief Decrease the rebuild lock counter. A lock count of 0 will trigger the rebuild. + */ + void DecrRebuildLockCnt(); + /** * @brief Process a table declaration. * @param[in, out] rNodeRange Reference to the extended token range of the node. @@ -139,10 +210,13 @@ namespace toml_parser inline_table ///< Environment for a table }; - enum_stack m_stackEnvironment; ///< Tracking of environments in nested structures. - std::shared_ptr m_ptrRoot; ///< The one root node. - std::shared_ptr m_ptrCurrentCollection; ///< The current collection node. - CLexer m_lexer; ///< Lexer. + enum_stack m_stackEnvironment; ///< Tracking of environments in nested structures. + std::shared_ptr m_ptrRoot; ///< The one root node. + std::shared_ptr m_ptrCurrentCollection; ///< The current collection node. + CLexer m_lexer; ///< Lexer object user for lexing the TOML code. + CNodeIndexer m_indexer; ///< Indexer managing the oberall order of the nodes. + size_t m_nRebuildLockCnt = 0; ///< A lock counter > 0 will prevent the node order + ///< rebuild. }; } // namespace toml_parser diff --git a/sdv_services/ipc_com/CMakeLists.txt b/sdv_services/ipc_com/CMakeLists.txt index ef48e26..e3e15eb 100644 --- a/sdv_services/ipc_com/CMakeLists.txt +++ b/sdv_services/ipc_com/CMakeLists.txt @@ -25,8 +25,6 @@ add_library(ipc_com SHARED "com_channel.cpp" "marshall_object.h" "marshall_object.cpp" - - #"scheduler.cpp" ) if(UNIX) target_link_libraries(ipc_com rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) diff --git a/sdv_services/ipc_com/com_channel.cpp b/sdv_services/ipc_com/com_channel.cpp index 4e7d23d..ed436d7 100644 --- a/sdv_services/ipc_com/com_channel.cpp +++ b/sdv_services/ipc_com/com_channel.cpp @@ -18,7 +18,7 @@ #include #include #include -#include "../../global/scheduler/scheduler.cpp" +#include "../../global/scheduler/scheduler.h" CChannelConnector::CChannelConnector(CCommunicationControl& rcontrol, uint32_t uiIndex, sdv::IInterfaceAccess* pChannelEndpoint) : m_rcontrol(rcontrol), m_ptrChannelEndpoint(pChannelEndpoint), diff --git a/sdv_services/ipc_com/com_channel.h b/sdv_services/ipc_com/com_channel.h index b2ce3da..643ded7 100644 --- a/sdv_services/ipc_com/com_channel.h +++ b/sdv_services/ipc_com/com_channel.h @@ -95,8 +95,8 @@ public: * @brief Sends data consisting of multiple data chunks via the IPC connection. * @param[in] tProxyID Marshall ID of the proxy (source). * @param[in] tStubID Marshall ID of the stub (target). - * @param[in] rseqInputData Sequence of data buffers to be sent. May be altered during processing to add/change the sequence content - * without having to copy the data. + * @param[in] rseqInputData Sequence of data buffers to be sent. May be altered during processing to add/change the sequence + * content without having to copy the data. * @return Returns the results of the call or throws a marshall exception. */ sdv::sequence> MakeCall(sdv::ps::TMarshallID tProxyID, sdv::ps::TMarshallID tStubID, @@ -155,7 +155,9 @@ private: sdv::ipc::IDataSend* m_pDataSend = nullptr; ///< Pointer to the send interface. std::mutex m_mtxCalls; ///< Call map protection. std::map m_mapCalls; ///< call map. - CTaskScheduler m_scheduler; ///< Scheduler to process incoming calls. + CTaskScheduler m_scheduler; ///< Scheduler to process incoming calls. + sdv::core::IPermissionControl* m_pPermissionControl = nullptr; ///< Pointer to the permission control interface of the + ///< target system. }; #endif // !defined COM_CHANNEL_H \ No newline at end of file diff --git a/sdv_services/ipc_com/com_ctrl.cpp b/sdv_services/ipc_com/com_ctrl.cpp index 0999351..d10cbb8 100644 --- a/sdv_services/ipc_com/com_ctrl.cpp +++ b/sdv_services/ipc_com/com_ctrl.cpp @@ -16,6 +16,8 @@ #include #include "com_channel.h" #include "marshall_object.h" +#include +#include thread_local CChannelConnector* CCommunicationControl::m_pConnectorContext = nullptr; @@ -40,7 +42,7 @@ void CCommunicationControl::OnShutdown() std::unique_lock lock(m_mtxChannels); auto vecInitialConnectMon = std::move(m_vecInitialConnectMon); lock.unlock(); - for (std::thread& rthread : vecInitialConnectMon) + for (sdv::core::secure_thread& rthread : vecInitialConnectMon) { if (rthread.joinable()) rthread.join(); @@ -63,10 +65,11 @@ sdv::com::TConnectionID CCommunicationControl::CreateServerConnection(/*in*/ sdv switch (eChannelType) { case sdv::com::EChannelType::local_channel: - ssChannelServer = "LocalChannelControl"; + ssChannelServer = static_cast(sdv::app::GetAppSettingsAttribute("Communication.DefaultProvider")); break; case sdv::com::EChannelType::remote_channel: - ssChannelServer = "RemoteChannelControl"; + // Currently not supported + ssChannelServer = "UnknownComProvider"; break; default: return {}; diff --git a/sdv_services/ipc_com/com_ctrl.h b/sdv_services/ipc_com/com_ctrl.h index 7a10887..d9e791e 100644 --- a/sdv_services/ipc_com/com_ctrl.h +++ b/sdv_services/ipc_com/com_ctrl.h @@ -196,7 +196,7 @@ public: private: std::mutex m_mtxChannels; ///< Protect the channel map. std::vector> m_vecChannels; ///< Channel vector. - std::vector m_vecInitialConnectMon; ///< Initial connection monitor. + std::vector m_vecInitialConnectMon; ///< Initial connection monitor. std::recursive_mutex m_mtxObjects; ///< Protect object vectors. std::vector> m_vecMarshallObjects; ///< Vector with marshall objects; lifetime is handled by channel. std::map> m_mapStubObjects; ///< Map of interfaces to stub objects diff --git a/sdv_services/ipc_connect/client.cpp b/sdv_services/ipc_connect/client.cpp index 366f321..8413327 100644 --- a/sdv_services/ipc_connect/client.cpp +++ b/sdv_services/ipc_connect/client.cpp @@ -1,3 +1,4 @@ + /******************************************************************************** * Copyright (c) 2025-2026 ZF Friedrichshafen AG * @@ -18,7 +19,7 @@ #include #include -CRepositoryProxy::CRepositoryProxy(CClient& rClient, sdv::com::TConnectionID tConnection, +CRepositoryProxy::CRepositoryProxy(CClientConnect& rClient, sdv::com::TConnectionID tConnection, sdv::IInterfaceAccess* pRepositoryProxy) : m_rClient(rClient), m_tConnection(tConnection), m_ptrRepositoryProxy(pRepositoryProxy) {} @@ -26,120 +27,131 @@ CRepositoryProxy::CRepositoryProxy(CClient& rClient, sdv::com::TConnectionID tCo void CRepositoryProxy::DestroyObject() { // Call the client to disconnect the connection and destroy the object. - m_rClient.Disconnect(m_tConnection); + m_rClient.Disconnect(); } -bool CClient::OnInitialize() +sdv::com::TConnectionID CRepositoryProxy::GetConnectionID() const { - return true; + return m_tConnection; } -void CClient::OnShutdown() -{ - sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject("CommunicationControl"); - if (!pConnectionControl) - SDV_LOG_ERROR("Failed to get communication control!"); - - // Disconnect from all repositories - std::unique_lock lock(m_mtxRepositoryProxies); - auto mapRepositoryProxiesCopy = std::move(m_mapRepositoryProxies); - lock.unlock(); - if (pConnectionControl) - { - for (const auto& rvtRepository : mapRepositoryProxiesCopy) - pConnectionControl->RemoveConnection(rvtRepository.first); - } -} - -sdv::IInterfaceAccess* CClient::Connect(const sdv::u8string& ssConnectString) +bool CClientConnect::OnInitialize() { const sdv::app::IAppContext* pContext = sdv::core::GetCore(); if (!pContext) { SDV_LOG_ERROR("Failed to get application context!"); - return nullptr; + return false; } sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject("CommunicationControl"); if (!pConnectionControl) { SDV_LOG_ERROR("Failed to get communication control!"); - return nullptr; + return false; } - sdv::ipc::IChannelAccess* pChannelAccess = nullptr; - std::string ssConfig; - try + // Check for a provider. Without provider there is no object to use for listening. + if (m_ssProvider.empty()) { - // Determine whether the service is running as server or as client. - sdv::toml::CTOMLParser config(ssConnectString); - std::string ssType = config.GetDirect("Client.Type").GetValue(); - if (ssType.empty()) ssType = "Local"; - if (ssType == "Local") - { - uint32_t uiInstanceID = config.GetDirect("Client.Instance").GetValue(); - pChannelAccess = sdv::core::GetObject("LocalChannelControl"); - if (!pChannelAccess) - { - SDV_LOG_ERROR("No local channel control or channel control not configured as client!"); - return nullptr; - } - - ssConfig = std::string(R"code([IpcChannel] -Name = "LISTENER_)code") + std::to_string(uiInstanceID ? uiInstanceID : pContext->GetInstanceID()) + R"code(" -)code"; - - } - else if (ssType == "Remote") - { - std::string ssInterface = config.GetDirect("Client.Interface").GetValue(); - uint32_t uiPort = config.GetDirect("Client.Interface").GetValue(); - if (ssInterface.empty() || !uiPort) - { - SDV_LOG_ERROR("Missing interface or port number to initialize a remote client!"); - return nullptr; - } - pChannelAccess = sdv::core::GetObject("RemoteChannelControl"); - if (!pChannelAccess) - { - SDV_LOG_ERROR("No remote channel control or channel control not configured as client!"); - return nullptr; - } - - ssConfig = R"code([IpcChannel] -Interface = ")code" + ssInterface + R"code( -Port = ")code" + std::to_string(uiPort) + R"code( -)code"; - } - else - { - SDV_LOG_ERROR("Invalid or missing listener configuration for listener service!"); - SetObjectIntoConfigErrorState(); - return nullptr; - } + SDV_LOG_ERROR("Missing provider name for creating a client object!"); + return false; } - catch (const sdv::toml::XTOMLParseException& rexcept) + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject(m_ssProvider); + if (!pChannelAccess) { - SDV_LOG_ERROR("Invalid service configuration for listener service: ", rexcept.what(), "!"); - SetObjectIntoConfigErrorState(); - return nullptr; + SDV_LOG_ERROR("Cannot instantiate provider '", m_ssProvider, "' for the creation of a client object!"); + return false; } + // The connection will be established in the Connect function. + return true; +} + +void CClientConnect::OnShutdown() +{ + sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject("CommunicationControl"); + if (!pConnectionControl) + SDV_LOG_ERROR("Failed to get communication control!"); + + // Disconnect + Disconnect(); +} + +bool CClientConnect::Connect() +{ + sdv::u8string ssProvider; + sdv::u8string ssConfig; + + { + std::unique_lock lock(m_mtx); + ssProvider = m_ssProvider; + ssConfig = BuildObjectConfig(); + } + + const sdv::app::IAppContext* pContext = sdv::core::GetCore(); + if (!pContext) + { + SDV_LOG_ERROR("Failed to get application context!"); + return false; + } + sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject("CommunicationControl"); + if (!pConnectionControl) + { + SDV_LOG_ERROR("Failed to get communication control!"); + return false; + } + + // Check for a provider. Without provider there is no object to use for listening. + if (ssProvider.empty()) + { + SDV_LOG_ERROR("Missing provider name for creating a client object!"); + return false; + } + + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject(ssProvider); + if (!pChannelAccess) + { + SDV_LOG_ERROR("Cannot instantiate provider '", m_ssProvider, "' for the creation of a client object!"); + return false; + } + + // Get the repository + sdv::TInterfaceAccessPtr ptrRespository = sdv::core::GetObject("RepositoryService"); + if (!ptrRespository) + { + SDV_LOG_ERROR("Failed to get repository service!"); + return false; + } + + // Add a dependency of the provider to this service (to prevent the provider to be terminated while the service is still + // running). + sdv::core::IObjectDependency* pObjectDependency = ptrRespository.GetInterface(); + if (!pObjectDependency) + { + SDV_LOG_ERROR("Failed to get the object dependency interface!"); + return false; + } + + pObjectDependency->AddObjectDependency(Self().ssName, ssProvider); + + // First access the listener channel. This allows us to access the channel creation interface. // TODO: Use named mutex to prevent multiple connections at the same time. // Connect to the channel. sdv::TObjectPtr ptrListenerEndpoint = pChannelAccess->Access(ssConfig); + // Assign the endpoint to the communication service. sdv::IInterfaceAccess* pListenerProxy = nullptr; - sdv::com::TConnectionID tListenerConnection = pConnectionControl->AssignClientEndpoint(ptrListenerEndpoint, 5000, - pListenerProxy); + sdv::com::TConnectionID tListenerConnection = + pConnectionControl->AssignClientEndpoint(ptrListenerEndpoint, 5000, pListenerProxy); ptrListenerEndpoint.Clear(); // Lifetime has been taken over by communication control. if (!tListenerConnection || !pListenerProxy) { SDV_LOG_ERROR("Could not assign the client endpoint!"); if (tListenerConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tListenerConnection); - return nullptr; + return false; } sdv::TInterfaceAccessPtr ptrListenerProxy(pListenerProxy); @@ -149,9 +161,26 @@ Port = ")code" + std::to_string(uiPort) + R"code( { SDV_LOG_ERROR("Could not get the channel creation interface!"); if (tListenerConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tListenerConnection); - return nullptr; + return false; } - sdv::u8string ssConnectionString = pRequestChannel->RequestChannel(""); + + sdv::u8string ssRequestConfig; + + // Tunnel providers need the full object config so the private channel + // can preserve provider-specific fields such as the tunnel name. + // For shared memory we keep the legacy behavior and let the provider + // create its own private channel details. + if (ssProvider == "unix_domain_sockets_tunnel") + { + ssRequestConfig = ssConfig; + } + else + { + ssRequestConfig.clear(); + } + + sdv::u8string ssConnectionString = pRequestChannel->RequestChannel(ssRequestConfig); + // Disconnect from the listener if (tListenerConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tListenerConnection); @@ -159,7 +188,7 @@ Port = ")code" + std::to_string(uiPort) + R"code( if (ssConnectionString.empty()) { SDV_LOG_ERROR("Could not get the private channel connection information!"); - return nullptr; + return false; } // TODO: Use named mutex to prevent multiple connections at the same time. @@ -174,30 +203,64 @@ Port = ")code" + std::to_string(uiPort) + R"code( { SDV_LOG_ERROR("Could not assign the client endpoint to the private channel!"); if (tPrivateConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tPrivateConnection); - return nullptr; + return false; } // Create a remote repository object - std::unique_lock lock(m_mtxRepositoryProxies); - m_mapRepositoryProxies.try_emplace(tPrivateConnection, *this, tPrivateConnection, pPrivateProxy); + m_ptrRemoteRepo = std::make_shared(*this, tPrivateConnection, pPrivateProxy); - return pPrivateProxy; + return true; } -void CClient::Disconnect(sdv::com::TConnectionID tConnectionID) +bool CClientConnect::Disconnect() { - // Find the connection, disconnect and remove the connection from the repository list. - std::unique_lock lock(m_mtxRepositoryProxies); - auto itRepository = m_mapRepositoryProxies.find(tConnectionID); - if (itRepository == m_mapRepositoryProxies.end()) return; + std::unique_lock lock(m_mtx); + + if (!m_ptrRemoteRepo) return false; + + // Whatever happens, the connection will be removed + std::shared_ptr ptrRemoteRepoLocal = std::move(m_ptrRemoteRepo); // Disconnect sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject("CommunicationControl"); if (!pConnectionControl) + { SDV_LOG_ERROR("Failed to get communication control!"); - else - pConnectionControl->RemoveConnection(itRepository->first); + return false; + } + pConnectionControl->RemoveConnection(ptrRemoteRepoLocal->GetConnectionID()); - // Remove entry - m_mapRepositoryProxies.erase(itRepository); + // Get the repository + sdv::TInterfaceAccessPtr ptrRespository = sdv::core::GetObject("RepositoryService"); + if (!ptrRespository) + { + SDV_LOG_ERROR("Failed to get repository service!"); + return false; + } + + // Add a dependency of the provider to this service (to prevent the provider to be terminated while the service is still + // running). + sdv::core::IObjectDependency* pObjectDependency = ptrRespository.GetInterface(); + if (!pObjectDependency) + { + SDV_LOG_ERROR("Failed to get the object dependency interface!"); + return false; + } + pObjectDependency->RemoveObjectDependency("ClientConnectService", m_ssProvider); + + return true; +} + +bool CClientConnect::IsConnected() const +{ + std::unique_lock lock(m_mtx); + + return m_ptrRemoteRepo ? true : false; +} + +sdv::IInterfaceAccess* CClientConnect::GetRemoteRepository() +{ + std::unique_lock lock(m_mtx); + + return m_ptrRemoteRepo ? m_ptrRemoteRepo.get() : nullptr; } diff --git a/sdv_services/ipc_connect/client.h b/sdv_services/ipc_connect/client.h index 7b50edc..3811177 100644 --- a/sdv_services/ipc_connect/client.h +++ b/sdv_services/ipc_connect/client.h @@ -19,7 +19,7 @@ #include // Forward declaration. -class CClient; +class CClientConnect; /** * @brief Class managing the connection and providing access to the server repository through a proxy. @@ -33,7 +33,7 @@ public: * @param[in] tConnection The connection ID to the server. * @param[in] pRepositoryProxy Proxy to the server repository. */ - CRepositoryProxy(CClient& rClient, sdv::com::TConnectionID tConnection, sdv::IInterfaceAccess* pRepositoryProxy); + CRepositoryProxy(CClientConnect& rClient, sdv::com::TConnectionID tConnection, sdv::IInterfaceAccess* pRepositoryProxy); /** * @brief Do not allow a copy constructor. @@ -59,8 +59,14 @@ public: */ virtual void DestroyObject() override; + /** + * @brief Get the connection ID for this connection. + * @return The connection ID. + */ + sdv::com::TConnectionID GetConnectionID() const; + private: - CClient& m_rClient; ///< Reference to the client object. + CClientConnect& m_rClient; ///< Reference to the client object. sdv::com::TConnectionID m_tConnection = {}; ///< Connection ID. sdv::TInterfaceAccessPtr m_ptrRepositoryProxy; ///< Smart pointer to the remote repository. }; @@ -68,7 +74,7 @@ private: /** * @brief Client object */ -class CClient : public sdv::CSdvObject, public sdv::com::IClientConnect +class CClientConnect : public sdv::CSdvObject, public sdv::com::IClientConnect { public: // Interface map @@ -78,8 +84,15 @@ public: // Object declaration DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) - DECLARE_OBJECT_CLASS_NAME("ConnectionService") - DECLARE_OBJECT_SINGLETON() + DECLARE_OBJECT_CLASS_NAME("ClientConnectService") + DECLARE_OBJECT_DEPENDENCIES("CommunicationControl") + + // Parameter map + BEGIN_SDV_PARAM_MAP() + SDV_PARAM_ENABLE_LOCKING() + SDV_PARAM_GROUP("Provider") + SDV_PARAM_ENTRY(m_ssProvider, "Name", "", "", "Provider name to create a connection for.") + END_SDV_PARAM_MAP() /** * @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize. @@ -93,40 +106,40 @@ public: virtual void OnShutdown() override; /** - * @brief Connect to a remote system using the connection string to contact the system. Overload of - * sdv::com::IClientConnect::Connect. - * @remarks After a successful connection, the ConnectClient utility is not needed any more. - * @param[in] ssConnectString Optional connection string to use for connection. If not provided, the connection will - * automatically get the connection ID from the app-control service (default). The connection string for a local - * connection can be of the form: - * @code - * [Client] - * Type = "Local" - * Instance = 1234 # Optional: only use when connecting to a system with a different instance ID. - * @endcode - * And the following can be used for a remote connection: - * @code - * [Client] - * Type = "Remote" - * Interface = "127.0.0.1" - * Port = 2000 - * @endcode - * @return Returns an interface to the repository of the remote system or a NULL pointer if not found. + * @brief Connect to a remote system. Overload of IClientConnect::Connect. + * @return Returns whether connect was successful. */ - virtual sdv::IInterfaceAccess* Connect(const sdv::u8string& ssConnectString) override; + virtual bool Connect() override; /** - * @brief Disconnect and remove the remote repository object. - * @param[in] tConnectionID The ID of the connection. + * @brief Disconnect from a connected system. Overload of IClientConnect::Disconnect. + * @return Returns whether disconnect was successful. */ - void Disconnect(sdv::com::TConnectionID tConnectionID); + bool Disconnect() override; + + /** + * @brief State of the current connection. Overload of IClientConnect::IsConnected. + * @return Returns whether an active connection exists. + * @return The connect state. + */ + bool IsConnected() const override; + + /** + * @brief Get the remote repository that is available after connection. Overload of IClientConnect::GetRemoteRepository. + * @remarks For main, isolated and external applications, the remote repository will be automatically linked to the + * local repository. Hence a requests for the repository is not needed. For all other applications, access must be + * explicitly acquired through this interface. + * @return Interface to the remote repository if a successful connection is established. The interface is valid until + * disconnect is called or the client connection service is terminated. + */ + sdv::IInterfaceAccess* GetRemoteRepository() override; private: - std::mutex m_mtxRepositoryProxies; ///< Protect access to the remnote repository map. - std::map m_mapRepositoryProxies; ///< map of remote repositories. + mutable std::mutex m_mtx; ///< Protect against multiple parallel connection activities. + sdv::u8string m_ssProvider; ///< Name of the provider to use for the listening. + std::shared_ptr m_ptrRemoteRepo; ///< Interface to the remote repository. }; -DEFINE_SDV_OBJECT(CClient) - +DEFINE_SDV_OBJECT(CClientConnect) #endif // !defined CLIENT_H \ No newline at end of file diff --git a/sdv_services/ipc_connect/listener.cpp b/sdv_services/ipc_connect/listener.cpp index ba3f582..911b385 100644 --- a/sdv_services/ipc_connect/listener.cpp +++ b/sdv_services/ipc_connect/listener.cpp @@ -16,11 +16,12 @@ #include #include #include +#include CChannelBroker::CChannelBroker(CListener& rListener) : m_rListener(rListener) {} -sdv::u8string CChannelBroker::RequestChannel(/*in*/ const sdv::u8string& /*ssConfig*/) +sdv::u8string CChannelBroker::RequestChannel(/*in*/ const sdv::u8string& ssConfig) { // Get the communication control sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject("CommunicationControl"); @@ -39,26 +40,30 @@ sdv::u8string CChannelBroker::RequestChannel(/*in*/ const sdv::u8string& /*ssCon } // Get the channel control. - sdv::ipc::ICreateEndpoint* pEndpoint = nullptr; - if (m_rListener.IsLocalListener()) - pEndpoint = sdv::core::GetObject("LocalChannelControl"); - else - pEndpoint = sdv::core::GetObject("RemoteChannelControl"); + sdv::ipc::ICreateEndpoint* pEndpoint = sdv::core::GetObject(m_rListener.GetProviderName()); if (!pEndpoint) { SDV_LOG_ERROR("No local channel control!"); return {}; } - // Create the endpoint - sdv::ipc::SChannelEndpoint sEndpoint = pEndpoint->CreateEndpoint(sdv::u8string()); + // Forward protocol-specific configuration to the provider when creating the private channel. + SDV_LOG_INFO("[IPC_CONNECT][Listener] RequestChannel input config:\n", ssConfig); + + sdv::ipc::SChannelEndpoint sEndpoint = pEndpoint->CreateEndpoint(ssConfig); if (!sEndpoint.pConnection) { SDV_LOG_ERROR("Could not create the endpoint for channel request!"); - return sdv::u8string(); + return {}; } + + SDV_LOG_INFO("[IPC_CONNECT][Listener] RequestChannel produced connect string: ", sEndpoint.ssConnectString); + sdv::TObjectPtr ptrEndpoint(sEndpoint.pConnection); // Does automatic destruction if failure happens. + // Restrict access permissions + sdv::core::CAccessPermission permission = sdv::core::RestrictAccessPermission(sdv::core::EAccessPermission::local_access); + // Assign the endpoint to the communication service. sdv::com::TConnectionID tConnection = pConnectionControl->AssignServerEndpoint(ptrEndpoint, ptrRespository, 100, false); ptrEndpoint.Clear(); // Lifetime taken over by communication control. @@ -93,49 +98,21 @@ bool CListener::OnInitialize() return false; } - sdv::ipc::ICreateEndpoint* pEndpoint = nullptr; - std::string ssConfig; - if (m_ssType == "Local") + // Check for a provider. Without provider there is no object to use for listening. + if (m_ssProvider.empty()) { - m_bLocalListener = true; - pEndpoint = sdv::core::GetObject("LocalChannelControl"); - if (!pEndpoint) - { - SDV_LOG_ERROR("No local channel control!"); - return false; - } - - // Request the instance ID from the app control - ssConfig = std::string(R"code([IpcChannel] -Name = "LISTENER_)code") + std::to_string(m_uiInstanceID ? m_uiInstanceID : pContext->GetInstanceID()) + R"code(" -Size = 2048 -)code"; - } - else if (m_ssType == "Remote") - { - m_bLocalListener = false; - if (m_ssInterface.empty() || !m_uiPort) - { - SDV_LOG_ERROR("Missing interface or port number to initialize a remote listener!"); - return false; - } - pEndpoint = sdv::core::GetObject("RemoteChannelControl"); - if (!pEndpoint) - { - SDV_LOG_ERROR("No remote channel control!"); - return false; - } - - ssConfig = R"code([IpcChannel] -Interface = ")code" + m_ssInterface + R"code( -Port = ")code" + std::to_string(m_uiPort) + R"code( -)code"; - } - else - { - SDV_LOG_ERROR("Invalid or missing listener configuration for listener service!"); + SDV_LOG_ERROR("Missing provider name for creating a listener object!"); return false; } + sdv::ipc::ICreateEndpoint* pEndpoint = sdv::core::GetObject(m_ssProvider); + if (!pEndpoint) + { + SDV_LOG_ERROR("Cannot instantiate provider '", m_ssProvider, "' for the creation of a listener object!"); + return false; + } + + // Get the IpcChannel information from the the configuration + auto ssConfig = BuildObjectConfig(); // Create the endpoint sdv::ipc::SChannelEndpoint sEndpoint = pEndpoint->CreateEndpoint(ssConfig); @@ -146,6 +123,24 @@ Port = ")code" + std::to_string(m_uiPort) + R"code( } sdv::TObjectPtr ptrEndpoint(sEndpoint.pConnection); // Does automatic destruction if failure happens. + // Get the repository + sdv::TInterfaceAccessPtr ptrRespository = sdv::core::GetObject("RepositoryService"); + if (!ptrRespository) + { + SDV_LOG_ERROR("Failed to get repository service!"); + return false; + } + + // Add a dependency of the provider to this service (to prevent the provider to be terminated while the service is still + // running). + sdv::core::IObjectDependency* pObjectDependency = ptrRespository.GetInterface(); + if (!pObjectDependency) + { + SDV_LOG_ERROR("Failed to get the object dependency interface!"); + return false; + } + pObjectDependency->AddObjectDependency(Self().ssName, m_ssProvider); + // Assign the endpoint to the communication service. m_tConnection = pConnectionControl->AssignServerEndpoint(ptrEndpoint, &m_broker, 100, true); ptrEndpoint.Clear(); // Lifetime taken over by communication control. @@ -173,9 +168,8 @@ void CListener::OnShutdown() m_ptrConnection.Clear(); } -bool CListener::IsLocalListener() const + +const sdv::u8string& CListener::GetProviderName() { - return m_bLocalListener; + return m_ssProvider; } - - diff --git a/sdv_services/ipc_connect/listener.h b/sdv_services/ipc_connect/listener.h index 1e36b7a..38df1ee 100644 --- a/sdv_services/ipc_connect/listener.h +++ b/sdv_services/ipc_connect/listener.h @@ -52,6 +52,25 @@ private: /** * @brief Listener object + * @details the lister is instantiated using the following parameter information + * @code + * # Provider to use for listening + * [Provider] + * Name = "" + * + * # Additional channel information for the listener (needed for unique listener identification) + * [IpcChannel] + * xyz = "" + * @endcode + * + * For example for shared memory: + * @code + * [Provider] + * Name = "DefaultSharedMemory" + * [IpcChannel] + * Name = "LISTENER_1234" + * Size = 10240 + * @endcode */ class CListener : public sdv::CSdvObject { @@ -63,34 +82,18 @@ public: // Object declaration DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) - DECLARE_OBJECT_CLASS_NAME("ConnectionListenerService") + DECLARE_OBJECT_CLASS_NAME("ListenerConnectService") + DECLARE_OBJECT_DEPENDENCIES("CommunicationControl") // Parameter map BEGIN_SDV_PARAM_MAP() SDV_PARAM_ENABLE_LOCKING() - SDV_PARAM_GROUP("Listener") - SDV_PARAM_ENTRY(m_ssType, "Type", "Local", "", "The type of listener \"Local\" or \"Remote\".") - SDV_PARAM_ENTRY(m_uiInstanceID, "Instance", 0, "", "The instance ID to listen for.") - SDV_PARAM_ENTRY(m_ssInterface, "Interface", "", "", "Interface identification.") - SDV_PARAM_ENTRY(m_uiPort, "Port", 0, "", "Port number for connection.") + SDV_PARAM_GROUP("Provider") + SDV_PARAM_ENTRY(m_ssProvider, "Name", "", "", "Provider name to create a listener for.") END_SDV_PARAM_MAP() /** * @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize. - * @details The object configuration contains the information needed to start the listener. The following configuration is - * available for the local listener: - * @code - * [Listener] - * Type = "Local" - * Instance = 1000 # Normally not used; system instance ID is used automatically. - * @endcode - * And the following is available for a remote listener: - * @code - * [Listener] - * Type = "Remote" - * Interface = "127.0.0.1" - * Port = 2000 - * @endcode * @return Returns 'true' when the initialization was successful, 'false' when not. */ virtual bool OnInitialize() override; @@ -101,19 +104,15 @@ public: virtual void OnShutdown() override; /** - * @brief When set, the listener is configured to be a local listener. Otherwise the listerner is configured as remote listener. - * @return Boolean set when local lostener. + * @brief Get the provider name used by this listener. + * @return Reference to the provider name. */ - bool IsLocalListener() const; + const sdv::u8string& GetProviderName(); private: - sdv::u8string m_ssType; ///< Listener type: "Local" or "Remote" - uint32_t m_uiInstanceID = 0; ///< Instance ID to listen for. - std::string m_ssInterface; ///< Interface string for remote listener. - uint32_t m_uiPort = 0; ///< Port for remote listener. + sdv::u8string m_ssProvider; ///< Name of the provider to use for the listening. sdv::TObjectPtr m_ptrConnection; ///< The connection object. CChannelBroker m_broker; ///< Channel broker, used to request new channels - bool m_bLocalListener = true; ///< When set, the listener is a local listener; otherwise a remote listener. sdv::com::TConnectionID m_tConnection = {}; ///< Channel connection ID. }; diff --git a/sdv_services/ipc_shared_mem/channel_mgnt.cpp b/sdv_services/ipc_shared_mem/channel_mgnt.cpp index 9506e10..fffe76c 100644 --- a/sdv_services/ipc_shared_mem/channel_mgnt.cpp +++ b/sdv_services/ipc_shared_mem/channel_mgnt.cpp @@ -56,15 +56,15 @@ sdv::IInterfaceAccess* CSharedMemChannelMgnt::Access(const sdv::u8string& ssConn sdv::toml::CTOMLParser parser(ssConnectString); if (!parser.IsValid()) return nullptr; - // Is this a configuration provided by the endpoint (uses a "Provider" key), then this is a connection string. Use this + // Is this a configuration provided by the endpoint (uses a "ConnectParam" key), then this is a connection string. Use this // to connect to the shared memory. std::shared_ptr ptrConnection; - if (parser.GetDirect("Provider").IsValid()) + if (parser.GetDirect("ConnectParam").IsValid()) ptrConnection = std::make_shared(m_watchdog, ssConnectString.c_str()); else { std::string ssName = static_cast(parser.GetDirect("IpcChannel.Name").GetValue()); - ptrConnection = std::make_shared(m_watchdog, 0,ssName, false); + ptrConnection = std::make_shared(m_watchdog, 0, ssName, false); } if (!ptrConnection) return {}; m_watchdog.AddConnection(ptrConnection); diff --git a/sdv_services/ipc_shared_mem/channel_mgnt.h b/sdv_services/ipc_shared_mem/channel_mgnt.h index 6784152..2c9f2d1 100644 --- a/sdv_services/ipc_shared_mem/channel_mgnt.h +++ b/sdv_services/ipc_shared_mem/channel_mgnt.h @@ -43,9 +43,7 @@ public: // Object declarations DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) - DECLARE_OBJECT_CLASS_NAME("DefaultSharedMemoryChannelControl") - DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl") - DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl") + DECLARE_OBJECT_CLASS_NAME("DefaultSharedMemory") DECLARE_OBJECT_SINGLETON() /** diff --git a/sdv_services/ipc_shared_mem/connection.cpp b/sdv_services/ipc_shared_mem/connection.cpp index 9b7afc9..0b0b334 100644 --- a/sdv_services/ipc_shared_mem/connection.cpp +++ b/sdv_services/ipc_shared_mem/connection.cpp @@ -117,7 +117,7 @@ std::string CConnection::GetConnectionString() { // The connection string is of the form: // [Provider] - // Name = "DefaultSharedMemoryChannelControl" + // Name = "DefaultSharedMemory" // // [[ConnectParam]] // Type = "shared_mem" @@ -132,9 +132,9 @@ std::string CConnection::GetConnectionString() // SyncTx = "SDV_TX_SYNC_REQUEST_1234" // SyncRx = "SDV_RX_SYNC_REQUEST_1234" // Direction = "request" - std::string ssConnectionString = R"code([Provider] -Name = "LocalChannelControl" -)code" + std::string("\n") + m_sender.GetConnectionString() + "\n" + m_receiver.GetConnectionString(); + std::string ssConnectionString = R"toml([Provider] +Name = "DefaultSharedMemory" +)toml" + std::string("\n") + m_sender.GetConnectionString() + "\n" + m_receiver.GetConnectionString(); return ssConnectionString; } @@ -352,7 +352,7 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) std::unique_lock lock(m_mtxConnect); // Allowed to connect? - if (m_eConnectState != sdv::ipc::EConnectState::uninitialized) + if (m_eConnectState != sdv::ipc::EConnectState::uninitialized && m_eConnectState != sdv::ipc::EConnectState::disconnected) { for (auto& rprEventCallback : m_lstEventCallbacks) if (rprEventCallback.pCallback && rprEventCallback.uiCookie) @@ -377,9 +377,9 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) SetConnectState(sdv::ipc::EConnectState::initialized); // Start the receiving thread (wait until started). - m_threadReceive = std::thread(&CConnection::ReceiveMessages, this); + m_threadReceive = sdv::core::secure_thread(&CConnection::ReceiveMessages, this); #if ENABLE_DECOUPLING > 0 - m_threadDecoupleReceive = std::thread(&CConnection::DecoupleReceive, this); + m_threadDecoupleReceive = sdv::core::secure_thread(&CConnection::DecoupleReceive, this); #endif if (!m_bStarted) m_cvStartConnect.wait_for(lock, std::chrono::milliseconds(1000)); @@ -602,9 +602,10 @@ void CConnection::ReceiveMessages() auto optPacket = m_receiver.TryRead(); if (!optPacket) { + // TODO EVE: Also allow synchronization take place when disconnected. // Start communication, but only if connection is client based. Server based should not start the communication. If // there is no client, the server would otherwise fill its send-buffer. Repeat sending every 500ms. - if (!m_bServer && (/*m_eConnectState == sdv::ipc::EConnectState::disconnected ||*/ m_eConnectState == sdv::ipc::EConnectState::initialized)) + if (!m_bServer && (m_eConnectState == sdv::ipc::EConnectState::disconnected || m_eConnectState == sdv::ipc::EConnectState::initialized)) { // Send request auto tpNow = std::chrono::high_resolution_clock::now(); @@ -1001,9 +1002,10 @@ void CConnection::ReceiveConnectTerm(CMessage& /*rMessage*/) m_sender.CancelSend(); m_sender.ResetRx(); - // Send sync request (do not wait until next round in case a very short connection <100ms took place). - if (m_bServer) - Send(SMsgHdr{ SDVFrameworkInterfaceVersion, EMsgType::sync_request }); + // TODO EVE: Disabled, since state of client straight goes into "connecting" after being disconnected. + //// Send sync request (do not wait until next round in case a very short connection <100ms took place). + //if (m_bServer) + // Send(SMsgHdr{ SDVFrameworkInterfaceVersion, EMsgType::sync_request }); } void CConnection::ReceiveDataMessage(CMessage& rMessage, SDataContext& rsDataCtxt) diff --git a/sdv_services/ipc_shared_mem/connection.h b/sdv_services/ipc_shared_mem/connection.h index 037dc7e..88eac04 100644 --- a/sdv_services/ipc_shared_mem/connection.h +++ b/sdv_services/ipc_shared_mem/connection.h @@ -257,7 +257,7 @@ private: sdv::CLifetimeCookie m_cookie = sdv::CreateLifetimeCookie(); ///< Lifetime cookie to manage module lifetime. CSharedMemBufferTx m_sender; ///< Shared buffer for sending. CSharedMemBufferRx m_receiver; ///< Shared buffer for receiving. - std::thread m_threadReceive; ///< Thread which receives data from the socket. + sdv::core::secure_thread m_threadReceive; ///< Thread which receives data from the socket. std::atomic m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< the state of the connection sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages to if available std::shared_mutex m_mtxEventCallbacks; ///< Protect access to callback list. Only locking when @@ -275,7 +275,7 @@ private: #if ENABLE_DECOUPLING > 0 std::mutex m_mtxReceive; ///< Protect receive queue. std::queue>> m_queueReceive; ///< Receive queue to decouple receiving and processing. - std::thread m_threadDecoupleReceive; ///< Decoupled receive thread. + sdv::core::secure_thread m_threadDecoupleReceive; ///< Decoupled receive thread. std::condition_variable m_cvReceiveAvailable; ///< Condition variable synchronizing the processing. std::condition_variable m_cvReceiveProcessed; ///< Condition variable synchronizing the processing. #endif diff --git a/sdv_services/ipc_shared_mem/watchdog.cpp b/sdv_services/ipc_shared_mem/watchdog.cpp index b0ba781..9bc2354 100644 --- a/sdv_services/ipc_shared_mem/watchdog.cpp +++ b/sdv_services/ipc_shared_mem/watchdog.cpp @@ -22,7 +22,7 @@ CWatchDog::CWatchDog() { - m_threadScheduledConnectionDestructions = std::thread(&CWatchDog::HandleScheduledConnectionDestructions, this); + m_threadScheduledConnectionDestructions = sdv::core::secure_thread(&CWatchDog::HandleScheduledConnectionDestructions, this); } CWatchDog::~CWatchDog() diff --git a/sdv_services/ipc_shared_mem/watchdog.h b/sdv_services/ipc_shared_mem/watchdog.h index 73f1af0..90668e3 100644 --- a/sdv_services/ipc_shared_mem/watchdog.h +++ b/sdv_services/ipc_shared_mem/watchdog.h @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -127,7 +128,7 @@ private: std::condition_variable m_cvTriggerConnectionDestruction; ///< Condition variable used to trigger when a ///< connection is scheduled for destruction. std::queue> m_queueScheduledConnectionDestructions; ///< Scheduled connection for destruction. - std::thread m_threadScheduledConnectionDestructions; ///< Thread processing the scheduled destructions. + sdv::core::secure_thread m_threadScheduledConnectionDestructions; ///< Thread processing the scheduled destructions. std::atomic_bool m_bShutdown = false; ///< Set when shutting down the watchdog }; diff --git a/sdv_services/process_control/process_control.cpp b/sdv_services/process_control/process_control.cpp index 5ba14f0..cb04bbc 100644 --- a/sdv_services/process_control/process_control.cpp +++ b/sdv_services/process_control/process_control.cpp @@ -59,7 +59,7 @@ CProcessControl::~CProcessControl() bool CProcessControl::OnInitialize() { // Without monitor no trigger... - m_threadMonitor = std::thread(&CProcessControl::MonitorThread, this); + m_threadMonitor = sdv::core::secure_thread(&CProcessControl::MonitorThread, this); return true; } @@ -76,6 +76,7 @@ void CProcessControl::OnShutdown() bool CProcessControl::AllowProcessControl() const { + if (m_bEnableBypass) return true; const sdv::app::IAppContext* pAppContext = sdv::core::GetCore(); return pAppContext && (pAppContext->GetContextType() == sdv::app::EAppContext::main || pAppContext->GetContextType() == sdv::app::EAppContext::maintenance || @@ -574,3 +575,7 @@ void CProcessControl::MonitorThread() } } +void CProcessControl::EnableProcessControlAccessBypass() +{ + m_bEnableBypass = true; +} diff --git a/sdv_services/process_control/process_control.h b/sdv_services/process_control/process_control.h index 1d1334a..e3dcf96 100644 --- a/sdv_services/process_control/process_control.h +++ b/sdv_services/process_control/process_control.h @@ -118,7 +118,12 @@ public: */ virtual bool Terminate(/*in*/ sdv::process::TProcessID tProcessID) override; - private: + /** + * @brief Enable process control access (explicitly bypass application check for testing). + */ + void EnableProcessControlAccessBypass(); + +private: /** * @brief Monitor thread function. */ @@ -151,12 +156,13 @@ public: #else #error OS is not supported! #endif - mutable std::mutex m_mtxProcesses; ///< Access control for monitor map. + mutable std::mutex m_mtxProcesses; ///< Access control for monitor map. std::map> m_mapProcesses; ///< Monitor map - uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie + uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie std::map> m_mapMonitors; ///< Map with monitors. - std::atomic_bool m_bShutdown = false; ///< Set to shutdown the monitor thread. - std::thread m_threadMonitor; ///< Monitor thread. + std::atomic_bool m_bShutdown = false; ///< Set to shutdown the monitor thread. + sdv::core::secure_thread m_threadMonitor; ///< Monitor thread. + bool m_bEnableBypass = false; ///< When set, enables process control regardless of application mode. }; DEFINE_SDV_OBJECT(CProcessControl) diff --git a/sdv_services/task_timer/tasktimer.cpp b/sdv_services/task_timer/tasktimer.cpp index 8f1bf41..24f80c0 100644 --- a/sdv_services/task_timer/tasktimer.cpp +++ b/sdv_services/task_timer/tasktimer.cpp @@ -16,7 +16,7 @@ #include CTimer::CTimer(CTaskTimerService& rtimersvc, uint32_t uiPeriod, sdv::core::ITaskExecute* pExecute) : - m_rtimersvc(rtimersvc), m_pExecute(pExecute) + m_rtimersvc(rtimersvc), m_pExecute(pExecute), m_tPermissionTransferID(sdv::core::TransferCurrentPermission()) { if (!pExecute) return; @@ -35,6 +35,11 @@ CTimer::CTimer(CTaskTimerService& rtimersvc, uint32_t uiPeriod, sdv::core::ITask sev.sigev_notify_function = [](sigval sv) { CTimer* pTimer = reinterpret_cast(sv.sival_ptr); + if (pTimer->m_tPermissionTransferID) + { + sdv::core::SetAccessPermission(pTimer->m_tPermissionTransferID); + pTimer->m_tPermissionTransferID = 0u; + } std::unique_lock lock(pTimer->m_mtxExecution); if (!pTimer->m_bRunning) return; sdv::core::ITaskExecute* pExecuteLocal = reinterpret_cast(pTimer->m_pExecute); @@ -111,6 +116,11 @@ void CTimer::ExecuteCallback() { if (m_rtimersvc.GetObjectState() != sdv::EObjectState::running) return; if (!m_pExecute) return; + if (m_tPermissionTransferID) + { + sdv::core::SetAccessPermission(m_tPermissionTransferID); + m_tPermissionTransferID = 0u; + } if (!m_bPrioritySet) SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); m_bPrioritySet = true; diff --git a/sdv_services/task_timer/tasktimer.h b/sdv_services/task_timer/tasktimer.h index f6c4619..4a445c2 100644 --- a/sdv_services/task_timer/tasktimer.h +++ b/sdv_services/task_timer/tasktimer.h @@ -102,6 +102,8 @@ private: std::atomic_bool m_bRunning = false; ///< When set, the timer is running. std::mutex m_mtxExecution; ///< Prevent killing the timer when in execution. #endif + sdv::core::TPermissionTransferID m_tPermissionTransferID = 0u; ///< The transfer ID to transfer permissions between the + ///< timer creator and the execution thread. }; /** diff --git a/sdv_services/uds_unix_sockets/CMakeLists.txt b/sdv_services/uds_unix_sockets/CMakeLists.txt index 290f3c1..2fbfbe0 100644 --- a/sdv_services/uds_unix_sockets/CMakeLists.txt +++ b/sdv_services/uds_unix_sockets/CMakeLists.txt @@ -12,20 +12,55 @@ if(UNIX) # Define project project(uds_unix_sockets VERSION 1.0 LANGUAGES CXX) -# Define target -add_library(uds_unix_sockets STATIC +# Build sources once and expose them in two forms: +# - uds_unix_sockets: runtime-loadable .sdv module for ModuleControl +# - uds_unix_sockets_static: static link target for unit tests / static consumers +set(UDS_UNIX_SOCKETS_SOURCES channel_mgnt.cpp connection.cpp - ) + watchdog.cpp +) -target_link_libraries(uds_unix_sockets rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) +add_library(uds_unix_sockets SHARED + ${UDS_UNIX_SOCKETS_SOURCES} +) + +add_library(uds_unix_sockets_static STATIC + ${UDS_UNIX_SOCKETS_SOURCES} +) + +target_include_directories(uds_unix_sockets + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ./include/ +) + +target_include_directories(uds_unix_sockets_static + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ./include/ +) + +target_link_libraries(uds_unix_sockets + PUBLIC + rt + ${CMAKE_DL_LIBS} + ${CMAKE_THREAD_LIBS_INIT} +) + +target_link_libraries(uds_unix_sockets_static + PUBLIC + rt + ${CMAKE_DL_LIBS} + ${CMAKE_THREAD_LIBS_INIT} +) -target_include_directories(uds_unix_sockets PRIVATE ./include/) set_target_properties(uds_unix_sockets PROPERTIES PREFIX "") set_target_properties(uds_unix_sockets PROPERTIES SUFFIX ".sdv") # Build dependencies add_dependencies(uds_unix_sockets CompileCoreIDL) +add_dependencies(uds_unix_sockets_static CompileCoreIDL) # Appending the service in the service list set(SDV_Service_List ${SDV_Service_List} uds_unix_sockets PARENT_SCOPE) diff --git a/sdv_services/uds_unix_sockets/channel_mgnt.cpp b/sdv_services/uds_unix_sockets/channel_mgnt.cpp index a8ba4b2..a97284a 100644 --- a/sdv_services/uds_unix_sockets/channel_mgnt.cpp +++ b/sdv_services/uds_unix_sockets/channel_mgnt.cpp @@ -81,19 +81,19 @@ namespace // Directory selection (/run/user//sdv or /tmp/sdv) std::string CUnixDomainSocketsChannelMgnt::MakeUserRuntimeDir() { - std::ostringstream oss; - oss << "/run/user/" << ::getuid(); + const std::string path = "/run/ipc/sdv"; struct stat st{}; - if (::stat(oss.str().c_str(), &st) == 0) + if (::stat(path.c_str(), &st) == 0) { - std::string path = oss.str() + "/sdv"; - ::mkdir(path.c_str(), 0770); return path; } - ::mkdir("/tmp/sdv", 0770); - return "/tmp/sdv"; + // fallback if /run/ipc/sdv is not available + const std::string fallback = "/tmp/sdv"; + ::mkdir(fallback.c_str(), 0770); + + return fallback; } bool CUnixDomainSocketsChannelMgnt::OnInitialize() @@ -102,7 +102,11 @@ bool CUnixDomainSocketsChannelMgnt::OnInitialize() } void CUnixDomainSocketsChannelMgnt::OnShutdown() +{} + +void CUnixDomainSocketsChannelMgnt::OnDestroy() { + m_watchdog.Clear(); } // Endpoint creation (server) @@ -129,33 +133,133 @@ sdv::ipc::SChannelEndpoint CUnixDomainSocketsChannelMgnt::CreateEndpoint(const s path = ClampSunPath(path); - // Use a shared_ptr and store it to keep the server connection alive - auto server = std::make_shared(-1, true, path); - m_ServerConnections.push_back(server); + // Keep lifetime consistent with shared_mem: watchdog owns active connections. + std::shared_ptr server = std::make_shared(-1, true, path); + // Ignore cppcheck warning; if construction failed, an exception is expected first. + // cppcheck-suppress knownConditionTrueFalse + if (!server) + return {}; + server->SetWatchDogRemoveCallback([this](const void* connection) + { + m_watchdog.RemoveConnection(connection); + }); + m_watchdog.AddConnection(server); sdv::ipc::SChannelEndpoint ep{}; ep.pConnection = static_cast(server.get()); - ep.ssConnectString = server->GetConnectionString(); + + // Publish a Provider-wrapped connect string for compatibility with + // CCommunicationControl::CreateClientConnection() flows. + const std::string udsConnectString = server->GetConnectionString(); + ep.ssConnectString = std::string("[Provider]\n") + + "Name = \"unix_domain_sockets\"\n" + + "ConnectString = \"" + udsConnectString + "\"\n"; return ep; } // Access existing endpoint (server or client) sdv::IInterfaceAccess* CUnixDomainSocketsChannelMgnt::Access(const sdv::u8string& ssConnectString) { - const auto kv = ParseKV(static_cast(ssConnectString)); - const bool isServer = (kv.count("role") && kv.at("role") == "server"); - const std::string path = kv.count("path") ? kv.at("path") : (MakeUserRuntimeDir() + "/UDS_auto.sock"); + const std::string input = static_cast(ssConnectString); - if (isServer) + bool isServer = false; + bool parsed = false; + std::string path; + + // Parse structured TOML forms first, but only for non-raw inputs. + // Raw connect strings (proto=uds;...) are intentionally not TOML and + // would produce noisy parser diagnostics like "Missing value". + if (input.rfind("proto=uds", 0) != 0) { - auto server = std::make_shared(-1, true, path); - m_ServerConnections.push_back(server); - return static_cast(server.get()); + sdv::toml::CTOMLParser parser(input); + if (!parser.IsValid()) + return nullptr; + + const std::string providerName = parser.GetDirect("Provider.Name").GetValue(); + if (!providerName.empty()) + { + if (providerName != "unix_domain_sockets" && + providerName != "UnixSocketsChannelControl" && + providerName != "UnixDomainSocketsChannelControl") + { + return nullptr; + } + + const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue(); + if (!nested.empty()) + { + const auto kv = ParseKV(nested); + if (kv.count("path")) + path = kv.at("path"); + } + + // Provider descriptions are consumed as client endpoints. + parsed = true; + isServer = false; + } + else + { + // Shared-memory style callers pass [IpcChannel] config directly to Access(). + parsed = true; + isServer = false; + } + + if (path.empty()) + { + auto pathNode = parser.GetDirect("IpcChannel.Path"); + if (pathNode.GetType() == sdv::toml::ENodeType::node_string) + { + path = static_cast(pathNode.GetValue()); + } + else + { + auto nameNode = parser.GetDirect("IpcChannel.Name"); + if (nameNode.GetType() == sdv::toml::ENodeType::node_string) + { + const std::string name = static_cast(nameNode.GetValue()); + if (!name.empty()) + path = MakeUserRuntimeDir() + "/" + name + ".sock"; + } + } + } } - // Client: allocated raw pointer (expected to be managed by SDV framework) - auto* client = new CUnixSocketConnection(-1, false, path); - return static_cast(client); + // Raw KV fallback (legacy/tests): proto=uds;role=...;path=...; + if (!parsed) + { + // Only treat as raw format when it actually starts with proto=uds. + if (input.rfind("proto=uds", 0) != 0) + return nullptr; + + const auto kv = ParseKV(input); + parsed = true; + isServer = (kv.count("role") && kv.at("role") == "server"); + if (kv.count("path")) + path = kv.at("path"); + } + + if (!parsed) + return nullptr; + + if (path.empty()) + path = MakeUserRuntimeDir() + "/UDS_auto.sock"; + + path = ClampSunPath(path); + + std::shared_ptr connection = + std::make_shared(-1, isServer, path); + // Ignore cppcheck warning; if construction failed, an exception is expected first. + // cppcheck-suppress knownConditionTrueFalse + if (!connection) + return nullptr; + + connection->SetWatchDogRemoveCallback([this](const void* instance) + { + m_watchdog.RemoveConnection(instance); + }); + + m_watchdog.AddConnection(connection); + return static_cast(connection.get()); } #endif // defined(__unix__) \ No newline at end of file diff --git a/sdv_services/uds_unix_sockets/channel_mgnt.h b/sdv_services/uds_unix_sockets/channel_mgnt.h index 258966e..7f4bfe7 100644 --- a/sdv_services/uds_unix_sockets/channel_mgnt.h +++ b/sdv_services/uds_unix_sockets/channel_mgnt.h @@ -17,6 +17,8 @@ #include #include +#include "watchdog.h" +#include class CUnixSocketConnection; @@ -37,9 +39,9 @@ public: // Object declarations DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) - DECLARE_OBJECT_CLASS_NAME("UnixDomainSocketsChannelControl") - DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl") - DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl") + DECLARE_OBJECT_CLASS_NAME("UnixSocketsChannelControl") + DECLARE_OBJECT_CLASS_ALIAS("unix_domain_sockets") + DECLARE_DEFAULT_OBJECT_NAME("unix_domain_sockets") DECLARE_OBJECT_SINGLETON() /** @@ -53,6 +55,11 @@ public: */ virtual void OnShutdown() override; + /** + * @brief Last function called before destruction. Overload of sdv::CSdvObject::OnDestroy. + */ + virtual void OnDestroy() override; + /** * @brief Create IPC connection object and return the endpoint information. Overload of * sdv::ipc::ICreateEndpoint::CreateEndpoint. @@ -80,8 +87,7 @@ private: // Helper: choose runtime dir (/run/user//sdv) or fallback (/tmp/sdv) static std::string MakeUserRuntimeDir(); - - std::vector> m_ServerConnections; + CUnixSocketsConnectionWatchDog m_watchdog; }; DEFINE_SDV_OBJECT(CUnixDomainSocketsChannelMgnt) diff --git a/sdv_services/uds_unix_sockets/connection.cpp b/sdv_services/uds_unix_sockets/connection.cpp index c28160f..9d46f64 100644 --- a/sdv_services/uds_unix_sockets/connection.cpp +++ b/sdv_services/uds_unix_sockets/connection.cpp @@ -28,6 +28,8 @@ #include #include +#include + // Anonymous namespace: local helpers (not exported) namespace { @@ -93,6 +95,8 @@ namespace { return static_cast(::getpid()); } + + } // namespace // Construction / Destruction @@ -124,6 +128,12 @@ CUnixSocketConnection::~CUnixSocketConnection() } } +void CUnixSocketConnection::SetWatchDogRemoveCallback(std::function callback) +{ + std::lock_guard lock(m_WatchdogMtx); + m_WatchdogRemoveCallback = std::move(callback); +} + // Public API std::string CUnixSocketConnection::GetConnectionString() { @@ -137,27 +147,81 @@ std::string CUnixSocketConnection::GetConnectionString() bool CUnixSocketConnection::SendSizedPacket(const void* pData, uint32_t uiDataLength) { - if (m_Fd < 0) return false; + if (m_Fd < 0) + return false; + +#ifdef MSG_NOSIGNAL + constexpr int kSendFlags = MSG_NOSIGNAL; +#else + constexpr int kSendFlags = 0; +#endif + + auto sendAll = [&](const void* data, uint32_t length) -> int + { + const char* ptr = reinterpret_cast(data); + uint32_t remaining = length; + + while (remaining > 0) + { + const ssize_t sent = ::send(m_Fd, ptr, remaining, kSendFlags); + + if (sent < 0) + { + if (errno == EINTR) + continue; + + return errno; + } + + if (sent == 0) + { + return EPIPE; + } + + ptr += sent; + remaining -= static_cast(sent); + } + + return 0; + }; const uint32_t len = uiDataLength; - std::lock_guard lock(m_SendMtx); + int sendErr = 0; - // Transport header: packet size - if (::send(m_Fd, &len, sizeof(len), 0) != static_cast(sizeof(len))) - return false; - - // SDV payload - const char* ptr = reinterpret_cast(pData); - uint32_t remain = uiDataLength; - - while (remain > 0) { - const ssize_t sent = ::send(m_Fd, ptr, remain, 0); - if (sent <= 0) return false; - ptr += sent; - remain -= static_cast(sent); + std::lock_guard lock(m_SendMtx); + + // Transport header: packet size + sendErr = sendAll(&len, static_cast(sizeof(len))); + + // SDV payload + if (sendErr == 0 && uiDataLength > 0) + { + sendErr = sendAll(pData, uiDataLength); + } } + + if (sendErr != 0) + { + if (sendErr == EPIPE || sendErr == ECONNRESET || sendErr == ENOTCONN) + { + SDV_LOG_WARNING("[UDS][TX] Peer closed connection during send, errno=", + sendErr, " (", std::strerror(sendErr), ")"); + + SetConnectState(sdv::ipc::EConnectState::disconnected); + } + else + { + SDV_LOG_WARNING("[UDS][TX] send() failed, errno=", + sendErr, " (", std::strerror(sendErr), ")"); + + SetConnectState(sdv::ipc::EConnectState::communication_error); + } + + return false; + } + return true; } @@ -233,9 +297,10 @@ bool CUnixSocketConnection::SendData(sdv::sequence>& seqDa // Header { - auto* hdr = reinterpret_cast(frame.data()); - hdr->uiVersion = SDVFrameworkInterfaceVersion; - hdr->eType = EMsgType::data; + SMsgHdr hdr{}; + hdr.uiVersion = SDVFrameworkInterfaceVersion; + hdr.eType = EMsgType::data; + std::memcpy(frame.data(), &hdr, sizeof(SMsgHdr)); msgOff = static_cast(sizeof(SMsgHdr)); } @@ -284,11 +349,12 @@ bool CUnixSocketConnection::SendData(sdv::sequence>& seqDa // Fragment header { - auto* hdr = reinterpret_cast(frame.data()); - hdr->uiVersion = SDVFrameworkInterfaceVersion; - hdr->eType = EMsgType::data_fragment; - hdr->uiTotalLength= static_cast(required); - hdr->uiOffset = offset; + SFragmentedMsgHdr hdr{}; + hdr.uiVersion = SDVFrameworkInterfaceVersion; + hdr.eType = EMsgType::data_fragment; + hdr.uiTotalLength = static_cast(required); + hdr.uiOffset = offset; + std::memcpy(frame.data(), &hdr, sizeof(SFragmentedMsgHdr)); msgOff = static_cast(sizeof(SFragmentedMsgHdr)); } @@ -330,25 +396,37 @@ bool CUnixSocketConnection::SendData(sdv::sequence>& seqDa uint64_t CUnixSocketConnection::RegisterStateEventCallback(sdv::IInterfaceAccess* pEventCallback) { - if (!pEventCallback) return 0; + if (!pEventCallback) + return 0; - auto* pCb = sdv::TInterfaceAccessPtr(pEventCallback).GetInterface(); - if (!pCb) return 0; + // use SDV interface pointer for safe casting and lifetime management + sdv::TInterfaceAccessPtr ptr(pEventCallback); - // Generate a non-zero cookie - uint64_t cookie = 0; - do - { - cookie = static_cast(::rand()); - } while (cookie == 0); - - // Write-lock: add entry + auto* pCallback = ptr.GetInterface(); + if (!pCallback) { - std::unique_lock lock(m_mtxEventCallbacks); - m_lstEventCallbacks.emplace_front(SEventCallback{ cookie, pCb }); + SDV_LOG_WARNING("[UDS] RegisterStateEventCallback: invalid callback interface"); + return 0; } - return cookie; + // check valid cookie generation (avoid 0) + uint64_t uiCookie = 0; + while (uiCookie == 0) + { + uiCookie = static_cast(rand()); + } + + // protectiong shared list with write lock + { + std::unique_lock lock(m_mtxEventCallbacks); + + m_lstEventCallbacks.emplace_front(SEventCallback{ + uiCookie, + pCallback + }); + } + + return uiCookie; } void CUnixSocketConnection::UnregisterStateEventCallback(uint64_t uiCookie) @@ -381,12 +459,25 @@ void CUnixSocketConnection::UnregisterStateEventCallback(uint64_t uiCookie) bool CUnixSocketConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) { + const auto currentState = m_eConnectState.load(std::memory_order_acquire); + if (currentState == sdv::ipc::EConnectState::connected) + { + return true; + } + + if (currentState == sdv::ipc::EConnectState::initializing && m_ConnectThread.joinable()) + { + SDV_LOG_WARNING("[UDS] AsyncConnect ignored: connect worker already running"); + return false; + } + // Capture callbacks under lock { std::lock_guard lk(m_StateMtx); m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); m_pEvent = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); m_eConnectState = sdv::ipc::EConnectState::initializing; + m_bConnectedOnce.store(false, std::memory_order_release); // Reset stop flags m_StopReceiveThread.store(false); @@ -394,11 +485,12 @@ bool CUnixSocketConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) } m_StateCv.notify_all(); - // If an old worker exists, join it to avoid duplicates + // If an old worker exists, join it to avoid duplicates. + // Joining an in-progress worker here can deadlock when a previous connect attempt is still pending. if (m_ConnectThread.joinable()) m_ConnectThread.join(); // Start the unique connect worker (server/client) - m_ConnectThread = std::thread(&CUnixSocketConnection::ConnectWorker, this); + m_ConnectThread = sdv::core::secure_thread(&CUnixSocketConnection::ConnectWorker, this); return true; } @@ -448,35 +540,100 @@ int CUnixSocketConnection::AcceptConnection() // deprecated return clientFd; } +void CUnixSocketConnection::CloseClientSocketOnly() +{ + const int fd = m_Fd; + m_Fd = -1; + + if (fd >= 0) + { + ::shutdown(fd, SHUT_RDWR); + ::close(fd); + } +} + + +void CUnixSocketConnection::WaitForClientEnd() +{ + std::unique_lock lock(m_MtxConnect); + + m_cvConnect.wait(lock, [this] + { + const auto state = m_eConnectState.load(std::memory_order_acquire); + + return m_StopConnectThread.load(std::memory_order_acquire) || + state == sdv::ipc::EConnectState::disconnected || + state == sdv::ipc::EConnectState::disconnected_forced || + state == sdv::ipc::EConnectState::communication_error || + state == sdv::ipc::EConnectState::connection_error; + }); + +} + bool CUnixSocketConnection::WaitForConnection(uint32_t uiWaitMs) { - if (m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected) return true; +#if ENABLE_REPORTING >= 1 + if (m_eConnectState == sdv::ipc::EConnectState::connected) + TRACE("Not waiting for a connection - already connected"); + else + TRACE("Waiting for a connection of ", uiWaitMs, "ms"); +#endif - std::unique_lock lk(m_MtxConnect); + std::unique_lock lock(m_MtxConnect); - if (uiWaitMs == 0xFFFFFFFFu) + auto isConnected = [&]() { - m_CvConnect.wait(lk, [this]{ - return m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected; - }); - return true; - } - if (uiWaitMs == 0u) - return (m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected); + return m_eConnectState.load(std::memory_order_acquire) == + sdv::ipc::EConnectState::connected; + }; - return m_CvConnect.wait_for(lk, std::chrono::milliseconds(uiWaitMs), - [this]{ return m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected; }); + + if (isConnected()) + return true; + + // Wait for the connection to take place. + // Attention: sporadic + if (uiWaitMs) + m_cvConnect.wait_for(lock, std::chrono::milliseconds(uiWaitMs), isConnected); + +#if ENABLE_REPORTING >= 1 + if (m_eConnectState == sdv::ipc::EConnectState::connected) + TRACE("Waiting finished - connection established"); + else + TRACE("Waiting finished - timeout occurred"); +#endif + + return m_eConnectState == sdv::ipc::EConnectState::connected; } void CUnixSocketConnection::CancelWait() { - // optional: m_CvConnect.notify_all(); +#if ENABLE_REPORTING >= 1 + TRACE("Cancel the (potential) waiting for a connection"); +#endif + + m_cvConnect.notify_all(); } void CUnixSocketConnection::Disconnect() { - StopThreadsAndCloseSockets(/*unlinkPath*/ false); + + // Try to notify peer before closing the socket. + if (m_Fd >= 0 && + m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected) + { + SendControlMessage(EMsgType::connect_term); + } + + // Notify listeners before entering teardown mode. SetConnectState(sdv::ipc::EConnectState::disconnected); + m_cvConnect.notify_all(); + + m_StopReceiveThread.store(true, std::memory_order_release); + m_StopConnectThread.store(true, std::memory_order_release); + m_cvConnect.notify_all(); // wake WaitForClientEnd before joins + + StopThreadsAndCloseSockets(/*unlinkPath*/ false); } sdv::ipc::EConnectState CUnixSocketConnection::GetConnectState() const @@ -486,28 +643,58 @@ sdv::ipc::EConnectState CUnixSocketConnection::GetConnectState() const void CUnixSocketConnection::DestroyObject() { + bool expected = false; + if (!m_DestroyObjectCalled.compare_exchange_strong(expected, true)) + { + return; + } + m_StopReceiveThread.store(true); - m_eConnectState = sdv::ipc::EConnectState::disconnected; + m_StopConnectThread.store(true); + SetConnectState(sdv::ipc::EConnectState::terminating); + + // Perform full teardown (threads/sockets/state/waiters). + Disconnect(); + + std::function removeCallback; + { + std::lock_guard lock(m_WatchdogMtx); + removeCallback = std::move(m_WatchdogRemoveCallback); + } + if (removeCallback) + { + removeCallback(this); + } } void CUnixSocketConnection::SetConnectState(sdv::ipc::EConnectState eConnectState) { + if (eConnectState == sdv::ipc::EConnectState::connected) + m_bConnectedOnce.store(true, std::memory_order_release); + // Update internal state atomically and wake up waiters. { std::lock_guard lk(m_MtxConnect); m_eConnectState.store(eConnectState, std::memory_order_release); } - m_CvConnect.notify_all(); + + // During teardown, owner objects may already be destructing. Do not callback then. + const bool bTeardown = m_StopReceiveThread.load(std::memory_order_acquire) || + m_StopConnectThread.load(std::memory_order_acquire); // Notify the legacy single-listener (kept for backward compatibility). - try + if (!bTeardown && m_pEvent) { - m_pEvent->SetConnectState(eConnectState); + try + { + m_pEvent->SetConnectState(eConnectState); + } + catch (...) { /* swallow: user callback must not crash transport */ } } - catch (...) { /* swallow: user callback must not crash transport */ } // Notify all registered listeners from the registry (read-mostly path). bool needCompact = false; + if (!bTeardown) { std::shared_lock rlock(m_mtxEventCallbacks); for (const auto& entry : m_lstEventCallbacks) @@ -522,7 +709,7 @@ void CUnixSocketConnection::SetConnectState(sdv::ipc::EConnectState eConnectStat } // Compact registry if we saw null entries (write path). - if (needCompact) + if (!bTeardown && needCompact) { std::unique_lock wlock(m_mtxEventCallbacks); m_lstEventCallbacks.remove_if([](const SEventCallback& e){ return e.pCallback == nullptr; }); @@ -601,13 +788,29 @@ void CUnixSocketConnection::ConnectWorker() } ::unlink(m_UdsPath.c_str()); - sockaddr_un addr{}; addr.sun_family = AF_UNIX; - ::snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", m_UdsPath.c_str()); - if (::bind(m_ListenFd, reinterpret_cast(&addr), sizeof(addr)) < 0) + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + + if (m_UdsPath.size() >= sizeof(addr.sun_path)) + { + SDV_LOG_ERROR("[UDS] Path too long: ", m_UdsPath, " (max ", sizeof(addr.sun_path) - 1, ")"); + ::close(m_ListenFd); + m_ListenFd = -1; + SetConnectState(sdv::ipc::EConnectState::connection_error); + return; + } + + strncpy(addr.sun_path, m_UdsPath.c_str(), sizeof(addr.sun_path) - 1); + addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; + + socklen_t len = offsetof(sockaddr_un, sun_path) + strlen(addr.sun_path) + 1; + + if (::bind(m_ListenFd, reinterpret_cast(&addr), len) < 0) { SDV_LOG_ERROR("[UDS][Server] bind('", m_UdsPath, "') failed: ", std::strerror(errno)); - ::close(m_ListenFd); m_ListenFd = -1; + ::close(m_ListenFd); + m_ListenFd = -1; SetConnectState(sdv::ipc::EConnectState::connection_error); return; } @@ -617,71 +820,109 @@ void CUnixSocketConnection::ConnectWorker() if (::listen(m_ListenFd, 1) < 0) { SDV_LOG_ERROR("[UDS][Server] listen() failed: ", std::strerror(errno)); - ::close(m_ListenFd); m_ListenFd = -1; + ::close(m_ListenFd); + m_ListenFd = -1; SetConnectState(sdv::ipc::EConnectState::connection_error); return; } -#if ENABLE_REPORTING >= 1 + #if ENABLE_REPORTING >= 1 TRACE("[UDS][Server] Listening on path ", m_UdsPath); -#endif + #endif - // Cancellable accept loop (poll) - int clientFd = -1; + SetConnectState(sdv::ipc::EConnectState::initializing); + + // Keep listener alive and accept clients repeatedly. while (!m_StopConnectThread.load()) { - struct pollfd pfd{ m_ListenFd, POLLIN, 0 }; - const int pr = ::poll(&pfd, 1, /*timeout_ms*/ 100); - if (pr < 0) { if (errno == EINTR) continue; SDV_LOG_WARNING("[UDS][Server] poll() error: ", std::strerror(errno)); break; } - if (pr == 0) continue; + int clientFd = -1; + bool acceptFatal = false; - if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) + // Cancellable accept loop + while (!m_StopConnectThread.load()) { -#if ENABLE_REPORTING >= 1 - TRACE("[UDS][Server] Listening socket closed/invalidated"); -#endif - break; - } + struct pollfd pfd{ m_ListenFd, POLLIN, 0 }; + const int pr = ::poll(&pfd, 1, /*timeout_ms*/ 10); - if (pfd.revents & POLLIN) - { - clientFd = ::accept(m_ListenFd, nullptr, nullptr); - if (clientFd < 0) + if (pr < 0) { if (errno == EINTR) continue; if (m_StopConnectThread.load()) break; - SDV_LOG_ERROR("[UDS][Server] accept() failed: ", std::strerror(errno)); + SDV_LOG_WARNING("[UDS][Server] poll() error: ", std::strerror(errno)); + acceptFatal = true; break; } - break; // accepted + + if (pr == 0) continue; + + if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) + { + #if ENABLE_REPORTING >= 1 + TRACE("[UDS][Server] Listening socket closed/invalidated"); + #endif + if (!m_StopConnectThread.load()) + acceptFatal = true; + break; + } + + if (pfd.revents & POLLIN) + { + clientFd = ::accept(m_ListenFd, nullptr, nullptr); + + if (clientFd < 0) + { + if (errno == EINTR) continue; + if (m_StopConnectThread.load()) break; + SDV_LOG_ERROR("[UDS][Server] accept() failed: ", std::strerror(errno)); + acceptFatal = true; + break; + } + break; // one client accepted + } + } + + if (m_StopConnectThread.load()) + break; + + if (acceptFatal || clientFd < 0) + { + SetConnectState(sdv::ipc::EConnectState::connection_error); + break; + } + + // Serve one client session + m_Fd = clientFd; + SetConnectState(sdv::ipc::EConnectState::initialized); + StartReceiveThread_Unsafe(); + + // Wait until that client disconnects or teardown is requested + WaitForClientEnd(); + CloseClientSocketOnly(); + + if (!m_StopConnectThread.load()) + { + // Re-arm state for next accept + SetConnectState(sdv::ipc::EConnectState::initializing); } } - // Close listen fd (safe if already closed elsewhere) - const int lfd = m_ListenFd; m_ListenFd = -1; + // Close listen FD on final shutdown/error + const int lfd = m_ListenFd; + m_ListenFd = -1; if (lfd >= 0) ::close(lfd); if (m_StopConnectThread.load()) { -#if ENABLE_REPORTING >= 1 + #if ENABLE_REPORTING >= 1 TRACE("[UDS][Server] ConnectWorker stopped intentionally"); -#endif - return; // intentional stop - } - if (clientFd < 0) - { - SetConnectState(sdv::ipc::EConnectState::connection_error); - return; + #endif } - m_Fd = clientFd; - SetConnectState(sdv::ipc::EConnectState::connected); - StartReceiveThread_Unsafe(); return; } else { - // --- CLIENT --- + // --- CLIENT --- m_Fd = ::socket(AF_UNIX, SOCK_STREAM, 0); if (m_Fd < 0) { @@ -690,8 +931,22 @@ void CUnixSocketConnection::ConnectWorker() return; } - sockaddr_un addr{}; addr.sun_family = AF_UNIX; - ::snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", m_UdsPath.c_str()); + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + + if (m_UdsPath.size() >= sizeof(addr.sun_path)) + { + SDV_LOG_ERROR("[UDS] Path too long: ", m_UdsPath, " (max ", sizeof(addr.sun_path) - 1, ")"); + ::close(m_Fd); + m_Fd = -1; + SetConnectState(sdv::ipc::EConnectState::connection_error); + return; + } + + strncpy(addr.sun_path, m_UdsPath.c_str(), sizeof(addr.sun_path) - 1); + addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; + + socklen_t len = offsetof(sockaddr_un, sun_path) + strlen(addr.sun_path) + 1; #if ENABLE_REPORTING >= 1 TRACE("[UDS][Client] Connecting to ", m_UdsPath); @@ -700,62 +955,55 @@ void CUnixSocketConnection::ConnectWorker() int attempts = 10; while (attempts-- > 0 && !m_StopConnectThread.load()) { - if (::connect(m_Fd, reinterpret_cast(&addr), sizeof(addr)) == 0) + if (::connect(m_Fd, reinterpret_cast(&addr), len) == 0) { - SetConnectState(sdv::ipc::EConnectState::connected); -#if ENABLE_REPORTING >= 1 - TRACE("[UDS][Client] Connected"); -#endif + SetConnectState(sdv::ipc::EConnectState::initialized); StartReceiveThread_Unsafe(); return; } + else + { + SDV_LOG_WARNING("Connect failed: ", strerror(errno)); + } + std::this_thread::sleep_for(std::chrono::milliseconds(200)); } - if (!m_StopConnectThread.load()) + // Disconnect() requested while retries were running: do not overwrite state with connection_error. + if (m_StopConnectThread.load()) { - SDV_LOG_WARNING("[UDS][Client] connect() timeout to '", m_UdsPath, - "', last errno=", errno, " (", std::strerror(errno), ")"); + ::close(m_Fd); + m_Fd = -1; + return; } - ::close(m_Fd); m_Fd = -1; + SDV_LOG_WARNING("[UDS][Client] connect() timeout to '", m_UdsPath, + "', last errno=", errno, " (", std::strerror(errno), ")"); + + ::close(m_Fd); + m_Fd = -1; SetConnectState(sdv::ipc::EConnectState::connection_error); return; } } catch (const std::exception& ex) { - SDV_LOG_ERROR("[UDS][ConnectWorker] exception: ", ex.what()); - SetConnectState(sdv::ipc::EConnectState::connection_error); + if (!m_StopConnectThread.load(std::memory_order_acquire)) + { + SDV_LOG_ERROR("[UDS][ConnectWorker] exception: ", ex.what()); + SetConnectState(sdv::ipc::EConnectState::connection_error); + } } catch (...) { - SDV_LOG_ERROR("[UDS][ConnectWorker] unknown exception"); - SetConnectState(sdv::ipc::EConnectState::connection_error); + if (!m_StopConnectThread.load(std::memory_order_acquire)) + { + SDV_LOG_ERROR("[UDS][ConnectWorker] unknown exception"); + SetConnectState(sdv::ipc::EConnectState::connection_error); + } } } -// Receive thread and SDV state machine -void CUnixSocketConnection::ReceiveSyncAnswer(const CMessage& message) -{ - const auto hdr = message.GetMsgHdr(); - if (hdr.uiVersion != SDVFrameworkInterfaceVersion) - { - SetConnectState(sdv::ipc::EConnectState::communication_error); - SDV_LOG_WARNING("[UDS][RX] sync_answer with invalid version"); - return; - } - - // Client -> send connect_request - SConnectMsg msg{}; - msg.uiVersion = SDVFrameworkInterfaceVersion; - msg.eType = EMsgType::connect_request; - msg.tProcessID = GetCurrentProcessID_Local(); - - if (!SendSizedPacket(&msg, sizeof(msg))) - SDV_LOG_ERROR("[UDS][RX] Failed to send connect_request in response to sync_answer"); -} - void CUnixSocketConnection::ReceiveMessages() { try @@ -789,34 +1037,69 @@ void CUnixSocketConnection::ReceiveMessages() if (pr == 0) { - if (!m_AcceptConnectionRequired && (m_eConnectState == sdv::ipc::EConnectState::initialized)) + if (!m_AcceptConnectionRequired && + (m_eConnectState == sdv::ipc::EConnectState::initialized || + m_eConnectState == sdv::ipc::EConnectState::disconnected)) { auto now = std::chrono::high_resolution_clock::now(); if (std::chrono::duration(now - tpStart).count() > 0.5) { tpStart = now; - SMsgHdr sync{ SDVFrameworkInterfaceVersion, EMsgType::sync_request }; - if (!SendSizedPacket(&sync, static_cast(sizeof(sync)))) - SDV_LOG_WARNING("[UDS][RX] Failed to send periodic sync_request (client pacing)"); + + if (!SendControlMessage(EMsgType::sync_request)) + { + SDV_LOG_WARNING("[UDS][RX] Failed to send periodic sync_request"); + } } } continue; } - - if (pr < 0 || (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) + if (pr < 0) { SetConnectState(sdv::ipc::EConnectState::disconnected); - SDV_LOG_WARNING("[UDS][RX] poll() hangup/error -> disconnected"); + SDV_LOG_WARNING("[UDS][RX] poll() failed -> disconnected, errno=", errno, + " (", std::strerror(errno), ")"); break; } - if ((pfd.revents & POLLIN) == 0) continue; + + const short revents = pfd.revents; + const bool bHasPollIn = (revents & POLLIN) != 0; + const bool bHasPollErr = (revents & (POLLERR | POLLNVAL)) != 0; + const bool bHasPollHup = (revents & POLLHUP) != 0; + const bool bHasPollNval = (revents & POLLNVAL) != 0; + + // POLLNVAL commonly happens when another thread closed/replaced the FD during local shutdown. + // In this case treat it as an expected teardown signal. + if (bHasPollNval && (m_StopReceiveThread.load() || m_Fd < 0)) + { + SetConnectState(sdv::ipc::EConnectState::disconnected); + break; + } + + // If POLLIN is present together with HUP/ERR, consume pending data first. + // This avoids dropping the last frame when peer closes after sending. + if (bHasPollErr && !bHasPollIn) + { + SetConnectState(sdv::ipc::EConnectState::disconnected); + break; + } + + if (bHasPollHup && !bHasPollIn) + { + SetConnectState(sdv::ipc::EConnectState::disconnected); + break; + } + + if (!bHasPollIn) continue; + // Transport header uint32_t packetSize = 0; if (!ReadTransportHeader(packetSize)) { SetConnectState(sdv::ipc::EConnectState::disconnected); - SDV_LOG_WARNING("[UDS][RX] Invalid/missing transport header -> disconnected"); + if (!bHasPollHup) + SDV_LOG_WARNING("[UDS][RX] Invalid/missing transport header -> disconnected"); break; } @@ -825,7 +1108,8 @@ void CUnixSocketConnection::ReceiveMessages() if (!ReadNumberOfBytes(reinterpret_cast(payload.data()), packetSize)) { SetConnectState(sdv::ipc::EConnectState::disconnected); - SDV_LOG_WARNING("[UDS][RX] Incomplete payload read -> disconnected"); + if (!bHasPollHup) + SDV_LOG_WARNING("[UDS][RX] Incomplete payload read -> disconnected"); break; } @@ -886,61 +1170,168 @@ void CUnixSocketConnection::ReceiveMessages() void CUnixSocketConnection::ReceiveSyncRequest(const CMessage& message) { const auto hdr = message.GetMsgHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) { SetConnectState(sdv::ipc::EConnectState::communication_error); - SDV_LOG_WARNING("[UDS][RX] sync_request with invalid version"); return; } - // Reply with sync_answer - SMsgHdr reply{}; - reply.uiVersion = SDVFrameworkInterfaceVersion; - reply.eType = EMsgType::sync_answer; + if (!IsServer()) + return; + + SetConnectState(sdv::ipc::EConnectState::connecting); + + if (!SendControlMessage(EMsgType::sync_answer)) + { + SetConnectState(sdv::ipc::EConnectState::communication_error); + return; + } + +} + +// Receive thread and SDV state machine +void CUnixSocketConnection::ReceiveSyncAnswer(const CMessage& message) +{ + const auto hdr = message.GetMsgHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetConnectState(sdv::ipc::EConnectState::communication_error); + SDV_LOG_WARNING("[UDS][RX] sync_answer with invalid version"); + return; + } + + if (IsServer()) + return; + + SetConnectState(sdv::ipc::EConnectState::negotiating); + + if (!SendConnectMessage(EMsgType::connect_request)) + { + SetConnectState(sdv::ipc::EConnectState::communication_error); + return; + } - if (!SendSizedPacket(&reply, sizeof(reply))) - SDV_LOG_ERROR("[UDS][RX] Failed to send sync_answer to sync_request"); } void CUnixSocketConnection::ReceiveConnectRequest(const CMessage& message) { - const auto hdr = message.GetConnectHdr(); - if (hdr.uiVersion != SDVFrameworkInterfaceVersion) - { - SetConnectState(sdv::ipc::EConnectState::communication_error); - SDV_LOG_WARNING("[UDS][RX] connect_request with invalid version"); - return; - } + //if (m_eConnectState == sdv::ipc::EConnectState::connecting) + //{ + const auto hdr = message.GetConnectHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetConnectState(sdv::ipc::EConnectState::communication_error); + SDV_LOG_WARNING("[UDS][RX] connect_request with invalid version"); + return; + } - // Reply with connect_answer - SConnectMsg reply{}; - reply.uiVersion = SDVFrameworkInterfaceVersion; - reply.eType = EMsgType::connect_answer; - reply.tProcessID = GetCurrentProcessID_Local(); + // connect_request must be handled only by server + if (!IsServer()) + { + SDV_LOG_WARNING("[UDS][RX] Client received connect_request - ignored"); + return; + } + + if (!SendConnectMessage(EMsgType::connect_answer)) + { + SDV_LOG_ERROR("[UDS][RX] Failed to send connect_answer"); + SetConnectState(sdv::ipc::EConnectState::communication_error); + return; + } - if (!SendSizedPacket(&reply, sizeof(reply))) - SDV_LOG_ERROR("[UDS][RX] Failed to send connect_answer"); + SetConnectState(sdv::ipc::EConnectState::connected); + m_cvConnect.notify_all(); + +#if ENABLE_REPORTING >= 1 + TRACE("Trigger connected"); +#endif + + //} } void CUnixSocketConnection::ReceiveConnectAnswer(const CMessage& message) { - const auto hdr = message.GetConnectHdr(); - if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + //if (m_eConnectState == sdv::ipc::EConnectState::negotiating) + //{ + const auto hdr = message.GetConnectHdr(); + if (hdr.uiVersion != SDVFrameworkInterfaceVersion) + { + SetConnectState(sdv::ipc::EConnectState::communication_error); + SDV_LOG_WARNING("[UDS][RX] connect_answer with invalid version"); + return; + } + // connect_answer must be handled only by client + if (IsServer()) + { + SDV_LOG_WARNING("[UDS][RX] Server received connect_answer - ignored"); + return; + } + + SetConnectState(sdv::ipc::EConnectState::connected); + m_cvConnect.notify_all(); +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][RX] Client connected after connect_answer"); +#endif + + //} + +} + +bool CUnixSocketConnection::SendControlMessage(EMsgType type) +{ + // Only simple header-based control messages are allowed here. + if (type != EMsgType::sync_request && + type != EMsgType::sync_answer && + type != EMsgType::connect_term) { - SetConnectState(sdv::ipc::EConnectState::communication_error); - SDV_LOG_WARNING("[UDS][RX] connect_answer with invalid version"); - return; + SDV_LOG_ERROR("[UDS][TX] Invalid control message type"); + return false; } - // Fully established - SetConnectState(sdv::ipc::EConnectState::connected); + SMsgHdr msg{}; + msg.uiVersion = SDVFrameworkInterfaceVersion; + msg.eType = type; + +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][TX] Send control message type=", static_cast(type)); +#endif + + return SendSizedPacket(&msg, sizeof(msg)); +} + +bool CUnixSocketConnection::SendConnectMessage(EMsgType type) +{ + if (type != EMsgType::connect_request && + type != EMsgType::connect_answer) + { + SDV_LOG_ERROR("[UDS][TX] Invalid connect message type"); + return false; + } + + SConnectMsg msg{}; + msg.uiVersion = SDVFrameworkInterfaceVersion; + msg.eType = type; + msg.tProcessID = GetCurrentProcessID_Local(); + +#if ENABLE_REPORTING >= 1 + TRACE("[UDS][TX] Send ", + type == EMsgType::connect_request ? "connect_request" : "connect_answer", + " pid=", msg.tProcessID); +#endif + + return SendSizedPacket(&msg, sizeof(msg)); } void CUnixSocketConnection::ReceiveConnectTerm(const CMessage& /*message*/) { // Peer requested termination SetConnectState(sdv::ipc::EConnectState::disconnected); - m_StopReceiveThread.store(true); + // m_rWatchDog.RemoveMonitor(this); + + m_StopReceiveThread.store(true, std::memory_order_release); + m_cvConnect.notify_all(); + } void CUnixSocketConnection::StartReceiveThread_Unsafe() @@ -954,15 +1345,29 @@ void CUnixSocketConnection::StartReceiveThread_Unsafe() } m_StopReceiveThread.store(false); - m_ReceiveThread = std::thread(&CUnixSocketConnection::ReceiveMessages, this); + m_ReceiveThread = sdv::core::secure_thread(&CUnixSocketConnection::ReceiveMessages, this); } void CUnixSocketConnection::StopThreadsAndCloseSockets(bool unlinkPath) { + + { + // Prevent callbacks into potentially destroyed owners during teardown. + std::lock_guard lk(m_StateMtx); + m_pReceiver = nullptr; + m_pEvent = nullptr; + } + { + std::unique_lock wlock(m_mtxEventCallbacks); + m_lstEventCallbacks.clear(); + } + // Signal stop m_StopReceiveThread.store(true); m_StopConnectThread.store(true); + m_cvConnect.notify_all(); // defensive wake-up for any waiter + // Close listen FD to break accept() const int lfd = m_ListenFd; m_ListenFd = -1; if (lfd >= 0) ::close(lfd); @@ -1077,7 +1482,8 @@ uint32_t CUnixSocketConnection::ReadDataTable(const CMessage& rMessage, SDataCon // Buffer count if (rMessage.GetSize() < (uiOffset + static_cast(sizeof(uint32_t)))) return 0; - const uint32_t uiAmount = *reinterpret_cast(rMessage.GetData() + uiOffset); + uint32_t uiAmount = 0; + std::memcpy(&uiAmount, rMessage.GetData() + uiOffset, sizeof(uiAmount)); uiOffset += sizeof(uint32_t); rsDataCtxt.uiCurrentOffset += sizeof(uint32_t); @@ -1089,7 +1495,8 @@ uint32_t CUnixSocketConnection::ReadDataTable(const CMessage& rMessage, SDataCon for (uint32_t i = 0; i < uiAmount; ++i) { - const uint32_t sz = *reinterpret_cast(rMessage.GetData() + uiOffset); + uint32_t sz = 0; + std::memcpy(&sz, rMessage.GetData() + uiOffset, sizeof(sz)); sizes.push_back(static_cast(sz)); uiOffset += sizeof(uint32_t); rsDataCtxt.uiCurrentOffset += sizeof(uint32_t); @@ -1163,4 +1570,4 @@ bool CUnixSocketConnection::ReadDataChunk(const CMessage& rMessage, uint32_t uiO return true; } -#endif // defined(__unix__) +#endif // defined(__unix__) \ No newline at end of file diff --git a/sdv_services/uds_unix_sockets/connection.h b/sdv_services/uds_unix_sockets/connection.h index 1ce1730..979c296 100644 --- a/sdv_services/uds_unix_sockets/connection.h +++ b/sdv_services/uds_unix_sockets/connection.h @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -98,6 +100,9 @@ public: /** @brief Destroy object (IObjectDestroy). */ void DestroyObject() override; + /** @brief Register callback used to remove this connection from manager watchdog storage. */ + void SetWatchDogRemoveCallback(std::function callback); + /** @brief Set state and notify listeners (callback-safe). */ void SetConnectState(sdv::ipc::EConnectState eConnectState); @@ -153,21 +158,27 @@ public: SMsgHdr GetMsgHdr() const { if (GetSize() < sizeof(SMsgHdr)) return SMsgHdr{0, EMsgType::connect_term}; - return *reinterpret_cast(GetData()); + SMsgHdr hdr{}; + std::memcpy(&hdr, GetData(), sizeof(SMsgHdr)); + return hdr; } /** @return SDV connect header (or default if undersized). */ SConnectMsg GetConnectHdr() const { if (GetSize() < sizeof(SConnectMsg)) return SConnectMsg{}; - return *reinterpret_cast(GetData()); + SConnectMsg hdr{}; + std::memcpy(&hdr, GetData(), sizeof(SConnectMsg)); + return hdr; } /** @return SDV fragmented header (or default if undersized). */ SFragmentedMsgHdr GetFragmentedHdr() const { if (GetSize() < sizeof(SFragmentedMsgHdr)) return SFragmentedMsgHdr{}; - return *reinterpret_cast(GetData()); + SFragmentedMsgHdr hdr{}; + std::memcpy(&hdr, GetData(), sizeof(SFragmentedMsgHdr)); + return hdr; } /** @return true if the SDV envelope is well-formed. */ @@ -253,6 +264,10 @@ public: */ void ReceiveMessages(); + bool SendConnectMessage(EMsgType type); + + bool SendControlMessage(EMsgType type); + /** * @brief Handle an incoming sync_request message. * @@ -360,6 +375,10 @@ public: */ void StopThreadsAndCloseSockets(bool unlinkPath); + void WaitForClientEnd(); + + void CloseClientSocketOnly(); + private: //Transport state int m_Fd { -1 }; ///< Active connection FD. @@ -370,19 +389,24 @@ private: //Threads & control std::atomic m_StopReceiveThread { false }; std::atomic m_StopConnectThread { false }; - std::thread m_ReceiveThread; - std::thread m_ConnectThread; + sdv::core::secure_thread m_ReceiveThread; + sdv::core::secure_thread m_ConnectThread; //State & synchronization std::condition_variable m_StateCv; std::atomic m_eConnectState { sdv::ipc::EConnectState::uninitialized }; + std::atomic m_bConnectedOnce { false }; ///< Latched true once a connection was established. sdv::ipc::IDataReceiveCallback* m_pReceiver { nullptr }; sdv::ipc::IConnectEventCallback* m_pEvent { nullptr }; std::mutex m_MtxConnect; - std::condition_variable m_CvConnect; + std::condition_variable m_cvConnect; std::mutex m_StateMtx; ///< Protects receiver/event assignment. std::list m_lstEventCallbacks; ///< List containing event callbacks std::shared_mutex m_mtxEventCallbacks; ///< Protect access to callback list + std::mutex m_WatchdogMtx; + std::function m_WatchdogRemoveCallback; + std::atomic m_DestroyObjectCalled { false }; + std::atomic m_CancelWait { false }; //TX synchronization std::mutex m_SendMtx; diff --git a/sdv_services/uds_unix_sockets/watchdog.cpp b/sdv_services/uds_unix_sockets/watchdog.cpp new file mode 100644 index 0000000..98c141f --- /dev/null +++ b/sdv_services/uds_unix_sockets/watchdog.cpp @@ -0,0 +1,58 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#if defined(__unix__) + +#include "watchdog.h" + +void CUnixSocketsConnectionWatchDog::AddConnectionImpl(const std::shared_ptr& connection) +{ + std::lock_guard lock(m_Mutex); + m_Connections[connection.get()] = connection; +} + +void CUnixSocketsConnectionWatchDog::RemoveConnection(const void* connection) +{ + if (!connection) + { + return; + } + + std::shared_ptr removed; + { + std::lock_guard lock(m_Mutex); + auto it = m_Connections.find(connection); + if (it == m_Connections.end()) + { + return; + } + + removed = std::move(it->second); + m_Connections.erase(it); + } + + removed.reset(); +} + +void CUnixSocketsConnectionWatchDog::Clear() +{ + std::map> localConnections; + { + std::lock_guard lock(m_Mutex); + localConnections.swap(m_Connections); + } + + localConnections.clear(); +} + +#endif // defined(__unix__) diff --git a/sdv_services/uds_unix_sockets/watchdog.h b/sdv_services/uds_unix_sockets/watchdog.h new file mode 100644 index 0000000..4cb6abb --- /dev/null +++ b/sdv_services/uds_unix_sockets/watchdog.h @@ -0,0 +1,47 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#if defined(__unix__) +#ifndef UDS_UNIX_SOCKETS_WATCHDOG_H +#define UDS_UNIX_SOCKETS_WATCHDOG_H + +#include +#include +#include + +class CUnixSocketsConnectionWatchDog +{ +public: + template + void AddConnection(const std::shared_ptr& connection) + { + if (!connection) + { + return; + } + + AddConnectionImpl(std::static_pointer_cast(connection)); + } + + void RemoveConnection(const void* connection); + void Clear(); + +private: + void AddConnectionImpl(const std::shared_ptr& connection); + + std::mutex m_Mutex; + std::map> m_Connections; +}; + +#endif // UDS_UNIX_SOCKETS_WATCHDOG_H +#endif // defined(__unix__) diff --git a/sdv_services/uds_unix_tunnel/CMakeLists.txt b/sdv_services/uds_unix_tunnel/CMakeLists.txt index f40e862..db2700c 100644 --- a/sdv_services/uds_unix_tunnel/CMakeLists.txt +++ b/sdv_services/uds_unix_tunnel/CMakeLists.txt @@ -12,22 +12,57 @@ if(UNIX) # Define project project(uds_unix_tunnel VERSION 1.0 LANGUAGES CXX) -# Define target -add_library(uds_unix_tunnel STATIC +set(UDS_UNIX_TUNNEL_SOURCES channel_mgnt.cpp connection.cpp + watchdog.cpp +) + +# Define targets +add_library(uds_unix_tunnel STATIC + ${UDS_UNIX_TUNNEL_SOURCES} ) -target_link_libraries(uds_unix_tunnel rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) +add_library(uds_unix_tunnel_module SHARED + ${UDS_UNIX_TUNNEL_SOURCES} + ) -target_include_directories(uds_unix_tunnel PRIVATE ./include/) -set_target_properties(uds_unix_tunnel PROPERTIES PREFIX "") -set_target_properties(uds_unix_tunnel PROPERTIES SUFFIX ".sdv") +target_link_libraries(uds_unix_tunnel + PRIVATE + uds_unix_sockets_static + rt + ${CMAKE_DL_LIBS} + ${CMAKE_THREAD_LIBS_INIT} +) + +target_link_libraries(uds_unix_tunnel_module + PRIVATE + uds_unix_sockets_static + rt + ${CMAKE_DL_LIBS} + ${CMAKE_THREAD_LIBS_INIT} +) + +target_include_directories(uds_unix_tunnel + PRIVATE + ./include/ + ../uds_unix_sockets/ +) + +target_include_directories(uds_unix_tunnel_module + PRIVATE + ./include/ + ../uds_unix_sockets/ +) +set_target_properties(uds_unix_tunnel_module PROPERTIES PREFIX "") +set_target_properties(uds_unix_tunnel_module PROPERTIES SUFFIX ".sdv") +set_target_properties(uds_unix_tunnel_module PROPERTIES OUTPUT_NAME "uds_unix_tunnel") # Build dependencies add_dependencies(uds_unix_tunnel CompileCoreIDL) +add_dependencies(uds_unix_tunnel_module CompileCoreIDL) # Appending the service in the service list -set(SDV_Service_List ${SDV_Service_List} uds_unix_tunnel PARENT_SCOPE) +set(SDV_Service_List ${SDV_Service_List} uds_unix_tunnel_module PARENT_SCOPE) endif() \ No newline at end of file diff --git a/sdv_services/uds_unix_tunnel/channel_mgnt.cpp b/sdv_services/uds_unix_tunnel/channel_mgnt.cpp index 97ae63c..d07e215 100644 --- a/sdv_services/uds_unix_tunnel/channel_mgnt.cpp +++ b/sdv_services/uds_unix_tunnel/channel_mgnt.cpp @@ -1,3 +1,15 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ #if defined(__unix__) #include "channel_mgnt.h" @@ -15,6 +27,7 @@ namespace { +static std::atomic g_nextChannelId{1}; /** * @brief Parses a semicolon-separated list of key=value pairs into a map. @@ -52,23 +65,113 @@ static std::string ClampSunPath(const std::string& p) return (p.size() < MaxLen) ? p : p.substr(0, MaxLen - 1); } +static std::string GetUserRuntimeDir() +{ + const std::string path = "/run/ipc/sdv"; + + struct stat st{}; + if (::stat(path.c_str(), &st) == 0) + { + return path; + } + + // fallback if /run/ipc/sdv is not available + const std::string fallback = "/tmp/sdv"; + ::mkdir(fallback.c_str(), 0770); + + return fallback; +} + + +static std::string BuildTunnelPath(const std::string& baseDir, + const std::string& tunnel, + uint32_t instanceId, + uint32_t pid) +{ + std::string dir = baseDir + "/" + tunnel; + + if (::mkdir(dir.c_str(), 0770) != 0 && errno != EEXIST) + { + SDV_LOG_WARNING("Failed to create tunnel directory"); + } + + return dir + "/vapi_" + + std::to_string(instanceId) + "_" + + std::to_string(pid) + ".sock"; +} + +static bool ExtractTunnelPathFromConfig(const std::string& input, std::string& outPath) +{ + const auto kv = ParseKV(input); + if (kv.count("proto")) + { + if (kv.at("proto") != "tunnel") + return false; + + auto it = kv.find("path"); + outPath = (it != kv.end()) ? it->second : std::string{}; + return true; + } + + sdv::toml::CTOMLParser parser(input); + if (!parser.IsValid()) + return false; + + const std::string providerName = parser.GetDirect("Provider.Name").GetValue(); + if (!providerName.empty() && + providerName != "unix_domain_sockets_tunnel" && + providerName != "UnixTunnelChannelControl" && + providerName != "WinTunnelChannelControl") + { + return false; + } + + const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue(); + if (!nested.empty()) + { + return ExtractTunnelPathFromConfig(nested, outPath); + } + + auto pathNode = parser.GetDirect("IpcChannel.Path"); + if (pathNode.GetType() == sdv::toml::ENodeType::node_string) + { + outPath = static_cast(pathNode.GetValue()); + return true; + } + + auto nameNode = parser.GetDirect("IpcChannel.Name"); + if (nameNode.GetType() == sdv::toml::ENodeType::node_string) + { + const std::string name = static_cast(nameNode.GetValue()); + if (!name.empty()) + { + outPath = GetUserRuntimeDir() + "/" + name + ".sock"; + return true; + } + } + + outPath.clear(); + return true; +} + +static std::string ExtractTunnelName(const std::string& input) +{ + const auto kv = ParseKV(input); + if (kv.count("tunnel")) + { + return kv.at("tunnel"); + } + + // Fallback default + return "default"; +} + + } // anonymous namespace std::string CUnixTunnelChannelMgnt::MakeUserRuntimeDir() { - std::ostringstream oss; - oss << "/run/user/" << ::getuid(); - - struct stat st{}; - if (::stat(oss.str().c_str(), &st) == 0) - { - std::string path = oss.str() + "/sdv"; - ::mkdir(path.c_str(), 0770); - return path; - } - - ::mkdir("/tmp/sdv", 0770); - return "/tmp/sdv"; + return GetUserRuntimeDir(); } bool CUnixTunnelChannelMgnt::OnInitialize() @@ -77,9 +180,11 @@ bool CUnixTunnelChannelMgnt::OnInitialize() } void CUnixTunnelChannelMgnt::OnShutdown() +{} + +void CUnixTunnelChannelMgnt::OnDestroy() { - // Actual cleanup is handled by destructors of CUnixTunnelConnection - // and CUnixSocketConnection (shared_ptr). + m_watchdog.Clear(); } sdv::ipc::SChannelEndpoint CUnixTunnelChannelMgnt::CreateEndpoint( @@ -88,91 +193,270 @@ sdv::ipc::SChannelEndpoint CUnixTunnelChannelMgnt::CreateEndpoint( sdv::ipc::SChannelEndpoint endpoint{}; const std::string baseDir = MakeUserRuntimeDir(); - std::string name = "TUNNEL_" + std::to_string(::getpid()); - std::string path = baseDir + "/" + name + ".sock"; + uint32_t instanceId = 1000; + const sdv::app::IAppContext* pCtx = sdv::core::GetCore(); + if (pCtx && pCtx->GetInstanceID() != 0) + { + instanceId = pCtx->GetInstanceID(); + } + + // Extract tunnel name from config + std::string input = static_cast(ssChannelConfig); + std::string tunnel = ExtractTunnelName(input); + + // Build safe path + std::string path = BuildTunnelPath(baseDir, tunnel, instanceId, static_cast(::getpid())); - // Parse optional TOML config for custom name/path if (!ssChannelConfig.empty()) { - sdv::toml::CTOMLParser cfg(ssChannelConfig.c_str()); - auto nameNode = cfg.GetDirect("IpcChannel.Name"); - if (nameNode.GetType() == sdv::toml::ENodeType::node_string) - name = static_cast(nameNode.GetValue()); + std::string configuredPath; - auto pathNode = cfg.GetDirect("IpcChannel.Path"); - if (pathNode.GetType() == sdv::toml::ENodeType::node_string) - path = static_cast(pathNode.GetValue()); + if (ExtractTunnelPathFromConfig(input, configuredPath) && !configuredPath.empty()) + { + std::string baseName = configuredPath; + auto pos = baseName.find_last_of('/'); + if (pos != std::string::npos) + { + baseName = baseName.substr(pos + 1); + } + path = baseDir + "/" + tunnel + "/" + baseName; + SDV_LOG_WARNING("Using custom path without tunnel isolation"); + } else - path = baseDir + "/" + name + ".sock"; + { + // fallback - still use tunnel-based path + tunnel = ExtractTunnelName(input); + instanceId = 1000; + + if (pCtx && pCtx->GetInstanceID() != 0) + { + instanceId = pCtx->GetInstanceID(); + } + + path = BuildTunnelPath(baseDir, tunnel, instanceId, static_cast(::getpid())); + } } path = ClampSunPath(path); // Create underlying UDS server transport - auto udsServer = std::make_shared( - -1, - /*acceptConnectionRequired*/ true, - path); + auto udsServer = std::make_shared(-1, /*acceptConnectionRequired*/ true, path); + uint32_t chId = g_nextChannelId++; // Create tunnel wrapper on top of UDS - auto tunnelServer = std::make_shared( - udsServer, - /*channelId*/ 0u); + auto tunnelServer = std::make_shared(udsServer, /*channelId*/ chId); - m_ServerTunnels.push_back(tunnelServer); + // Ignore cppcheck warning; if construction failed, an exception is expected first. + // cppcheck-suppress knownConditionTrueFalse + if (!tunnelServer) + return {}; + + tunnelServer->SetWatchDogRemoveCallback([this](const void* connection) + { + m_watchdog.RemoveConnection(connection); + }); + m_watchdog.AddConnection(tunnelServer); endpoint.pConnection = static_cast(tunnelServer.get()); - endpoint.ssConnectString = "proto=tunnel;role=server;path=" + path + ";"; + const std::string tunnelConnectString = "proto=tunnel;path=" + path + ";tunnel=" + tunnel + ";"; + endpoint.ssConnectString = std::string("[Provider]\n") + + "Name = \"unix_domain_sockets_tunnel\"\n" + + "ConnectString = \"" + tunnelConnectString + "\"\n"; return endpoint; } -sdv::IInterfaceAccess* CUnixTunnelChannelMgnt::Access( - const sdv::u8string& ssConnectString) +sdv::IInterfaceAccess* CUnixTunnelChannelMgnt::Access(const sdv::u8string& ssConnectString) { - const auto kv = ParseKV(static_cast(ssConnectString)); + const std::string input = static_cast(ssConnectString); + std::string tunnel = ExtractTunnelName(input); + bool parsed = false; + bool isServer = false; + std::string path; - // Only handle proto=tunnel - if (!kv.count("proto") || kv.at("proto") != "tunnel") + // Parse structured TOML forms first; raw connect strings are handled below. + if (input.rfind("proto=tunnel", 0) != 0) + { + + sdv::toml::CTOMLParser parser(input); + if (!parser.IsValid()) + { + SDV_LOG_WARNING("[TUNNEL][Access] TOML parse failed. Trying text fallback."); + + // Fallback for malformed TOML: try to recover an embedded proto=tunnel connect string. + const auto protoPos = input.find("proto=tunnel"); + if (protoPos != std::string::npos) + { + std::string fallbackCs = input.substr(protoPos); + + const auto quotePos = fallbackCs.find('"'); + if (quotePos != std::string::npos) + fallbackCs = fallbackCs.substr(0, quotePos); + + const auto newlinePos = fallbackCs.find('\n'); + if (newlinePos != std::string::npos) + fallbackCs = fallbackCs.substr(0, newlinePos); + + const auto kv = ParseKV(fallbackCs); + if (kv.count("proto") && kv.at("proto") == "tunnel") + { + parsed = true; + isServer = (kv.count("role") && kv.at("role") == "server"); + if (kv.count("path")) + path = kv.at("path"); + } + } + + if (!parsed) + { + SDV_LOG_WARNING("[TUNNEL][Access] Fallback parse failed. Returning nullptr"); + return nullptr; + } + } + + if (!parsed) + { + const std::string providerName = parser.GetDirect("Provider.Name").GetValue(); + if (!providerName.empty()) + { + if (providerName != "unix_domain_sockets_tunnel" && + providerName != "UnixTunnelChannelControl" && + providerName != "WinTunnelChannelControl") + { + SDV_LOG_WARNING("[TUNNEL][Access] Unsupported provider. Returning nullptr"); + return nullptr; + } + + const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue(); + if (!nested.empty()) + { + const auto nestedKv = ParseKV(nested); + if (nestedKv.count("tunnel")) + { + tunnel = nestedKv.at("tunnel"); + } + + if (nestedKv.count("proto") && nestedKv.at("proto") != "tunnel") + { + SDV_LOG_WARNING("[TUNNEL][Access] Nested proto is not tunnel. Returning nullptr"); + return nullptr; + } + + if (nestedKv.count("path")) + { + std::string baseName = nestedKv.at("path"); + auto pos = baseName.find_last_of('/'); + if (pos != std::string::npos) + { + baseName = baseName.substr(pos + 1); + } + + path = MakeUserRuntimeDir() + "/" + tunnel + "/" + baseName; + } + } + + parsed = true; + isServer = false; + } + else + { + // Callers can pass [IpcChannel] directly to Access(). + parsed = true; + isServer = false; + } + + if (path.empty()) + { + auto pathNode = parser.GetDirect("IpcChannel.Path"); + if (pathNode.GetType() == sdv::toml::ENodeType::node_string) + { + std::string baseName = static_cast(pathNode.GetValue()); + + auto pos = baseName.find_last_of('/'); + if (pos != std::string::npos) + { + baseName = baseName.substr(pos + 1); + } + + path = MakeUserRuntimeDir() + "/" + tunnel + "/" + baseName; + } + else + { + auto nameNode = parser.GetDirect("IpcChannel.Name"); + if (nameNode.GetType() == sdv::toml::ENodeType::node_string) + { + const std::string name = static_cast(nameNode.GetValue()); + if (!name.empty()) + { + path = MakeUserRuntimeDir() + "/" + tunnel + "/" + name + ".sock"; + } + } + } + } + } + } + + // Raw KV fallback: proto=tunnel;role=...;path=...; + if (!parsed) + { + if (input.rfind("proto=tunnel", 0) != 0) + { + SDV_LOG_WARNING("[TUNNEL][Access] Raw KV parse rejected: input does not start with proto=tunnel. Returning nullptr"); + return nullptr; + } + + const auto kv = ParseKV(input); + if (!kv.count("proto") || kv.at("proto") != "tunnel") + { + SDV_LOG_WARNING("[TUNNEL][Access] Raw KV parse rejected: missing/invalid proto. Returning nullptr "); + return nullptr; + } + + parsed = true; + isServer = (kv.count("role") && kv.at("role") == "server"); + if (kv.count("path")) + { + path = kv.at("path"); + } + } + + if (!parsed) { return nullptr; } - const bool isServer = - (kv.count("role") && kv.at("role") == "server"); - - const std::string path = - kv.count("path") - ? kv.at("path") - : (MakeUserRuntimeDir() + "/TUNNEL_auto.sock"); - - if (isServer) + if (path.empty()) { - // For simplicity, create a new server tunnel instance for each Access(). - // The SDV framework is expected to call Access(serverCS) only once in normal cases. - auto udsServer = std::make_shared( - -1, - /*acceptConnectionRequired*/ true, - path); + tunnel = ExtractTunnelName(input); - auto tunnelServer = std::make_shared( - udsServer, - /*channelId*/ 0u); + uint32_t instanceId = 1000; + const sdv::app::IAppContext* pCtx = sdv::core::GetCore(); + if (pCtx && pCtx->GetInstanceID() != 0) + { + instanceId = pCtx->GetInstanceID(); + } - m_ServerTunnels.push_back(tunnelServer); - return static_cast(tunnelServer.get()); + path = BuildTunnelPath(MakeUserRuntimeDir(), tunnel, instanceId, static_cast(::getpid())); } - // Client: allocate raw pointer (expected to be managed by SDV framework via IObjectDestroy) - auto udsClient = std::make_shared( - -1, - /*acceptConnectionRequired*/ false, - path); + path = ClampSunPath(path); - auto* tunnelClient = - new CUnixTunnelConnection(udsClient, /*channelId*/ 0u); + std::shared_ptr transport = std::make_shared(-1, /*acceptConnectionRequired*/ isServer, path); + uint32_t chId = g_nextChannelId++; + std::shared_ptr connection = std::make_shared(transport, /*channelId*/ chId); - return static_cast(tunnelClient); + // Ignore cppcheck warning; if construction failed, an exception is expected first. + // cppcheck-suppress knownConditionTrueFalse + if (!connection) + return nullptr; + + connection->SetWatchDogRemoveCallback([this](const void* instance) + { + m_watchdog.RemoveConnection(instance); + }); + + m_watchdog.AddConnection(connection); + return static_cast(connection.get()); } #endif // defined(__unix__) \ No newline at end of file diff --git a/sdv_services/uds_unix_tunnel/channel_mgnt.h b/sdv_services/uds_unix_tunnel/channel_mgnt.h index 09484c5..8761ac0 100644 --- a/sdv_services/uds_unix_tunnel/channel_mgnt.h +++ b/sdv_services/uds_unix_tunnel/channel_mgnt.h @@ -16,43 +16,11 @@ #include #include -#include "../sdv_services/uds_unix_sockets/channel_mgnt.h" // existing UDS transport +#include "watchdog.h" +#include class CUnixTunnelConnection; -/** - * @brief Initialize WinSock on Windows (idempotent). - * - * This helper ensures WSAStartup() is called only once in the process. - * On non-Windows platforms, this is a no-op and always returns success. - * - * @return 0 on success, otherwise a WinSock error code (Windows only). - */ -inline int StartUpWinSock() -{ -#ifdef _WIN32 - static bool isInitialized = false; - if (isInitialized) - { - return 0; - } - WSADATA wsaData {}; - const int error = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (error != 0) - { - SDV_LOG_ERROR("WSAStartup failed with error: ", std::to_string(error)); - } - else - { - SDV_LOG_INFO("WSAStartup initialized"); - isInitialized = true; - } - return error; -#else - // Non-Windows: nothing to do. Return success for symmetry - return 0; -#endif -} /** * @class CUnixTunnelChannelMgnt * @brief IPC channel management class for Unix Domain Socket tunnel communication. @@ -76,8 +44,8 @@ public: // Object declarations DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) DECLARE_OBJECT_CLASS_NAME("UnixTunnelChannelControl") - DECLARE_OBJECT_CLASS_ALIAS("TunnelChannelControl") - DECLARE_DEFAULT_OBJECT_NAME("TunnelChannelControl") + DECLARE_OBJECT_CLASS_ALIAS("unix_domain_sockets_tunnel") + DECLARE_DEFAULT_OBJECT_NAME("unix_domain_sockets_tunnel") DECLARE_OBJECT_SINGLETON() /** @@ -96,6 +64,11 @@ public: */ virtual void OnShutdown() override; + /** + * @brief Last function called before destruction. Overload of sdv::CSdvObject::OnDestroy. + */ + virtual void OnDestroy() override; + /** * @brief Creates a tunnel endpoint (server side) and returns endpoint info. * @@ -126,12 +99,7 @@ private: */ static std::string MakeUserRuntimeDir(); - /** - * @brief Keeps server-side tunnel connections alive for the lifetime of the manager. - * - * This ensures that server tunnel objects are not destroyed while the manager is active. - */ - std::vector> m_ServerTunnels; + CUnixTunnelConnectionWatchDog m_watchdog; }; DEFINE_SDV_OBJECT(CUnixTunnelChannelMgnt) diff --git a/sdv_services/uds_unix_tunnel/connection.cpp b/sdv_services/uds_unix_tunnel/connection.cpp index 4d274fc..c89f879 100644 --- a/sdv_services/uds_unix_tunnel/connection.cpp +++ b/sdv_services/uds_unix_tunnel/connection.cpp @@ -28,6 +28,12 @@ CUnixTunnelConnection::CUnixTunnelConnection( // No additional initialization required; acts as a thin wrapper. } +void CUnixTunnelConnection::SetWatchDogRemoveCallback(std::function callback) +{ + std::lock_guard lock(m_WatchdogMtx); + m_WatchdogRemoveCallback = std::move(callback); +} + /** * @brief Prepends a tunnel header and forwards the data to the underlying transport. @@ -155,11 +161,38 @@ sdv::ipc::EConnectState CUnixTunnelConnection::GetConnectState() const void CUnixTunnelConnection::DestroyObject() { + bool expected = false; + if (!m_DestroyObjectCalled.compare_exchange_strong(expected, true)) + { + return; + } + + // Stop forwarding to upper layer before transport teardown. + { + std::lock_guard lock(m_CallbackMtx); + m_pUpperReceiver = nullptr; + m_pUpperEvent = nullptr; + } + + SetConnectState(sdv::ipc::EConnectState::terminating); + // Disconnect underlying transport and clear callbacks. Disconnect(); - std::lock_guard lock(m_CallbackMtx); - m_Transport.reset(); + { + std::lock_guard lock(m_CallbackMtx); + m_Transport.reset(); + } + + std::function removeCallback; + { + std::lock_guard watchdogLock(m_WatchdogMtx); + removeCallback = std::move(m_WatchdogRemoveCallback); + } + if (removeCallback) + { + removeCallback(this); + } } void CUnixTunnelConnection::ReceiveData(/*inout*/ sdv::sequence>& seqData) diff --git a/sdv_services/uds_unix_tunnel/connection.h b/sdv_services/uds_unix_tunnel/connection.h index 7d30ccb..5ebad48 100644 --- a/sdv_services/uds_unix_tunnel/connection.h +++ b/sdv_services/uds_unix_tunnel/connection.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -179,12 +180,20 @@ public: */ uint16_t GetChannelId() const noexcept { return m_ChannelId; } + /** + * @brief Register callback used to remove this connection from manager watchdog storage. + */ + void SetWatchDogRemoveCallback(std::function callback); + private: std::shared_ptr m_Transport; ///< shared physical tunnel port uint16_t m_ChannelId {0}; ///< default logical channel id sdv::ipc::IDataReceiveCallback* m_pUpperReceiver {nullptr}; ///< Callback to upper layer (data receive) sdv::ipc::IConnectEventCallback* m_pUpperEvent {nullptr}; ///< Callback to upper layer (state event) mutable std::mutex m_CallbackMtx; ///< Mutex to guard callback access + std::mutex m_WatchdogMtx; + std::function m_WatchdogRemoveCallback; + std::atomic m_DestroyObjectCalled { false }; }; #endif // UNIX_SOCKET_TUNNEL_CONNECTION_H diff --git a/sdv_services/uds_unix_tunnel/watchdog.cpp b/sdv_services/uds_unix_tunnel/watchdog.cpp new file mode 100644 index 0000000..e2dcf8f --- /dev/null +++ b/sdv_services/uds_unix_tunnel/watchdog.cpp @@ -0,0 +1,58 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#if defined(__unix__) + +#include "watchdog.h" + +void CUnixTunnelConnectionWatchDog::AddConnectionImpl(const std::shared_ptr& connection) +{ + std::lock_guard lock(m_Mutex); + m_Connections[connection.get()] = connection; +} + +void CUnixTunnelConnectionWatchDog::RemoveConnection(const void* connection) +{ + if (!connection) + { + return; + } + + std::shared_ptr removed; + { + std::lock_guard lock(m_Mutex); + auto it = m_Connections.find(connection); + if (it == m_Connections.end()) + { + return; + } + + removed = std::move(it->second); + m_Connections.erase(it); + } + + removed.reset(); +} + +void CUnixTunnelConnectionWatchDog::Clear() +{ + std::map> localConnections; + { + std::lock_guard lock(m_Mutex); + localConnections.swap(m_Connections); + } + + localConnections.clear(); +} + +#endif // defined(__unix__) diff --git a/sdv_services/uds_unix_tunnel/watchdog.h b/sdv_services/uds_unix_tunnel/watchdog.h new file mode 100644 index 0000000..1fa0fb9 --- /dev/null +++ b/sdv_services/uds_unix_tunnel/watchdog.h @@ -0,0 +1,47 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#if defined(__unix__) +#ifndef UDS_UNIX_TUNNEL_WATCHDOG_H +#define UDS_UNIX_TUNNEL_WATCHDOG_H + +#include +#include +#include + +class CUnixTunnelConnectionWatchDog +{ +public: + template + void AddConnection(const std::shared_ptr& connection) + { + if (!connection) + { + return; + } + + AddConnectionImpl(std::static_pointer_cast(connection)); + } + + void RemoveConnection(const void* connection); + void Clear(); + +private: + void AddConnectionImpl(const std::shared_ptr& connection); + + std::mutex m_Mutex; + std::map> m_Connections; +}; + +#endif // UDS_UNIX_TUNNEL_WATCHDOG_H +#endif // defined(__unix__) diff --git a/sdv_services/uds_win_sockets/CMakeLists.txt b/sdv_services/uds_win_sockets/CMakeLists.txt index ac138e3..2b4e9e9 100644 --- a/sdv_services/uds_win_sockets/CMakeLists.txt +++ b/sdv_services/uds_win_sockets/CMakeLists.txt @@ -12,31 +12,59 @@ if(WIN32) # Define project project(uds_win_sockets VERSION 1.0 LANGUAGES CXX) -# Define target -add_library(uds_win_sockets STATIC +# Build sources once and expose them in two forms: +# - uds_win_sockets: static link target for tests / other local libraries on MinGW +# - uds_win_sockets_module: runtime-loadable .sdv module for ModuleControl +set(UDS_WIN_SOCKETS_SOURCES channel_mgnt.cpp connection.cpp + watchdog.cpp ) -target_include_directories(uds_win_sockets - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} - ./include/ +add_library(uds_win_sockets STATIC + ${UDS_WIN_SOCKETS_SOURCES} +) + +add_library(uds_win_sockets_module SHARED + ${UDS_WIN_SOCKETS_SOURCES} +) + +target_include_directories(uds_win_sockets + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ./include/ +) + +target_include_directories(uds_win_sockets_module + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ./include/ ) target_link_libraries(uds_win_sockets PUBLIC - ${CMAKE_THREAD_LIBS_INIT} + ${CMAKE_THREAD_LIBS_INIT} Ws2_32.lib ) -set_target_properties(uds_win_sockets PROPERTIES PREFIX "") -set_target_properties(uds_win_sockets PROPERTIES SUFFIX ".sdv") +target_link_libraries(uds_win_sockets_module + PUBLIC + ${CMAKE_THREAD_LIBS_INIT} + Ws2_32.lib +) + +set_target_properties(uds_win_sockets_module PROPERTIES + PREFIX "" + SUFFIX ".sdv" + OUTPUT_NAME "uds_win_sockets" + ARCHIVE_OUTPUT_NAME "uds_win_sockets_module" +) # Build dependencies add_dependencies(uds_win_sockets CompileCoreIDL) +add_dependencies(uds_win_sockets_module CompileCoreIDL) # Appending the service in the service list -set(SDV_Service_List ${SDV_Service_List} uds_win_sockets PARENT_SCOPE) +set(SDV_Service_List ${SDV_Service_List} uds_win_sockets_module PARENT_SCOPE) endif() diff --git a/sdv_services/uds_win_sockets/channel_mgnt.cpp b/sdv_services/uds_win_sockets/channel_mgnt.cpp index 6e42b9c..6c5fe05 100644 --- a/sdv_services/uds_win_sockets/channel_mgnt.cpp +++ b/sdv_services/uds_win_sockets/channel_mgnt.cpp @@ -11,15 +11,20 @@ * Denisa Ros - initial API and implementation ********************************************************************************/ #ifdef _WIN32 - #include "channel_mgnt.h" #include "connection.h" -#include "../../global/base64.h" -#include -#include - +#include #include +#include +#include +#include + +#include "../../global/base64.h" +#include +#include +#include +#include #pragma push_macro("interface") #undef interface @@ -44,53 +49,23 @@ namespace { -/** - * @brief Parse a UDS connect/config string and extract the path - * - * Expected format (substring-based, not strict): - * "proto=uds;path=;" - * - * Behavior: - * - If "proto=uds" is missing -> returns false (not a UDS config) - * - If "path=" is missing -> returns true and outPath is cleared - * - If "path=" is present -> extracts the substring until ';' or end - * - * @param cs Input configuration / connect string - * @param outPath Output: extracted path (possibly empty) - * @return true if this looks like a UDS string, false otherwise - */ -static bool ParseUdsPath(const std::string& cs, std::string& outPath) +static bool EnsureWSAInitialized() { - constexpr const char* protoKey = "proto=uds"; - constexpr const char* pathKey = "path="; + static std::once_flag s_once; + static bool s_ok = false; - // Must contain "proto=uds" to be considered UDS - if (cs.find(protoKey) == std::string::npos) + std::call_once(s_once, []() { - return false; - } + WSADATA wsa{}; + const int rc = WSAStartup(MAKEWORD(2, 2), &wsa); + s_ok = (rc == 0); + if (!s_ok) + { + SDV_LOG_ERROR("[AF_UNIX] WSAStartup failed, rc=", rc); + } + }); - const auto p = cs.find(pathKey); - if (p == std::string::npos) - { - // No path given, but proto=uds is present -> use default later - outPath.clear(); - return true; - } - - const auto start = p + std::strlen(pathKey); - const auto end = cs.find(';', start); - - if (end == std::string::npos) - { - outPath = cs.substr(start); - } - else - { - outPath = cs.substr(start, end - start); - } - - return true; + return s_ok; } /** @@ -105,6 +80,7 @@ static bool ParseUdsPath(const std::string& cs, std::string& outPath) * * @return Expanded string, or the original input on failure */ + static std::string ExpandEnvVars(const std::string& in) { if (in.find('%') == std::string::npos) @@ -112,9 +88,8 @@ static std::string ExpandEnvVars(const std::string& in) return in; } - char buf[4096] = {}; - DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast(sizeof(buf))); - + char buf[4096] = {}; + DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast(sizeof(buf))); if (n > 0 && n < sizeof(buf)) { return std::string(buf); @@ -135,19 +110,181 @@ static std::string ExpandEnvVars(const std::string& in) * * @return A safe pathname guaranteed to fit into sun_path */ + static std::string ClampUdsPath(const std::string& p) { - SOCKADDR_UN tmp{}; + SOCKADDR_UN tmp{}; constexpr auto kMax = sizeof(tmp.sun_path) - 1; - if (p.size() <= kMax) { return p; } - return p.substr(0, kMax); } +/*static std::string BuildFinalUdsPath(const std::string& rawPath) +{ + std::string full = ExpandEnvVars(rawPath); + + auto pos = full.find_last_of("\\/"); + if (pos != std::string::npos) + { + const std::string dir = full.substr(0, pos); + CreateDirectoryA(dir.c_str(), nullptr); + } + + return ClampUdsPath(full); +}*/ + + + +/** + * @brief Parse a UDS connect/config string and extract the path + * + * Expected format (substring-based, not strict): + * "proto=uds;path=;" + * + * Behavior: + * - If "proto=uds" is missing -> returns false (not a UDS config) + * - If "path=" is missing -> returns true and outPath is cleared + * - If "path=" is present -> extracts the substring until ';' or end + * + * @param cs Input configuration / connect string + * @param outPath Output: extracted path (possibly empty) + * @return true if this looks like a UDS string, false otherwise + */ +static bool ParseUdsPath(const std::string& cs, std::string& outPath) +{ + constexpr const char* protoKey = "proto=uds"; + constexpr const char* pathKey = "path="; + + // Strict raw connect string only + if (cs.rfind(protoKey, 0) != 0) + { + return false; + } + + const auto p = cs.find(pathKey); + if (p == std::string::npos) + { + outPath.clear(); + return true; + } + + const auto start = p + std::strlen(pathKey); + const auto end = cs.find(';', start); + if (end == std::string::npos) + { + outPath = cs.substr(start); + } + else + { + outPath = cs.substr(start, end - start); + } + + return true; +} + +static std::string SanitizeUdsName(std::string name) +{ + for (char& ch : name) + { + const bool isAlphaNum = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'); + if (!isAlphaNum && ch != '_' && ch != '-') + { + ch = '_'; + } + } + + if (name.empty()) + { + name = "sdv"; + } + + return name; +} + +/** + * @brief Build a named UDS raw path using the channel name + * + * @param[in] channelName The name of the channel + * + * @return A raw UDS path suitable for further processing + */ +static std::string BuildNamedUdsRawPath(const std::string& channelName) +{ + return "%LOCALAPPDATA%/sdv/" + SanitizeUdsName(channelName) + ".sock"; +} + +/** + * @brief Extract a UDS connect string from either raw UDS format or Provider TOML. + * + * Supported inputs: + * - "proto=uds;path=<...>;" + * - [Provider]\nName="unix_domain_sockets"\nConnectString="proto=uds;path=<...>;" + */ +static bool ExtractUdsConnectString(const std::string& in, + std::string& outUdsConnectString) +{ + std::string path; + + // Case 1: strict raw UDS connect string + if (ParseUdsPath(in, path)) + { + outUdsConnectString = in; + return true; + } + + // Do not run the TOML parser on obviously non-TOML garbage + //if (!LooksLikeToml(in)) + //{ + // return false; + //} + + // Case 2: structured TOML + sdv::toml::CTOMLParser parser(in); + if (!parser.IsValid()) + { + return false; + } + + const std::string providerName = parser.GetDirect("Provider.Name").GetValue(); + if (!providerName.empty() && + providerName != "unix_domain_sockets" && + providerName != "WinSocketsChannelControl") + { + return false; + } + + const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue(); + if (!nested.empty()) + { + if (ParseUdsPath(nested, path)) + { + outUdsConnectString = nested; + return true; + } + return false; + } + + const std::string cfgPath = parser.GetDirect("IpcChannel.Path").GetValue(); + if (!cfgPath.empty()) + { + outUdsConnectString = "proto=uds;path=" + cfgPath + ";"; + return true; + } + + const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue(); + if (!cfgName.empty()) + { + outUdsConnectString = "proto=uds;path=" + BuildNamedUdsRawPath(cfgName) + ";"; + return true; + } + + return false; +} + + /** * @brief Normalize a UDS path for display/logging purposes * @@ -166,20 +303,18 @@ static std::string ClampUdsPath(const std::string& p) static std::string NormalizeUdsPathForWindows(const std::string& raw) { std::string p = ExpandEnvVars(raw); - - const size_t pos = p.find_last_of("/\\"); - std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1); - + const size_t pos = p.find_last_of("/\\"); + std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1); if (base.empty()) { base = "sdv.sock"; } SDV_LOG_INFO("[AF_UNIX] Normalize raw='", raw, "' -> base='", base, "'"); - return ClampUdsPath(base); } + /** * @brief Build a short absolute Win32 path suitable for AF_UNIX `sun_path` * @@ -224,6 +359,41 @@ static std::string MakeShortWinUdsPath(const std::string& raw) return ClampUdsPath(full); } +/** + * @brief Build the default UDS path using the current SDV instance ID. + * + * Using a single global socket filename makes independently running test + * processes trample each other when the build executes multiple post-build + * tests in parallel. Namespace the implicit endpoint per instance instead. + */ +static std::string GetDefaultUdsRawPath() +{ + uint32_t instanceId = 1000u; + const sdv::app::IAppContext* pAppContext = sdv::core::GetCore(); + if (pAppContext && pAppContext->GetInstanceID() != 0u) + { + instanceId = pAppContext->GetInstanceID(); + } + + return "%LOCALAPPDATA%/sdv/vapi_" + std::to_string(instanceId) + ".sock"; +} + +static std::string GetUniqueEndpointUdsRawPath() +{ + static std::atomic nextEndpointId { 0u }; + + uint32_t instanceId = 1000u; + const sdv::app::IAppContext* pAppContext = sdv::core::GetCore(); + if (pAppContext && pAppContext->GetInstanceID() != 0u) + { + instanceId = pAppContext->GetInstanceID(); + } + + const uint32_t endpointId = nextEndpointId.fetch_add(1u, std::memory_order_relaxed); + return "%LOCALAPPDATA%/sdv/vapi_" + std::to_string(instanceId) + "_" + + std::to_string(static_cast(GetCurrentProcessId())) + "_" + std::to_string(endpointId) + ".sock"; +} + /** * @brief Create a listening AF_UNIX socket on Windows * @@ -239,6 +409,9 @@ static std::string MakeShortWinUdsPath(const std::string& raw) */ static SOCKET CreateUnixListenSocket(const std::string& rawPath) { + if (!EnsureWSAInitialized()) + return INVALID_SOCKET; + SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { @@ -285,118 +458,99 @@ static SOCKET CreateUnixListenSocket(const std::string& rawPath) return s; } -/** - * @brief Connect to a Windows AF_UNIX server socket with retry logic - * - * Repeatedly attempts to connect to the server's UDS path until either: - * - connection succeeds, or - * - total timeout is exceeded - * - * On each attempt: - * - a new socket() is created - * - connect() is attempted - * - on failure the socket is closed and retried - * - * This mirrors Linux AF_UNIX behavior where the client waits for the - * server's socket file to appear/become ready - * - * @param[in] rawPath Raw UDS path from configuration - * @param[in] totalTimeoutMs Maximum total wait time in milliseconds - * @param[in] retryDelayMs Delay between retries in milliseconds - * - * @return Connected SOCKET on success, INVALID_SOCKET on timeout or error - */ -static SOCKET ConnectUnixSocket( - const std::string& rawPath, - uint32_t totalTimeoutMs, - uint32_t retryDelayMs) +/*static SOCKET ConnectUnixSocket(const std::string& rawPath, + uint32_t totalTimeoutMs, + uint32_t retryDelayMs) { - const std::string udsPath = MakeShortWinUdsPath(rawPath); + if (!EnsureWSAInitialized()) + { + return INVALID_SOCKET; + } + + const std::string udsPath = BuildFinalUdsPath(rawPath); SOCKADDR_UN addr{}; addr.sun_family = AF_UNIX; strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str()); const int addrlen = static_cast( - offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1 - ); + offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1); - const auto deadline = std::chrono::steady_clock::now() + - std::chrono::milliseconds(totalTimeoutMs); + auto deadline = + std::chrono::steady_clock::now() + std::chrono::milliseconds(totalTimeoutMs); while (true) { SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { - SDV_LOG_ERROR("[AF_UNIX] socket() FAIL (client), WSA=", WSAGetLastError()); + SDV_LOG_ERROR("[AF_UNIX] socket FAIL (client), WSA=", WSAGetLastError()); return INVALID_SOCKET; } if (connect(s, reinterpret_cast(&addr), addrlen) == 0) { - SDV_LOG_INFO("[AF_UNIX] connect OK (pathname), path='", udsPath, "'"); + SDV_LOG_INFO("[AF_UNIX] connect OK: ", udsPath); return s; } - int lastError = WSAGetLastError(); + const int err = WSAGetLastError(); closesocket(s); if (std::chrono::steady_clock::now() >= deadline) { - SDV_LOG_ERROR( - "[AF_UNIX] connect TIMEOUT (pathname), last WSA=", - lastError, ", path='", udsPath, "'" - ); + SDV_LOG_ERROR("[AF_UNIX] connect TIMEOUT, WSA=", err, ", path=", udsPath); return INVALID_SOCKET; } std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs)); } -} +}*/ } // anonymous namespace bool CSocketsChannelMgnt::OnInitialize() { - return true; -} - -void CSocketsChannelMgnt::OnServerClosed(const std::string& udsPath, CWinsockConnection* ptr) -{ - std::lock_guard lock(m_udsMtx); - - auto it = m_udsServers.find(udsPath); - if (it != m_udsServers.end() && it->second.get() == ptr) - { - // Remove the server entry only if it matches the pointer we know - m_udsServers.erase(it); - } - - // Mark this UDS path as no longer claimed - m_udsServerClaimed.erase(udsPath); + return EnsureWSAInitialized(); } void CSocketsChannelMgnt::OnShutdown() -{} +{ +} + +void CSocketsChannelMgnt::OnDestroy() +{ + m_watchdog.Clear(); +} sdv::ipc::SChannelEndpoint CSocketsChannelMgnt::CreateEndpoint(const sdv::u8string& cfgStr) { - // Ensure WinSock is initialized on Windows - if (StartUpWinSock() != 0) - { - // If WinSock cannot be initialized, we cannot create an endpoint - return {}; - } - // Parse UDS path from config. If proto!=uds, we still default to UDS std::string udsRaw; bool udsRequested = ParseUdsPath(cfgStr, udsRaw); if (!udsRequested || udsRaw.empty()) { - // Default path if not provided or not UDS-specific - udsRaw = "%LOCALAPPDATA%/sdv/vapi.sock"; + sdv::toml::CTOMLParser parser(cfgStr); + const std::string cfgPath = parser.GetDirect("IpcChannel.Path").GetValue(); + const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue(); + + if (!cfgPath.empty()) + { + udsRaw = cfgPath; + } + else if (!cfgName.empty()) + { + udsRaw = BuildNamedUdsRawPath(cfgName); + } + else if (cfgStr.empty()) + { + udsRaw = GetUniqueEndpointUdsRawPath(); + } + else + { + udsRaw = GetDefaultUdsRawPath(); + } } std::string udsPath = NormalizeUdsPathForWindows(udsRaw); @@ -411,75 +565,81 @@ sdv::ipc::SChannelEndpoint CSocketsChannelMgnt::CreateEndpoint(const sdv::u8stri // Server-side CWinsockConnection, it will accept() a client on first use auto server = std::make_shared(listenSocket, /*acceptRequired*/ true); - + // Ignore cppcheck warning; if construction failed, an exception is expected first. + // cppcheck-suppress knownConditionTrueFalse + if (!server) { - std::lock_guard lock(m_udsMtx); - m_udsServers[udsPath] = server; - m_udsServerClaimed.erase(udsPath); + return {}; } + server->SetWatchDogRemoveCallback([this](const void* connection) + { + m_watchdog.RemoveConnection(connection); + }); + m_watchdog.AddConnection(server); sdv::ipc::SChannelEndpoint ep{}; - ep.pConnection = static_cast(server.get()); - ep.ssConnectString = "proto=uds;path=" + udsPath + ";"; + ep.pConnection = static_cast(server.get()); + + // Keep compatibility with CCommunicationControl::CreateClientConnection, + // which expects a Provider TOML section with Provider.Name. + const std::string udsConnectString = "proto=uds;path=" + udsPath + ";"; + ep.ssConnectString = udsConnectString; return ep; } sdv::IInterfaceAccess* CSocketsChannelMgnt::Access(const sdv::u8string& cs) { - // Ensure WinSock is initialized - if (StartUpWinSock() != 0) + std::string udsConnectString; + if (!ExtractUdsConnectString(cs, udsConnectString)) { + // Not a UDS connect string / provider description return nullptr; } std::string udsRaw; - if (!ParseUdsPath(cs, udsRaw)) - { - // Not a UDS connect string - return nullptr; - } + ParseUdsPath(udsConnectString, udsRaw); if (udsRaw.empty()) { - udsRaw = "%LOCALAPPDATA%/sdv/vapi.sock"; + udsRaw = GetDefaultUdsRawPath(); } std::string udsPath = NormalizeUdsPathForWindows(udsRaw); SDV_LOG_INFO("[AF_UNIX] Access udsPath=", udsPath); + const bool isServer = (udsConnectString.find("role=server") != std::string::npos); + std::shared_ptr connection; + if (isServer) { - std::lock_guard lock(m_udsMtx); - - auto it = m_udsServers.find(udsPath); - if (it != m_udsServers.end() && it->second != nullptr) + SOCKET listenSocket = CreateUnixListenSocket(udsPath); + if (listenSocket == INVALID_SOCKET) { - // Return the server-side object only once for this UDS path - if (!m_udsServerClaimed.count(udsPath)) - { - m_udsServerClaimed.insert(udsPath); - SDV_LOG_INFO("[AF_UNIX] Access -> RETURN SERVER for ", udsPath); - return it->second.get(); // server object (acceptRequired=true) - } - // Otherwise, later calls will create a client socket below + return nullptr; } + connection = std::make_shared(listenSocket, /*acceptRequired*/ true); + } + else + { + // Client-side endpoint object. The actual socket connect is deferred to AsyncConnect, + // matching the semantic contract used by ipc_com and shared memory. + connection = std::make_shared(udsPath); } - // CLIENT: create a socket connected to the same udsPath - SOCKET s = ConnectUnixSocket(udsPath, - /*totalTimeoutMs*/ 5000, - /*retryDelayMs*/ 50); - - if (s == INVALID_SOCKET) + // Ignore cppcheck warning; if construction failed, an exception is expected first. + // cppcheck-suppress knownConditionTrueFalse + if (!connection) { return nullptr; } - SDV_LOG_INFO("[AF_UNIX] Access -> CREATE CLIENT for ", udsPath); + connection->SetWatchDogRemoveCallback([this](const void* instance) + { + m_watchdog.RemoveConnection(instance); + }); - // Client-side connection object (acceptRequired=false) - // Ownership is transferred to the caller (VAPI runtime) - return new CWinsockConnection(s, /*acceptRequired*/ false); + m_watchdog.AddConnection(connection); + return static_cast(connection.get()); } #endif \ No newline at end of file diff --git a/sdv_services/uds_win_sockets/channel_mgnt.h b/sdv_services/uds_win_sockets/channel_mgnt.h index 81ab015..dabdb9f 100644 --- a/sdv_services/uds_win_sockets/channel_mgnt.h +++ b/sdv_services/uds_win_sockets/channel_mgnt.h @@ -18,16 +18,14 @@ #include #include #include "connection.h" +#include "watchdog.h" + +#include -#include -#include -#include -#include #include #ifdef _WIN32 // Winsock headers are required for SOCKET / AF_UNIX / WSAStartup -// NOTE: The actual initialization is done via StartUpWinSock() # include #endif @@ -77,40 +75,6 @@ struct CAddrInfo addrinfo* AddressInfo { nullptr }; }; -/** - * @brief Initialize WinSock on Windows (idempotent) - * - * This helper ensures WSAStartup() is called only once in the process - * - * @return 0 on success, otherwise a WinSock error code - */ -inline int StartUpWinSock() -{ -#ifdef _WIN32 - static bool isInitialized = false; - if (isInitialized) - { - return 0; - } - - WSADATA wsaData {}; - const int error = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (error != 0) - { - SDV_LOG_ERROR("WSAStartup failed with error: ", std::to_string(error)); - } - else - { - SDV_LOG_INFO("WSAStartup initialized"); - isInitialized = true; - } - return error; -#else - // Non-Windows: nothing to do. Return success for symmetry - return 0; -#endif -} - /** * @brief Simple pair of sockets used to connect two child processes * @@ -147,8 +111,8 @@ public: // Object declarations DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) DECLARE_OBJECT_CLASS_NAME("WinSocketsChannelControl") - DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl") - DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl") + DECLARE_OBJECT_CLASS_ALIAS("unix_domain_sockets") + DECLARE_DEFAULT_OBJECT_NAME("unix_domain_sockets") DECLARE_OBJECT_SINGLETON() virtual ~CSocketsChannelMgnt() = default; @@ -164,6 +128,11 @@ public: */ virtual void OnShutdown() override; + /** + * @brief Last function called before destruction. Overload of sdv::CSdvObject::OnDestroy. + */ + virtual void OnDestroy() override; + /** * @brief Create an IPC endpoint and return its connection information * @@ -208,29 +177,8 @@ public: */ sdv::IInterfaceAccess* Access(const sdv::u8string& ssConnectString) override; - /** - * @brief Called by a CWinsockConnection instance when the server side is closed - * - * Used to clean up internal registries for a given UDS path - * - * @param ptr Pointer to the CWinsockConnection instance that was closed - */ - void OnServerClosed(const std::string& udsPath, CWinsockConnection* ptr); - private: - /// @brief Registry of AF_UNIX server connections keyed by normalized UDS path - std::map> m_udsServers; - - /** - * @brief Set of UDS paths that already returned their server-side - * connection once via Access() - * - * This prevents returning the same server object multiple times - */ - std::unordered_set m_udsServerClaimed; - - /// @brief Mutex protecting m_udsServers and m_udsServerClaimed - std::mutex m_udsMtx; + CWinSocketsConnectionWatchDog m_watchdog; }; // SDV object factory macro diff --git a/sdv_services/uds_win_sockets/connection.cpp b/sdv_services/uds_win_sockets/connection.cpp index 1e73b92..7f36a89 100644 --- a/sdv_services/uds_win_sockets/connection.cpp +++ b/sdv_services/uds_win_sockets/connection.cpp @@ -17,11 +17,92 @@ #include #include #include +#include + +namespace +{ +static std::string ExpandEnvVarsLocal(const std::string& in) +{ + if (in.find('%') == std::string::npos) + { + return in; + } + + char buf[4096] = {}; + DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast(sizeof(buf))); + if (n > 0 && n < sizeof(buf)) + { + return std::string(buf); + } + + return in; +} + +static std::string ClampUdsPathLocal(const std::string& path) +{ + sockaddr_un tmp{}; + constexpr auto kMax = sizeof(tmp.sun_path) - 1; + return path.size() <= kMax ? path : path.substr(0, kMax); +} + +static std::string MakeShortWinUdsPathLocal(const std::string& raw) +{ + std::string path = ExpandEnvVarsLocal(raw); + const size_t pos = path.find_last_of("/\\"); + std::string base = (pos == std::string::npos) ? path : path.substr(pos + 1); + + if (base.empty()) + { + base = "sdv.sock"; + } + + std::string dir = ExpandEnvVarsLocal("%TEMP%\\sdv\\"); + CreateDirectoryA(dir.c_str(), nullptr); + return ClampUdsPathLocal(dir + base); +} + +static SOCKET ConnectUnixSocketLocal(const std::string& rawPath, uint32_t totalTimeoutMs, uint32_t retryDelayMs) +{ + const std::string udsPath = MakeShortWinUdsPathLocal(rawPath); + + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str()); + + const int addrlen = static_cast(offsetof(sockaddr_un, sun_path) + std::strlen(addr.sun_path) + 1); + const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(totalTimeoutMs); + + while (true) + { + SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) + { + return INVALID_SOCKET; + } + + if (connect(s, reinterpret_cast(&addr), addrlen) == 0) + { + return s; + } + + const int lastError = WSAGetLastError(); + closesocket(s); + + if (std::chrono::steady_clock::now() >= deadline) + { + SDV_LOG_ERROR("[AF_UNIX] lazy connect TIMEOUT, last WSA=", lastError, ", path='", udsPath, "'"); + return INVALID_SOCKET; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs)); + } +} +} CWinsockConnection::CWinsockConnection(unsigned long long preconfiguredSocket, bool acceptConnectionRequired) : m_ConnectionState(sdv::ipc::EConnectState::uninitialized) , m_AcceptConnectionRequired(acceptConnectionRequired) - , m_CancelWait(false) + , m_UdsPath() { // Initialize legacy buffers with zero (kept for potential compatibility) std::fill(std::begin(m_SendBuffer), std::end(m_SendBuffer), '\0'); @@ -41,6 +122,17 @@ CWinsockConnection::CWinsockConnection(unsigned long long preconfiguredSocket, b } } +CWinsockConnection::CWinsockConnection(const std::string& udsPath) + : m_ConnectionState(sdv::ipc::EConnectState::uninitialized) + , m_AcceptConnectionRequired(false) + , m_UdsPath(udsPath) +{ + std::fill(std::begin(m_SendBuffer), std::end(m_SendBuffer), '\0'); + std::fill(std::begin(m_ReceiveBuffer), std::end(m_ReceiveBuffer), '\0'); + m_ListenSocket = INVALID_SOCKET; + m_ConnectionSocket = INVALID_SOCKET; +} + /*CWinsockConnection::CWinsockConnection() : m_ConnectionState(sdv::ipc::EConnectState::uninitialized) , m_AcceptConnectionRequired(false) @@ -65,28 +157,47 @@ CWinsockConnection::~CWinsockConnection() } } +void CWinsockConnection::SetWatchDogRemoveCallback(std::function callback) +{ + std::lock_guard lock(m_WatchdogMtx); + m_WatchdogRemoveCallback = std::move(callback); +} + void CWinsockConnection::SetConnectState(sdv::ipc::EConnectState state) { + sdv::ipc::IConnectEventCallback* pEventLocal = nullptr; + { std::lock_guard lk(m_MtxConnect); m_ConnectionState.store(state, std::memory_order_release); + + // Main receiver has priority over registered state listener + if (m_pMainEvent) + { + pEventLocal = m_pMainEvent; + } + else + { + pEventLocal = m_pRegisteredEvent; + } } // Wake up any waiter m_CvConnect.notify_all(); // Notify event callback if registered - if (m_pEvent) + if (pEventLocal) { try { - m_pEvent->SetConnectState(state); + pEventLocal->SetConnectState(state); } catch (...) { // Ignore callbacks throwing exceptions } } + } int32_t CWinsockConnection::Send(const char* data, int32_t dataLength) @@ -177,7 +288,7 @@ bool CWinsockConnection::SendData(/*inout*/ sdv::sequence> (kMaxUdsPacketSize - static_cast(sizeof(SFragmentedMsgHdr))) : 0; - if (maxPayloadFrag == 0) + if constexpr (maxPayloadFrag == 0) { return false; } @@ -318,9 +429,18 @@ SOCKET CWinsockConnection::AcceptConnection() tv.tv_usec = 50 * 1000; // 50 ms const int sr = ::select(0, &rfds, nullptr, nullptr, &tv); + if (sr == SOCKET_ERROR) { - SDV_LOG_ERROR("[AF_UNIX] select(listen) FAIL, WSA=", WSAGetLastError()); + const int err = WSAGetLastError(); + + if (m_StopConnectThread.load() || m_ListenSocket == INVALID_SOCKET) + { + SDV_LOG_INFO("[AF_UNIX] select(listen) canceled during shutdown, WSA=", err); + return INVALID_SOCKET; + } + + SDV_LOG_ERROR("[AF_UNIX] select(listen) FAIL, WSA=", err); SetConnectState(sdv::ipc::EConnectState::connection_error); return INVALID_SOCKET; } @@ -331,9 +451,17 @@ SOCKET CWinsockConnection::AcceptConnection() } SOCKET c = ::accept(m_ListenSocket, nullptr, nullptr); + if (c == INVALID_SOCKET) { const int err = WSAGetLastError(); + + if (m_StopConnectThread.load() || m_ListenSocket == INVALID_SOCKET || err == WSAENOTSOCK) + { + SDV_LOG_INFO("[AF_UNIX] accept canceled during shutdown, WSA=", err); + return INVALID_SOCKET; + } + if (err == WSAEINTR || err == WSAEWOULDBLOCK) { continue; @@ -355,23 +483,53 @@ SOCKET CWinsockConnection::AcceptConnection() bool CWinsockConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) { + auto expectedState = m_ConnectionState.load(std::memory_order_acquire); + while (true) + { + if (expectedState == sdv::ipc::EConnectState::connected) + { + return true; + } + + if (expectedState == sdv::ipc::EConnectState::initializing) + { + SDV_LOG_WARNING("[AF_UNIX] AsyncConnect ignored: connect worker already running"); + return false; + } + + if (m_ConnectionState.compare_exchange_weak( + expectedState, + sdv::ipc::EConnectState::initializing, + std::memory_order_acq_rel, + std::memory_order_acquire)) + { + break; + } + } + // Store callbacks - m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); - m_pEvent = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); + auto* pReceiverIfc = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); + auto* pEventIfc = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); + { + std::lock_guard lk(m_MtxConnect); + m_pReceiver = pReceiverIfc; + m_pMainEvent = pEventIfc; + } // Reset stop flags m_StopReceiveThread.store(false); m_StopConnectThread.store(false); m_CancelWait.store(false); - // Join old threads if any + // Join old workers only after they completed. + // Joining an active worker here can block forever when a previous connect is still pending. if (m_ReceiveThread.joinable()) m_ReceiveThread.join(); if (m_ConnectThread.joinable()) m_ConnectThread.join(); // Start the connect worker - m_ConnectThread = std::thread(&CWinsockConnection::ConnectWorker, this); + m_ConnectThread = sdv::core::secure_thread(&CWinsockConnection::ConnectWorker, this); return true; } @@ -428,11 +586,16 @@ void CWinsockConnection::Disconnect() uint64_t CWinsockConnection::RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) { + auto* pEventIfc = sdv::TInterfaceAccessPtr(pEventCallback).GetInterface(); + // Extract IConnectEventCallback interface - m_pEvent = sdv::TInterfaceAccessPtr(pEventCallback).GetInterface(); + { + std::lock_guard lk(m_MtxConnect); + m_pRegisteredEvent = pEventIfc; + } // Only one callback is supported; cookie 1 = valid - return (m_pEvent != nullptr) ? 1ULL : 0ULL; + return (pEventIfc != nullptr) ? 1ULL : 0ULL; } void CWinsockConnection::UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) @@ -440,7 +603,9 @@ void CWinsockConnection::UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) // Only one callback supported -> cookie value is 1 if (uiCookie == 1ULL) { - m_pEvent = nullptr; + std::lock_guard lk(m_MtxConnect); + m_pMainEvent = nullptr; + m_pRegisteredEvent = nullptr; } } @@ -451,11 +616,38 @@ sdv::ipc::EConnectState CWinsockConnection::GetConnectState() const void CWinsockConnection::DestroyObject() { - m_StopReceiveThread = true; - m_StopConnectThread = true; - StopThreadsAndCloseSockets(); - m_ConnectionState = sdv::ipc::EConnectState::disconnected; + bool expected = false; + if (!m_DestroyObjectCalled.compare_exchange_strong(expected, true)) + { + return; + } + + m_StopReceiveThread.store(true); + m_StopConnectThread.store(true); + + // Optional: notify terminating before releasing callbacks + SetConnectState(sdv::ipc::EConnectState::terminating); + + Disconnect(); + + // Release callbacks only during final teardown + { + std::lock_guard lk(m_MtxConnect); + m_pReceiver = nullptr; + m_pMainEvent = nullptr; + m_pRegisteredEvent = nullptr; + } + + std::function removeCallback; + { + std::lock_guard lock(m_WatchdogMtx); + removeCallback = std::move(m_WatchdogRemoveCallback); + } + if (removeCallback) + { + removeCallback(this); + } } void CWinsockConnection::ConnectWorker() @@ -482,16 +674,6 @@ void CWinsockConnection::ConnectWorker() if (c == INVALID_SOCKET) { - if (m_pEvent) - { - try - { - m_pEvent->SetConnectState(m_ConnectionState); - } - catch (...) - { - } - } return; } @@ -505,8 +687,22 @@ void CWinsockConnection::ConnectWorker() // CLIENT SIDE if (m_ConnectionSocket == INVALID_SOCKET) { - SetConnectState(sdv::ipc::EConnectState::connection_error); - return; + SetConnectState(sdv::ipc::EConnectState::initializing); + + if (m_UdsPath.empty()) + { + SetConnectState(sdv::ipc::EConnectState::connection_error); + return; + } + + SOCKET s = ConnectUnixSocketLocal(m_UdsPath, 5000, 50); + if (s == INVALID_SOCKET) + { + SetConnectState(sdv::ipc::EConnectState::connection_error); + return; + } + + m_ConnectionSocket = s; } } @@ -528,7 +724,7 @@ void CWinsockConnection::StartReceiveThread_Unsafe() } m_StopReceiveThread.store(false); - m_ReceiveThread = std::thread(&CWinsockConnection::ReceiveMessages, this); + m_ReceiveThread = sdv::core::secure_thread(&CWinsockConnection::ReceiveMessages, this); } void CWinsockConnection::StopThreadsAndCloseSockets() @@ -583,6 +779,7 @@ void CWinsockConnection::StopThreadsAndCloseSockets() SDV_LOG_INFO("[AF_UNIX] StopThreadsAndCloseSockets: closing listen=%llu conn=%llu", static_cast(l), static_cast(s)); + } bool CWinsockConnection::ReadNumberOfBytes(char* buffer, uint32_t length) @@ -608,7 +805,7 @@ bool CWinsockConnection::ReadNumberOfBytes(char* buffer, uint32_t length) continue; } - SDV_LOG_WARNING("[UDS][RX] recv() error: ", std::strerror(err)); + SDV_LOG_WARNING("[UDS][RX] recv() FAIL, WSA=", err); return false; } @@ -742,8 +939,13 @@ bool CWinsockConnection::ReadDataChunk(const CMessage& message, uint32_t offset, #if ENABLE_DECOUPLING > 0 // optional queueing path... #else - if (m_pReceiver) - m_pReceiver->ReceiveData(dataCtx.seqDataChunks); + sdv::ipc::IDataReceiveCallback* pReceiverLocal = nullptr; + { + std::lock_guard lk(m_MtxConnect); + pReceiverLocal = m_pReceiver; + } + if (pReceiverLocal) + pReceiverLocal->ReceiveData(dataCtx.seqDataChunks); dataCtx = SDataContext(); // reset context #endif break; diff --git a/sdv_services/uds_win_sockets/connection.h b/sdv_services/uds_win_sockets/connection.h index 87a2064..9c29275 100644 --- a/sdv_services/uds_win_sockets/connection.h +++ b/sdv_services/uds_win_sockets/connection.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include #ifdef _WIN32 # include @@ -70,11 +72,12 @@ struct SMsgHeader * - sdv::ipc::IConnect : async connect / wait / state / events * - sdv::IObjectDestroy : explicit destruction hook for SDV runtime */ -class CWinsockConnection - : public sdv::IInterfaceAccess - , public sdv::ipc::IDataSend - , public sdv::ipc::IConnect - , public sdv::IObjectDestroy +class CWinsockConnection : + //public sdv::CSharedLifetimeControlImpl, public sdv::ipc::IDataSend, public sdv::ipc::IConnect + public sdv::IInterfaceAccess, + public sdv::ipc::IDataSend, + public sdv::ipc::IConnect, + public sdv::IObjectDestroy { public: /** @@ -89,6 +92,12 @@ public: */ CWinsockConnection(unsigned long long preconfiguredSocket, bool acceptConnectionRequired); + /** + * @brief Create a client endpoint that connects lazily in AsyncConnect. + * @param[in] udsPath Normalized path for AF_UNIX connection. + */ + explicit CWinsockConnection(const std::string& udsPath); + /** * @brief Virtual destructor needed for "delete this;" */ @@ -98,6 +107,7 @@ public: SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend) SDV_INTERFACE_ENTRY(sdv::ipc::IConnect) SDV_INTERFACE_ENTRY(sdv::IObjectDestroy) + //SDV_INTERFACE_CHAIN_BASE(sdv::CSharedLifetimeControlImpl) END_SDV_INTERFACE_MAP() /** @@ -188,23 +198,28 @@ public: */ void DestroyObject() override; + /** @brief Register callback used to remove this connection from manager watchdog storage. */ + void SetWatchDogRemoveCallback(std::function callback); + private: std::mutex m_MtxConnect; std::condition_variable m_CvConnect; - std::thread m_ReceiveThread; ///< Thread which receives data from the socket - std::thread m_ConnectThread; + sdv::core::secure_thread m_ReceiveThread; ///< Thread which receives data from the socket + sdv::core::secure_thread m_ConnectThread; std::atomic m_StopReceiveThread{false}; ///< bool variable to stop thread std::atomic m_StopConnectThread{false}; std::atomic m_ConnectionState; ///< the state of the connection - sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages if available - sdv::ipc::IConnectEventCallback* m_pEvent = nullptr; ///< Event receiver + sdv::ipc::IDataReceiveCallback* m_pReceiver{nullptr}; ///< Receiver to pass the messages if available + sdv::ipc::IConnectEventCallback* m_pMainEvent{nullptr}; ///< Event receiver + sdv::ipc::IConnectEventCallback* m_pRegisteredEvent{nullptr}; bool m_AcceptConnectionRequired; ///< if true connection has to be accepted before receive thread can be started mutable std::recursive_mutex m_SendMutex; ///< Synchronize all packages to be send SOCKET m_ListenSocket{INVALID_SOCKET}; ///< Server-side listening socket SOCKET m_ConnectionSocket{INVALID_SOCKET}; ///< Active connected socket (client <-> server) + std::string m_UdsPath; ///< Client-side AF_UNIX path (lazy connect) static constexpr uint32_t m_SendMessageSize{ 1024 }; ///< size for the message to be send static constexpr uint32_t m_SendBufferSize = sizeof(SMsgHeader) + m_SendMessageSize; ///< Initial size of the send buffer @@ -213,6 +228,9 @@ private: uint32_t m_ReceiveBufferLength = sizeof(SMsgHeader); ///< receive buffer length std::atomic m_CancelWait{false}; + std::mutex m_WatchdogMtx; + std::function m_WatchdogRemoveCallback; + std::atomic m_DestroyObjectCalled{false}; /// @brief Server accept loop / client connect confirmation void ConnectWorker(); diff --git a/sdv_services/uds_win_sockets/watchdog.cpp b/sdv_services/uds_win_sockets/watchdog.cpp new file mode 100644 index 0000000..959082b --- /dev/null +++ b/sdv_services/uds_win_sockets/watchdog.cpp @@ -0,0 +1,58 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#ifdef _WIN32 + +#include "watchdog.h" + +void CWinSocketsConnectionWatchDog::AddConnectionImpl(const std::shared_ptr& connection) +{ + std::lock_guard lock(m_Mutex); + m_Connections[connection.get()] = connection; +} + +void CWinSocketsConnectionWatchDog::RemoveConnection(const void* connection) +{ + if (!connection) + { + return; + } + + std::shared_ptr removed; + { + std::lock_guard lock(m_Mutex); + auto it = m_Connections.find(connection); + if (it == m_Connections.end()) + { + return; + } + + removed = std::move(it->second); + m_Connections.erase(it); + } + + removed.reset(); +} + +void CWinSocketsConnectionWatchDog::Clear() +{ + std::map> localConnections; + { + std::lock_guard lock(m_Mutex); + localConnections.swap(m_Connections); + } + + localConnections.clear(); +} + +#endif // _WIN32 diff --git a/sdv_services/uds_win_sockets/watchdog.h b/sdv_services/uds_win_sockets/watchdog.h new file mode 100644 index 0000000..4bed92a --- /dev/null +++ b/sdv_services/uds_win_sockets/watchdog.h @@ -0,0 +1,47 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#ifdef _WIN32 +#ifndef UDS_WIN_SOCKETS_WATCHDOG_H +#define UDS_WIN_SOCKETS_WATCHDOG_H + +#include +#include +#include + +class CWinSocketsConnectionWatchDog +{ +public: + template + void AddConnection(const std::shared_ptr& connection) + { + if (!connection) + { + return; + } + + AddConnectionImpl(std::static_pointer_cast(connection)); + } + + void RemoveConnection(const void* connection); + void Clear(); + +private: + void AddConnectionImpl(const std::shared_ptr& connection); + + std::mutex m_Mutex; + std::map> m_Connections; +}; + +#endif // UDS_WIN_SOCKETS_WATCHDOG_H +#endif // _WIN32 diff --git a/sdv_services/uds_win_tunnel/CMakeLists.txt b/sdv_services/uds_win_tunnel/CMakeLists.txt index 3493a2a..bf41b3c 100644 --- a/sdv_services/uds_win_tunnel/CMakeLists.txt +++ b/sdv_services/uds_win_tunnel/CMakeLists.txt @@ -8,33 +8,64 @@ # SPDX-License-Identifier: Apache-2.0 #******************************************************************************* +if(WIN32) + # Define project project(uds_win_tunnel VERSION 1.0 LANGUAGES CXX) -# Define target -add_library(uds_win_tunnel STATIC +set(UDS_WIN_TUNNEL_SOURCES channel_mgnt.cpp connection.cpp + watchdog.cpp +) + +# Define targets +add_library(uds_win_tunnel STATIC + ${UDS_WIN_TUNNEL_SOURCES} +) + +add_library(uds_win_tunnel_module SHARED + ${UDS_WIN_TUNNEL_SOURCES} ) target_link_libraries(uds_win_tunnel PRIVATE uds_win_sockets - Ws2_32.lib + Ws2_32.lib ${CMAKE_THREAD_LIBS_INIT} ) -target_include_directories(uds_win_tunnel - PRIVATE +target_link_libraries(uds_win_tunnel_module + PRIVATE + uds_win_sockets + Ws2_32.lib + ${CMAKE_THREAD_LIBS_INIT} +) + +target_include_directories(uds_win_tunnel + PRIVATE ./include/ ../uds_win_sockets/ ) -set_target_properties(uds_win_tunnel PROPERTIES PREFIX "") -set_target_properties(uds_win_tunnel PROPERTIES SUFFIX ".sdv") +target_include_directories(uds_win_tunnel_module + PRIVATE + ./include/ + ../uds_win_sockets/ +) + +set_target_properties(uds_win_tunnel_module PROPERTIES + PREFIX "" + SUFFIX ".sdv" + OUTPUT_NAME "uds_win_tunnel" + ARCHIVE_OUTPUT_NAME "uds_win_tunnel_module" +) # Build dependencies add_dependencies(uds_win_tunnel CompileCoreIDL) +add_dependencies(uds_win_tunnel_module CompileCoreIDL) # Appending the service in the service list -set(SDV_Service_List ${SDV_Service_List} uds_win_tunnel PARENT_SCOPE) +set(SDV_Service_List ${SDV_Service_List} uds_win_tunnel_module PARENT_SCOPE) + +endif() diff --git a/sdv_services/uds_win_tunnel/channel_mgnt.cpp b/sdv_services/uds_win_tunnel/channel_mgnt.cpp index bfd29e4..51384c1 100644 --- a/sdv_services/uds_win_tunnel/channel_mgnt.cpp +++ b/sdv_services/uds_win_tunnel/channel_mgnt.cpp @@ -13,12 +13,14 @@ #ifdef _WIN32 #include "channel_mgnt.h" +#include +#include +#include +#include #include "../../global/base64.h" #include #include -#include - #pragma push_macro("interface") #undef interface @@ -39,298 +41,498 @@ #pragma pop_macro("GetObject") #pragma pop_macro("interface") -extern int StartUpWinSock(); +//#include "../sdv_services/uds_win_sockets/channel_mgnt.cpp" namespace { -/** - * @brief Parse a tunnel connect/config string and extract the path. - * - * Expected format: - * "proto=tunnel;path=;" - * - * Behavior: - * - If "proto=tunnel" missing -> false - * - If "path=" missing -> true and outPath.clear() - * - * @param[in] cs The connect/config string to parse. - * @param[out] outPath The extracted path, or empty if not found. - * @return true if parsing succeeded, false otherwise. - */ -static bool ParseTunnelPath(const std::string& cs, std::string& outPath) -{ - constexpr const char* protoKey = "proto=tunnel"; - constexpr const char* pathKey = "path="; - if (cs.find(protoKey) == std::string::npos) + static std::atomic g_nextChannelId{1}; + + static bool EnsureWSAInitialized() { - return false; + static std::once_flag s_once; + static bool s_ok = false; + + std::call_once(s_once, []() + { + WSADATA wsa{}; + const int rc = WSAStartup(MAKEWORD(2, 2), &wsa); + s_ok = (rc == 0); + if (!s_ok) + { + SDV_LOG_ERROR("[AF_UNIX] WSAStartup failed, rc=", rc); + } + }); + + return s_ok; } - const auto p = cs.find(pathKey); - if (p == std::string::npos) + static std::string SanitizeUdsName(std::string name) { - outPath.clear(); + for (char& ch : name) + { + const bool isAlphaNum = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'); + if (!isAlphaNum && ch != '_' && ch != '-') + { + ch = '_'; + } + } + + if (name.empty()) + { + name = "sdv"; + } + + return name; + } + + static std::string BuildNamedUdsRawPath(const std::string& channelName) + { + return "%LOCALAPPDATA%/sdv/" + SanitizeUdsName(channelName) + ".sock"; + } + + static std::string GetDefaultUdsRawPath() + { + uint32_t instanceId = 1000u; + const sdv::app::IAppContext* pAppContext = sdv::core::GetCore(); + if (pAppContext && pAppContext->GetInstanceID() != 0u) + { + instanceId = pAppContext->GetInstanceID(); + } + + return "%LOCALAPPDATA%/sdv/vapi_" + std::to_string(instanceId) + ".sock"; + } + + static std::string GetUniqueEndpointUdsRawPath() + { + static std::atomic nextEndpointId { 0u }; + + uint32_t instanceId = 1000u; + const sdv::app::IAppContext* pAppContext = sdv::core::GetCore(); + if (pAppContext && pAppContext->GetInstanceID() != 0u) + { + instanceId = pAppContext->GetInstanceID(); + } + + const uint32_t endpointId = nextEndpointId.fetch_add(1u, std::memory_order_relaxed); + return "%LOCALAPPDATA%/sdv/vapi_" + std::to_string(instanceId) + "_" + + std::to_string(static_cast(GetCurrentProcessId())) + "_" + std::to_string(endpointId) + ".sock"; + } + + /** + * @brief Parse a tunnel connect/config string and extract the path. + * + * Expected format: + * "proto=tunnel;path=;" + * + * Behavior: + * - If "proto=tunnel" missing -> false + * - If "path=" missing -> true and outPath.clear() + * + * @param[in] cs The connect/config string to parse. + * @param[out] outPath The extracted path, or empty if not found. + * @return true if parsing succeeded, false otherwise. + */ + static bool ParseTunnelPath(const std::string& cs, std::string& outPath) + { + constexpr const char* protoKey = "proto=tunnel"; + constexpr const char* pathKey = "path="; + + // Strict raw connect string only + if (cs.rfind(protoKey, 0) != 0) + { + return false; + } + + const auto p = cs.find(pathKey); + if (p == std::string::npos) + { + outPath.clear(); + return true; + } + + const auto start = p + std::strlen(pathKey); + const auto end = cs.find(';', start); + if (end == std::string::npos) + { + outPath = cs.substr(start); + } + else + { + outPath = cs.substr(start, end - start); + } return true; } - const auto start = p + std::strlen(pathKey); - const auto end = cs.find(';', start); - if (end == std::string::npos) + static std::string ExtractTunnelNameFromToml(sdv::toml::CTOMLParser& parser) { - outPath = cs.substr(start); - } - else - { - outPath = cs.substr(start, end - start); - } - return true; -} + std::string tunnel; -/** - * @brief Expands Windows environment variables in a string (e.g., %TEMP%). - * @param[in] in Input string possibly containing environment variables. - * @return String with environment variables expanded, or original if expansion fails. - */ -static std::string ExpandEnvVars(const std::string& in) -{ - if (in.find('%') == std::string::npos) + auto ipcTunnelNode = parser.GetDirect("IpcChannel.Tunnel"); + if (ipcTunnelNode.GetType() == sdv::toml::ENodeType::node_string) + { + tunnel = static_cast(ipcTunnelNode.GetValue()); + } + + if (tunnel.empty()) + { + auto providerTunnelNode = parser.GetDirect("Provider.Tunnel"); + if (providerTunnelNode.GetType() == sdv::toml::ENodeType::node_string) + { + tunnel = static_cast(providerTunnelNode.GetValue()); + } + } + + return tunnel; + } + + static bool ExtractTunnelConnectString(const std::string& in, + std::string& outTunnelConnectString) { + std::string path; + + // Case 1: strict raw tunnel connect string + if (ParseTunnelPath(in, path)) + { + outTunnelConnectString = in; + return true; + } + + // Case 2: structured TOML + sdv::toml::CTOMLParser parser(in); + if (!parser.IsValid()) + { + return false; + } + + const std::string providerName = parser.GetDirect("Provider.Name").GetValue(); + if (!providerName.empty() && + providerName != "unix_domain_sockets_tunnel" && + providerName != "WinTunnelChannelControl" && + providerName != "UnixTunnelChannelControl") + { + return false; + } + + // Provider.ConnectString = "proto=tunnel;path=...;tunnel=...;" + const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue(); + if (!nested.empty()) + { + if (ParseTunnelPath(nested, path)) + { + outTunnelConnectString = nested; + return true; + } + return false; + } + + // Build a tunnel connect string from [IpcChannel] + const std::string tunnel = ExtractTunnelNameFromToml(parser); + + const std::string cfgPath = parser.GetDirect("IpcChannel.Path").GetValue(); + if (!cfgPath.empty()) + { + outTunnelConnectString = "proto=tunnel;path=" + cfgPath + ";"; + if (!tunnel.empty()) + { + outTunnelConnectString += "tunnel=" + tunnel + ";"; + } + return true; + } + + const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue(); + if (!cfgName.empty()) + { + outTunnelConnectString = "proto=tunnel;path=" + BuildNamedUdsRawPath(cfgName) + ";"; + if (!tunnel.empty()) + { + outTunnelConnectString += "tunnel=" + tunnel + ";"; + } + return true; + } + + return false; + } + + /** + * @brief Expands Windows environment variables in a string (e.g., %TEMP%). + * @param[in] in Input string possibly containing environment variables. + * @return String with environment variables expanded, or original if expansion fails. + */ + static std::string ExpandEnvVars(const std::string& in) + { + if (in.find('%') == std::string::npos) + { + return in; + } + char buf[4096] = {}; + DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast(sizeof(buf))); + if (n > 0 && n < sizeof(buf)) + { + return std::string(buf); + } return in; } - char buf[4096] = {}; - DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast(sizeof(buf))); - if (n > 0 && n < sizeof(buf)) - { - return std::string(buf); - } - return in; -} -/** - * @brief Clamps a UDS path to the maximum allowed by SOCKADDR_UN. - * @param[in] p The input path. - * @return The clamped path. - */ -static std::string ClampUdsPath(const std::string& p) -{ - SOCKADDR_UN tmp{}; - constexpr auto kMax = sizeof(tmp.sun_path) - 1; - if (p.size() <= kMax) + /** + * @brief Clamps a UDS path to the maximum allowed by SOCKADDR_UN. + * @param[in] p The input path. + * @return The clamped path. + */ + static std::string ClampUdsPath(const std::string& p) { - return p; - } - return p.substr(0, kMax); -} - -// Only for logging – basename -/** - * @brief Normalizes a raw UDS path for Windows, extracting the basename and ensuring a default if empty. - * @param[in] raw The raw path string. - * @return The normalized basename, clamped to max length. - */ -static std::string NormalizeUdsPathForWindows(const std::string& raw) -{ - std::string p = ExpandEnvVars(raw); - const size_t pos = p.find_last_of("/\\"); - std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1); - if (base.empty()) - { - base = "sdv_tunnel.sock"; - } - SDV_LOG_INFO("[AF_UNIX][Tunnel] Normalize raw='", raw, "' -> base='", base, "'"); - return ClampUdsPath(base); -} - -/** - * @brief Creates a short, safe UDS path in the Windows temp directory. - * @param[in] raw The raw path string. - * @return The full path in %TEMP%\sdv\, clamped to max length. - */ -static std::string MakeShortWinUdsPath(const std::string& raw) -{ - std::string p = ExpandEnvVars(raw); - const size_t pos = p.find_last_of("/\\"); - std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1); - if (base.empty()) - { - base = "sdv_tunnel.sock"; + SOCKADDR_UN tmp{}; + constexpr auto kMax = sizeof(tmp.sun_path) - 1; + if (p.size() <= kMax) + { + return p; + } + return p.substr(0, kMax); } - std::string dir = ExpandEnvVars("%TEMP%\\sdv\\"); - CreateDirectoryA(dir.c_str(), nullptr); - const std::string full = dir + base; - - return ClampUdsPath(full); -} - -/** - * @brief Creates an AF_UNIX listen socket at the specified path. - * @param[in] rawPath The raw path for the socket. - * @return The created socket handle, or INVALID_SOCKET on failure. - */ -static SOCKET CreateUnixListenSocket(const std::string& rawPath) -{ - SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); - if (s == INVALID_SOCKET) + static std::string BuildFinalUdsPath(const std::string& rawPath) { - SDV_LOG_ERROR("[AF_UNIX][Tunnel] socket() FAIL (listen), WSA=", WSAGetLastError()); - return INVALID_SOCKET; + // Expand environment variables + std::string full = ExpandEnvVars(rawPath); + + // Ensure directory exists (parent folder) + auto pos = full.find_last_of("\\/"); + if (pos != std::string::npos) + { + std::string dir = full.substr(0, pos); + + // Ensure immediate parent exists (base/tunnel directory is created earlier) + CreateDirectoryA(dir.c_str(), nullptr); + } + + // Clamp to AF_UNIX limit + return ClampUdsPath(full); + } + + /** + * @brief Normalizes a raw UDS path for Windows, extracting the basename and ensuring a default if empty. + * @param[in] raw The raw path string. + * @return The normalized basename, clamped to max length. + */ + static std::string NormalizeUdsPathForWindows(const std::string& raw) + { + std::string p = ExpandEnvVars(raw); + const size_t pos = p.find_last_of("/\\"); + std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1); + if (base.empty()) + { + base = "sdv_tunnel.sock"; + } + SDV_LOG_INFO("[AF_UNIX][Tunnel] Normalize raw='", raw, "' -> base='", base, "'"); + return ClampUdsPath(base); } - std::string udsPath = MakeShortWinUdsPath(rawPath); - SOCKADDR_UN addr{}; - addr.sun_family = AF_UNIX; - strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str()); - - const int addrlen = static_cast( - offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1); - - ::remove(udsPath.c_str()); - if (bind(s, reinterpret_cast(&addr), addrlen) == SOCKET_ERROR) + /** + * @brief Creates an AF_UNIX listen socket at the specified path. + * @param[in] rawPath The raw path for the socket. + * @return The created socket handle, or INVALID_SOCKET on failure. + */ + static SOCKET CreateUnixListenSocket(const std::string& rawPath) { - SDV_LOG_ERROR("[AF_UNIX][Tunnel] bind FAIL, WSA=", - WSAGetLastError(), ", path='", udsPath, "'"); - closesocket(s); - return INVALID_SOCKET; - } - if (listen(s, SOMAXCONN) == SOCKET_ERROR) - { - SDV_LOG_ERROR("[AF_UNIX][Tunnel] listen FAIL, WSA=", - WSAGetLastError(), ", path='", udsPath, "'"); - closesocket(s); - return INVALID_SOCKET; - } + + if (!EnsureWSAInitialized()) + return INVALID_SOCKET; - SDV_LOG_INFO("[AF_UNIX][Tunnel] bind+listen OK, path='", udsPath, "'"); - return s; -} - -/** - * @brief Connects to an AF_UNIX socket at the specified path, retrying until timeout. - * @param[in] rawPath The raw path to connect to. - * @param[in] totalTimeoutMs Total timeout in milliseconds. - * @param[in] retryDelayMs Delay between retries in milliseconds. - * @return The connected socket handle, or INVALID_SOCKET on failure. - */ -static SOCKET ConnectUnixSocket( - const std::string& rawPath, - uint32_t totalTimeoutMs, - uint32_t retryDelayMs) -{ - const std::string udsPath = MakeShortWinUdsPath(rawPath); - SOCKADDR_UN addr{}; - addr.sun_family = AF_UNIX; - strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str()); - - const int addrlen = static_cast( - offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1); - - const auto deadline = std::chrono::steady_clock::now() + - std::chrono::milliseconds(totalTimeoutMs); - - int lastError = 0; - while (true) - { SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { - lastError = WSAGetLastError(); - SDV_LOG_ERROR("[AF_UNIX][Tunnel] socket() FAIL (client), WSA=", lastError); + SDV_LOG_ERROR("[AF_UNIX] socket FAIL (listen), WSA=", WSAGetLastError()); return INVALID_SOCKET; } - if (connect(s, reinterpret_cast(&addr), addrlen) == 0) - { - SDV_LOG_INFO("[AF_UNIX][Tunnel] connect OK, path='", udsPath, "'"); - return s; - } + //bulletproof path handling + std::string udsPath = BuildFinalUdsPath(rawPath); - lastError = WSAGetLastError(); - closesocket(s); + SOCKADDR_UN addr{}; + addr.sun_family = AF_UNIX; + strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str()); - if (std::chrono::steady_clock::now() >= deadline) + const int addrlen = static_cast( + offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1); + + ::remove(udsPath.c_str()); + + if (bind(s, reinterpret_cast(&addr), addrlen) == SOCKET_ERROR) { - SDV_LOG_ERROR("[AF_UNIX][Tunnel] connect TIMEOUT, last WSA=", - lastError, ", path='", udsPath, "'"); + int err = WSAGetLastError(); + SDV_LOG_ERROR("[AF_UNIX] bind FAIL, WSA=", err, ", path=", udsPath); + closesocket(s); return INVALID_SOCKET; } - std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs)); + if (listen(s, SOMAXCONN) == SOCKET_ERROR) + { + int err = WSAGetLastError(); + SDV_LOG_ERROR("[AF_UNIX] listen FAIL, WSA=", err, ", path=", udsPath); + closesocket(s); + return INVALID_SOCKET; + } + + SDV_LOG_INFO("[AF_UNIX] bind+listen OK: ", udsPath); + return s; } -} + + static bool ExtractTunnelName(const std::string& in, std::string& outTunnel) + { + auto pos = in.find("tunnel="); + if (pos == std::string::npos) + { + outTunnel.clear(); + return false; + } + pos += 7; + auto end = in.find(';', pos); + outTunnel = (end == std::string::npos) ? in.substr(pos) : in.substr(pos, end - pos); + return !outTunnel.empty(); + } + + /** + * @brief Connects to an AF_UNIX socket at the specified path, retrying until timeout. + * @param[in] rawPath The raw path to connect to.CreateUnixListenSocket + * @param[in] totalTimeoutMs Total timeout in milliseconds. + * @param[in] retryDelayMs Delay between retries in milliseconds. + * @return The connected socket handle, or INVALID_SOCKET on failure. + */ + static SOCKET ConnectUnixSocket( + const std::string& rawPath, + uint32_t totalTimeoutMs, + uint32_t retryDelayMs) + { + if (!EnsureWSAInitialized()) + return INVALID_SOCKET; + + //SAME path logic ca server + const std::string udsPath = BuildFinalUdsPath(rawPath); + SDV_LOG_INFO("[AF_UNIX][Tunnel] Attempting to connect to ", udsPath, "with rawPath=", rawPath); + SOCKADDR_UN addr{}; + addr.sun_family = AF_UNIX; + strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str()); + + const int addrlen = static_cast( + offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1); + + auto deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(totalTimeoutMs); + + while (true) + { + SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) + { + SDV_LOG_ERROR("[AF_UNIX] socket FAIL (client), WSA=", WSAGetLastError()); + return INVALID_SOCKET; + } + + if (connect(s, reinterpret_cast(&addr), addrlen) == 0) + { + SDV_LOG_INFO("[AF_UNIX] connect OK: ", udsPath); + return s; + } + + int err = WSAGetLastError(); + closesocket(s); + + if (std::chrono::steady_clock::now() >= deadline) + { + SDV_LOG_ERROR("[AF_UNIX] connect TIMEOUT, WSA=", err, ", path=", udsPath); + return INVALID_SOCKET; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs)); + } + } } // anonymous namespace bool CSocketsTunnelChannelMgnt::OnInitialize() { - return true; + return EnsureWSAInitialized(); } void CSocketsTunnelChannelMgnt::OnShutdown() {} -// -------- Server bookkeeping (optional) -------- -void CSocketsTunnelChannelMgnt::OnServerClosed(const std::string& udsPath, CWinTunnelConnection* ptr) +void CSocketsTunnelChannelMgnt::OnDestroy() { - std::lock_guard lock(m_udsMtx); - auto it = m_udsServers.find(udsPath); - if (it != m_udsServers.end() && it->second.get() == ptr) - { - m_udsServers.erase(it); - } - m_udsServerClaimed.erase(udsPath); + m_watchdog.Clear(); } -// -------- ICreateEndpoint -------- + +// Note: +// tunnel name is required by the current Windows tunnel design for namespace isolation. +// [IpcChannel.Path]/[IpcChannel.Name] alone are not sufficient unless Tunnel is also provided. sdv::ipc::SChannelEndpoint CSocketsTunnelChannelMgnt::CreateEndpoint(const sdv::u8string& cfgStr) { sdv::ipc::SChannelEndpoint ep{}; - - if (StartUpWinSock() != 0) - { - SDV_LOG_ERROR("[AF_UNIX][Tunnel] WinSock startup failed in CreateEndpoint"); - return ep; - } - - // Optional TOML config: [IpcChannel] Path = "..." std::string udsRaw; - if (!cfgStr.empty()) - { - //for toml file - bool isTOML = cfgStr.find('=') == std::string::npos; - if(isTOML) - { - sdv::toml::CTOMLParser cfg(cfgStr.c_str()); - auto pathNode = cfg.GetDirect("IpcChannel.Path"); - if (pathNode.GetType() == sdv::toml::ENodeType::node_string) - { - udsRaw = static_cast(pathNode.GetValue()); - } - } + std::string tunnelConnectString; - //for connect string - if (udsRaw.empty()) + if (ExtractTunnelConnectString(cfgStr, tunnelConnectString)) + { + ParseTunnelPath(tunnelConnectString, udsRaw); + } + else if (!cfgStr.empty()) + { + sdv::toml::CTOMLParser parser(cfgStr); + const std::string cfgPath = parser.GetDirect("IpcChannel.Path").GetValue(); + const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue(); + + if (!cfgPath.empty()) { - const std::string s(cfgStr); - const std::string key = "path="; - auto pos = s.find(key); - if (pos != std::string::npos) - { - auto end = s.find(';', pos + key.size()); - if (end == std::string::npos) - udsRaw = s.substr(pos + key.size()); - else - udsRaw = s.substr(pos + key.size(), end - pos - key.size()); - } + udsRaw = cfgPath; + } + else if (!cfgName.empty()) + { + udsRaw = BuildNamedUdsRawPath(cfgName); + } + else + { + udsRaw = GetDefaultUdsRawPath(); } } if (udsRaw.empty()) { - udsRaw = "%LOCALAPPDATA%/sdv/tunnel.sock"; + udsRaw = cfgStr.empty() ? GetUniqueEndpointUdsRawPath() : GetDefaultUdsRawPath(); + } + + if (tunnelConnectString.empty()) + { + tunnelConnectString = cfgStr; + } + + std::string tunnel; + if (!ExtractTunnelName(tunnelConnectString, tunnel)) + { + // fallback for AppConnect (config minimal) + sdv::toml::CTOMLParser parser(cfgStr); + const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue(); + + if (!cfgName.empty()) + { + tunnel = cfgName; + SDV_LOG_INFO("[AF_UNIX][Tunnel] Using channel name as tunnel name: ", tunnel); + } + else + { + // fallback + tunnel = "default"; + SDV_LOG_WARNING("[AF_UNIX][Tunnel] Missing tunnel and channel name, using default"); + } } - std::string udsPathBase = NormalizeUdsPathForWindows(udsRaw); + std::string base = ExpandEnvVars("%TEMP%\\sdv\\"); + CreateDirectoryA(base.c_str(), nullptr); + + std::string dir = base + tunnel + "\\"; + CreateDirectoryA(dir.c_str(), nullptr); + + std::string udsPathBase = dir + NormalizeUdsPathForWindows(udsRaw); + SDV_LOG_INFO("[AF_UNIX][Tunnel] endpoint udsPath=", udsPathBase); SOCKET listenSocket = CreateUnixListenSocket(udsPathBase); @@ -340,75 +542,177 @@ sdv::ipc::SChannelEndpoint CSocketsTunnelChannelMgnt::CreateEndpoint(const sdv:: return ep; } - auto serverTransport = std::make_shared( static_cast(listenSocket), true); - auto serverTunnel = std::make_shared(serverTransport, /*channelId*/ static_cast(0u)); - + auto serverTransport = std::make_shared(static_cast(listenSocket), true); + uint32_t chId = g_nextChannelId++; + auto serverTunnel = std::make_shared(serverTransport, /*channelId*/ static_cast(chId)); + // Ignore cppcheck warning; if construction failed, an exception is expected first. + // cppcheck-suppress knownConditionTrueFalse + if (!serverTunnel) { - std::lock_guard lock(m_udsMtx); - m_udsServers[udsPathBase] = serverTunnel; - m_udsServerClaimed.erase(udsPathBase); + return ep; + } + // Retain shared_ptr to keep object alive for the duration of the connection. + // This ensures shared_from_this() works correctly in DestroyObject(). + { + std::lock_guard lock(m_ConnectionsMutex); + m_ptrConnections[serverTunnel.get()] = serverTunnel; } + serverTunnel->SetWatchDogRemoveCallback([this](const void* connection) + { + m_watchdog.RemoveConnection(connection); + // Remove the retained shared_ptr when connection is destroyed + std::lock_guard lock(m_ConnectionsMutex); + m_ptrConnections.erase(connection); + }); + + m_watchdog.AddConnection(serverTunnel); + ep.pConnection = static_cast(serverTunnel.get()); - ep.ssConnectString = "proto=tunnel;role=server;path=" + udsPathBase + ";"; + + const std::string clientConnectString = "proto=tunnel;path=" + udsPathBase + ";tunnel=" + tunnel + ";"; + + // Publish raw connect string (cleaner and avoids TOML escaping issues on Windows) + ep.ssConnectString = clientConnectString; return ep; } sdv::IInterfaceAccess* CSocketsTunnelChannelMgnt::Access(const sdv::u8string& cs) { - if (StartUpWinSock() != 0) + std::string tunnelConnectString; + const std::string input = static_cast(cs); + + // Operational metadata, not part of the tunnel connect string itself + const bool isServer = (input.find("role=server") != std::string::npos); + + // Parse only the real connect/config part + std::string parseInput = input; + const auto rolePos = parseInput.find(";role="); + if (rolePos != std::string::npos) { - SDV_LOG_ERROR("[AF_UNIX][Tunnel] WinSock startup failed in Access()" ); + const auto roleEnd = parseInput.find(';', rolePos + 1); + if (roleEnd != std::string::npos) + { + parseInput.erase(rolePos, roleEnd - rolePos + 1); + } + else + { + parseInput.erase(rolePos); + } + } + + if (!ExtractTunnelConnectString(parseInput, tunnelConnectString)) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] Invalid tunnel connect/config string"); return nullptr; } - std::string connectStr = static_cast(cs); std::string udsRaw; - if (!ParseTunnelPath(connectStr, udsRaw)) - { - SDV_LOG_ERROR("[AF_UNIX][Tunnel] Invalid tunnel connect string: ", connectStr); - return nullptr; - } + ParseTunnelPath(tunnelConnectString, udsRaw); + SDV_LOG_INFO("[AF_UNIX][Tunnel] Access requested with connect string: ", + tunnelConnectString, ", extracted path: ", udsRaw); if (udsRaw.empty()) { - udsRaw = "%LOCALAPPDATA%/sdv/tunnel.sock"; + udsRaw = GetDefaultUdsRawPath(); + SDV_LOG_INFO("[AF_UNIX][Tunnel] No path specified, using default: ", udsRaw); } - std::string udsPathBase = NormalizeUdsPathForWindows(udsRaw); - SDV_LOG_INFO("[AF_UNIX][Tunnel] Access udsPath=", udsPathBase); - - const bool isServer = - (connectStr.find("role=server") != std::string::npos); + std::string tunnel; + if (!ExtractTunnelName(tunnelConnectString, tunnel)) { - std::lock_guard lock(m_udsMtx); - auto it = m_udsServers.find(udsPathBase); - if (isServer && it != m_udsServers.end() && it->second != nullptr) + // fallback: derive from filename + std::string path; + ParseTunnelPath(tunnelConnectString, path); + + // extract filename + auto pos = path.find_last_of("/\\"); + std::string filename = (pos != std::string::npos) ? path.substr(pos + 1) : path; + + // remove ".sock" + auto dot = filename.rfind(".sock"); + if (dot != std::string::npos) { - if (!m_udsServerClaimed.count(udsPathBase)) - { - m_udsServerClaimed.insert(udsPathBase); - SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> RETURN SERVER for ", udsPathBase); - return it->second.get(); // Ownership: managed by m_udsServers (do not delete) - } + tunnel = filename.substr(0, dot); + } + else + { + tunnel = filename; + } + + if (!tunnel.empty()) + { + SDV_LOG_INFO("[AF_UNIX][Tunnel] Derived tunnel from filename: ", tunnel); } } - // CLIENT: create AF_UNIX client socket and wrap it in a tunnel - SOCKET s = ConnectUnixSocket(udsPathBase, /*totalTimeoutMs*/ 5000, /*retryDelayMs*/ 50); - if (s == INVALID_SOCKET) + if (tunnel.empty()) { - SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to connect client socket for ", udsPathBase); + SDV_LOG_ERROR("[AF_UNIX][Tunnel] Missing required tunnel name"); return nullptr; } - SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> CREATE CLIENT for ", udsPathBase); - auto clientTransport = std::make_shared(s, /*acceptRequired*/ false); - // Ownership: The returned pointer must be managed and deleted by the SDV framework via IObjectDestroy - auto* tunnelClient = new CWinTunnelConnection(clientTransport, /*channelId*/ 0u); + std::string base = ExpandEnvVars("%TEMP%\\sdv\\"); + CreateDirectoryA(base.c_str(), nullptr); - return static_cast(tunnelClient); + std::string dir = base + tunnel + "\\"; + CreateDirectoryA(dir.c_str(), nullptr); + + std::string udsPathBase = dir + NormalizeUdsPathForWindows(udsRaw); + SDV_LOG_INFO("[AF_UNIX][Tunnel] Access udsPath=", udsPathBase); + + std::shared_ptr connection; + if (isServer) + { + SOCKET listenSocket = CreateUnixListenSocket(udsPathBase); + if (listenSocket == INVALID_SOCKET) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to create server socket for ", udsPathBase); + return nullptr; + } + + auto serverTransport = std::make_shared( + static_cast(listenSocket), true); + uint32_t chId = g_nextChannelId++; + connection = std::make_shared( + serverTransport, static_cast(chId)); + } + else + { + SOCKET s = ConnectUnixSocket(udsPathBase, 5000, 50); + if (s == INVALID_SOCKET) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to connect client socket for ", udsPathBase); + return nullptr; + } + + SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> CREATE CLIENT for ", udsPathBase); + auto clientTransport = std::make_shared(s, false); + uint32_t chId = g_nextChannelId++; + connection = std::make_shared( + clientTransport, static_cast(chId)); + } + + if (!connection) + { + return nullptr; + } + + { + std::lock_guard lock(m_ConnectionsMutex); + m_ptrConnections[connection.get()] = connection; + } + + connection->SetWatchDogRemoveCallback([this](const void* instance) + { + m_watchdog.RemoveConnection(instance); + std::lock_guard lock(m_ConnectionsMutex); + m_ptrConnections.erase(instance); + }); + + m_watchdog.AddConnection(connection); + return static_cast(connection.get()); } #endif \ No newline at end of file diff --git a/sdv_services/uds_win_tunnel/channel_mgnt.h b/sdv_services/uds_win_tunnel/channel_mgnt.h index 89c48e6..9c09a92 100644 --- a/sdv_services/uds_win_tunnel/channel_mgnt.h +++ b/sdv_services/uds_win_tunnel/channel_mgnt.h @@ -16,18 +16,15 @@ #include #include -#include "../sdv_services/uds_win_sockets/channel_mgnt.h" -#include "../sdv_services/uds_win_sockets/connection.h" #include "connection.h" +#include "watchdog.h" -#include +#include #include -#include -#include +#include #include // Winsock headers are required for SOCKET / AF_UNIX / WSAStartup -// NOTE: The actual initialization is done via StartUpWinSock() #include class CWinTunnelConnection; @@ -56,8 +53,8 @@ public: DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) DECLARE_OBJECT_CLASS_NAME("WinTunnelChannelControl") - DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl") - DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl") + DECLARE_OBJECT_CLASS_ALIAS("unix_domain_sockets_tunnel") + DECLARE_DEFAULT_OBJECT_NAME("unix_domain_sockets_tunnel") DECLARE_OBJECT_SINGLETON() virtual ~CSocketsTunnelChannelMgnt() = default; @@ -73,6 +70,11 @@ public: */ virtual void OnShutdown() override; + /** + * @brief Last function called before destruction. Overload of sdv::CSdvObject::OnDestroy. + */ + virtual void OnDestroy() override; + /** * @brief Creates a tunnel endpoint (server side) and returns endpoint info. * @param[in] cfgStr Optional config string (TOML or connect string). @@ -87,17 +89,10 @@ public: */ sdv::IInterfaceAccess* Access(const sdv::u8string& cs) override; - /** - * @brief Called by server tunnel when closing (bookkeeping). - * @param[in] udsPath The UDS path for the server. - * @param[in] ptr Pointer to the tunnel connection being closed. - */ - void OnServerClosed(const std::string& udsPath, CWinTunnelConnection* ptr); - private: - std::mutex m_udsMtx; - std::map> m_udsServers; - std::set m_udsServerClaimed; + CWinTunnelConnectionWatchDog m_watchdog; + std::map> m_ptrConnections; ///< Retain shared_ptr for all connections + mutable std::mutex m_ConnectionsMutex; ///< Protect concurrent access to m_ptrConnections }; DEFINE_SDV_OBJECT(CSocketsTunnelChannelMgnt) diff --git a/sdv_services/uds_win_tunnel/connection.cpp b/sdv_services/uds_win_tunnel/connection.cpp index 9aa27fa..57dac10 100644 --- a/sdv_services/uds_win_tunnel/connection.cpp +++ b/sdv_services/uds_win_tunnel/connection.cpp @@ -24,26 +24,35 @@ CWinTunnelConnection::CWinTunnelConnection( // No additional initialization required; acts as a thin wrapper. } -bool CWinTunnelConnection::SendData( - /*inout*/ sdv::sequence>& seqData) +CWinTunnelConnection::~CWinTunnelConnection() { + +} + +void CWinTunnelConnection::SetWatchDogRemoveCallback(std::function callback) +{ + std::lock_guard lock(m_WatchdogMtx); + m_WatchdogRemoveCallback = std::move(callback); +} + +bool CWinTunnelConnection::SendData( /*inout*/ sdv::sequence>& seqData) +{ + if (!m_Transport) { SDV_LOG_ERROR("[WinTunnel] SendData failed: transport is null"); return false; } - // Build tunnel header buffer sdv::pointer hdrBuf; hdrBuf.resize(sizeof(STunnelHeader)); STunnelHeader hdr{}; - hdr.uiChannelId = m_ChannelId; // Logical channel for this connection - hdr.uiFlags = 0; // Reserved for future use + hdr.uiChannelId = m_ChannelId; + hdr.uiFlags = 0; std::memcpy(hdrBuf.get(), &hdr, sizeof(STunnelHeader)); - // Compose new sequence: [header] + original payload chunks sdv::sequence> seqWithHdr; seqWithHdr.push_back(hdrBuf); for (auto& chunk : seqData) @@ -51,11 +60,13 @@ bool CWinTunnelConnection::SendData( seqWithHdr.push_back(chunk); } - bool result = m_Transport->SendData(seqWithHdr); - if (!result) { + const bool result = m_Transport->SendData(seqWithHdr); + if (!result) + { SDV_LOG_ERROR("[WinTunnel] SendData failed in underlying transport"); } return result; + } bool CWinTunnelConnection::AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) @@ -70,7 +81,7 @@ bool CWinTunnelConnection::AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) { std::lock_guard lock(m_CallbackMtx); sdv::TInterfaceAccessPtr acc(pReceiver); - m_pUpperReceiver = acc.GetInterface(); + m_pUpperRecv = acc.GetInterface(); m_pUpperEvent = acc.GetInterface(); } @@ -110,14 +121,8 @@ void CWinTunnelConnection::Disconnect() return; } + // Keep upper callbacks on plain Disconnect(); they are released in DestroyObject(). m_Transport->Disconnect(); - - // Clear upper-layer callbacks (thread-safe) - { - std::lock_guard lock(m_CallbackMtx); - m_pUpperReceiver = nullptr; - m_pUpperEvent = nullptr; - } } uint64_t CWinTunnelConnection::RegisterStateEventCallback( @@ -156,55 +161,91 @@ sdv::ipc::EConnectState CWinTunnelConnection::GetConnectState() const void CWinTunnelConnection::DestroyObject() { + bool expected = false; + if (!m_DestroyObjectCalled.compare_exchange_strong(expected, true)) + { + return; + } + + // Stop forwarding to upper layer before transport teardown. + { + std::lock_guard lock(m_CallbackMtx); + m_pUpperRecv = nullptr; + m_pUpperEvent = nullptr; + } + + // Do not notify upper layer during destruction teardown to avoid races. + SetConnectState(sdv::ipc::EConnectState::terminating); + Disconnect(); - std::lock_guard lock(m_CallbackMtx); - m_Transport.reset(); + { + std::lock_guard lock(m_CallbackMtx); + m_Transport.reset(); + } + + std::function removeCallback; + { + std::lock_guard watchdogLock(m_WatchdogMtx); + removeCallback = std::move(m_WatchdogRemoveCallback); + } + if (removeCallback) + { + removeCallback(this); + } } -void CWinTunnelConnection::ReceiveData( - /*inout*/ sdv::sequence>& seqData) +void CWinTunnelConnection::ReceiveData(/*inout*/ sdv::sequence>& seqData) { - // Expect at least one chunk (the tunnel header) + if (seqData.empty()) { SDV_LOG_ERROR("[WinTunnel] ReceiveData: empty sequence"); - return; // nothing to do + return; } const auto& hdrChunk = seqData[0]; if (hdrChunk.size() < sizeof(STunnelHeader)) { SDV_LOG_ERROR("[WinTunnel] ReceiveData: invalid tunnel header size"); - // Invalid tunnel frame; drop it for now (could set communication_error) return; } STunnelHeader hdr{}; std::memcpy(&hdr, hdrChunk.get(), sizeof(STunnelHeader)); + // Future demux point: + // if (hdr.uiChannelId != m_ChannelId) { ... } - // TODO: use channelId for multiplexing later - - // Build payload-only sequence: drop header chunk, keep others sdv::sequence> payload; for (size_t i = 1; i < seqData.size(); ++i) { payload.push_back(seqData[i]); } - if (m_pUpperReceiver) + sdv::ipc::IDataReceiveCallback* upper = nullptr; { - try { - m_pUpperReceiver->ReceiveData(payload); // header stripped - } catch (...) { - SDV_LOG_ERROR("[WinTunnel] Exception in upper receiver's ReceiveData"); + std::lock_guard lock(m_CallbackMtx); + upper = m_pUpperRecv; + } + + if (upper) + { + try + { + upper->ReceiveData(payload); + } + catch (...) + { + SDV_LOG_ERROR("[WinTunnel] Exception in upper receiver callback"); } } + } void CWinTunnelConnection::SetConnectState(sdv::ipc::EConnectState state) { sdv::ipc::IConnectEventCallback* upper = nullptr; + { std::lock_guard lock(m_CallbackMtx); upper = m_pUpperEvent; @@ -218,8 +259,7 @@ void CWinTunnelConnection::SetConnectState(sdv::ipc::EConnectState state) } catch (...) { - SDV_LOG_ERROR("[WinTunnel] Exception in upper event callback's SetConnectState"); - // Never let user callback crash the transport. + SDV_LOG_ERROR("[WinTunnel] Exception in upper event callback"); } } } diff --git a/sdv_services/uds_win_tunnel/connection.h b/sdv_services/uds_win_tunnel/connection.h index 5b0fcdd..7549dc8 100644 --- a/sdv_services/uds_win_tunnel/connection.h +++ b/sdv_services/uds_win_tunnel/connection.h @@ -21,10 +21,12 @@ #include #include #include +#include #include #include #include #include +#include #include "../sdv_services/uds_win_sockets/connection.h" // existing AF_UNIX transport: CWinsockConnection @@ -69,7 +71,7 @@ public: /** * @brief Destructor. */ - virtual ~CWinTunnelConnection() = default; + virtual ~CWinTunnelConnection(); BEGIN_SDV_INTERFACE_MAP() SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend) @@ -153,15 +155,19 @@ public: // Helpers void SetChannelId(uint16_t channelId) { m_ChannelId = channelId; } uint16_t GetChannelId() const noexcept { return m_ChannelId; } + void SetWatchDogRemoveCallback(std::function callback); private: std::shared_ptr m_Transport; ///< shared physical tunnel port uint16_t m_ChannelId{0}; ///< default logical channel id // Upper layer callbacks (original VAPI receiver) - sdv::ipc::IDataReceiveCallback* m_pUpperReceiver{nullptr}; + sdv::ipc::IDataReceiveCallback* m_pUpperRecv{nullptr}; sdv::ipc::IConnectEventCallback* m_pUpperEvent{nullptr}; mutable std::mutex m_CallbackMtx; + std::mutex m_WatchdogMtx; + std::function m_WatchdogRemoveCallback; + std::atomic m_DestroyObjectCalled{false}; }; #endif // UDS_WIN_TUNNEL_CONNECTION_H diff --git a/sdv_services/uds_win_tunnel/watchdog.cpp b/sdv_services/uds_win_tunnel/watchdog.cpp new file mode 100644 index 0000000..83343ba --- /dev/null +++ b/sdv_services/uds_win_tunnel/watchdog.cpp @@ -0,0 +1,58 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#ifdef _WIN32 + +#include "watchdog.h" + +void CWinTunnelConnectionWatchDog::AddConnectionImpl(const std::shared_ptr& connection) +{ + std::lock_guard lock(m_Mutex); + m_Connections[connection.get()] = connection; +} + +void CWinTunnelConnectionWatchDog::RemoveConnection(const void* connection) +{ + if (!connection) + { + return; + } + + std::shared_ptr removed; + { + std::lock_guard lock(m_Mutex); + auto it = m_Connections.find(connection); + if (it == m_Connections.end()) + { + return; + } + + removed = std::move(it->second); + m_Connections.erase(it); + } + + removed.reset(); +} + +void CWinTunnelConnectionWatchDog::Clear() +{ + std::map> localConnections; + { + std::lock_guard lock(m_Mutex); + localConnections.swap(m_Connections); + } + + localConnections.clear(); +} + +#endif // _WIN32 diff --git a/sdv_services/uds_win_tunnel/watchdog.h b/sdv_services/uds_win_tunnel/watchdog.h new file mode 100644 index 0000000..c87d9ff --- /dev/null +++ b/sdv_services/uds_win_tunnel/watchdog.h @@ -0,0 +1,47 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#ifdef _WIN32 +#ifndef UDS_WIN_TUNNEL_WATCHDOG_H +#define UDS_WIN_TUNNEL_WATCHDOG_H + +#include +#include +#include + +class CWinTunnelConnectionWatchDog +{ +public: + template + void AddConnection(const std::shared_ptr& connection) + { + if (!connection) + { + return; + } + + AddConnectionImpl(std::static_pointer_cast(connection)); + } + + void RemoveConnection(const void* connection); + void Clear(); + +private: + void AddConnectionImpl(const std::shared_ptr& connection); + + std::mutex m_Mutex; + std::map> m_Connections; +}; + +#endif // UDS_WIN_TUNNEL_WATCHDOG_H +#endif // _WIN32 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cc93e23..83abb2c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -60,6 +60,7 @@ endif() # Add test projects add_subdirectory(unit_tests/concurrency) add_subdirectory(unit_tests/flags_type) +add_subdirectory(unit_tests/unique_id) add_subdirectory(unit_tests/scheduler) add_subdirectory(unit_tests/idl_compiler) add_subdirectory(unit_tests/commandline_parser) @@ -68,16 +69,18 @@ add_subdirectory(unit_tests/asc_format) add_subdirectory(unit_tests/basic_types) add_subdirectory(unit_tests/smart_ifc) add_subdirectory(unit_tests/toml_parser) +add_subdirectory(unit_tests/simple_toml_parser) add_subdirectory(unit_tests/parameters) add_subdirectory(unit_tests/module_control) add_subdirectory(unit_tests/repository) +add_subdirectory(unit_tests/permission_control) add_subdirectory(unit_tests/shared_mem) add_subdirectory(unit_tests/memory_manager) add_subdirectory(unit_tests/core_loader) add_subdirectory(unit_tests/named_mutex) add_subdirectory(unit_tests/trace_fifo) add_subdirectory(unit_tests/socket_can_com_tests) -add_subdirectory(unit_tests/app_connect) +add_subdirectory(unit_tests/app_settings) add_subdirectory(unit_tests/process_control) add_subdirectory(unit_tests/ipc_com) add_subdirectory(unit_tests/ipc_connect) @@ -89,6 +92,7 @@ add_subdirectory(unit_tests/unix_sockets) add_subdirectory(unit_tests/unix_tunnel) add_subdirectory(unit_tests/win_sockets) add_subdirectory(unit_tests/win_tunnel) +add_subdirectory(component_tests/param_utils) add_subdirectory(component_tests/config) add_subdirectory(component_tests/logger) add_subdirectory(component_tests/data_dispatch_service) @@ -99,4 +103,5 @@ add_subdirectory(component_tests/vss_util) add_subdirectory(component_tests/task_timer) add_subdirectory(component_tests/app_control) add_subdirectory(component_tests/toml_parser) +add_subdirectory(component_tests/app_connect) add_subdirectory(manual_tests/silkit_can_com_tests) diff --git a/tests/cmake/run_gtest_serialized.cmake b/tests/cmake/run_gtest_serialized.cmake new file mode 100644 index 0000000..9a7fb79 --- /dev/null +++ b/tests/cmake/run_gtest_serialized.cmake @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.20) + +if(NOT DEFINED TEST_EXE) + message(FATAL_ERROR "TEST_EXE is required") +endif() + +if(NOT DEFINED TEST_XML) + message(FATAL_ERROR "TEST_XML is required") +endif() + +if(NOT DEFINED LOCK_FILE) + message(FATAL_ERROR "LOCK_FILE is required") +endif() + +if(NOT DEFINED TEST_EXECUTION_MODE) + set(TEST_EXECUTION_MODE "CMake") +endif() + +if(NOT DEFINED LOCK_TIMEOUT_SEC) + set(LOCK_TIMEOUT_SEC 1200) +endif() + +file(LOCK "${LOCK_FILE}" TIMEOUT ${LOCK_TIMEOUT_SEC} RESULT_VARIABLE LOCK_RESULT) +if(NOT LOCK_RESULT STREQUAL "0") + message(FATAL_ERROR "Failed to acquire test lock '${LOCK_FILE}': ${LOCK_RESULT}") +endif() + +execute_process( + COMMAND "${CMAKE_COMMAND}" -E env + "TEST_EXECUTION_MODE=${TEST_EXECUTION_MODE}" + "${TEST_EXE}" + "--gtest_output=xml:${TEST_XML}" + RESULT_VARIABLE TEST_RESULT +) + +file(LOCK "${LOCK_FILE}" RELEASE) + +if(NOT TEST_RESULT EQUAL 0) + message(FATAL_ERROR "Test execution failed with code ${TEST_RESULT}: ${TEST_EXE}") +endif() diff --git a/tests/component_tests/app_connect/CMakeLists.txt b/tests/component_tests/app_connect/CMakeLists.txt new file mode 100644 index 0000000..d80398a --- /dev/null +++ b/tests/component_tests/app_connect/CMakeLists.txt @@ -0,0 +1,156 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 ZF Friedrichshafen AG +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Erik Verhoeven - initial API and implementation +#******************************************************************************* + +# Define project +project (ComponentTest_App_Connect VERSION 1.0 LANGUAGES CXX) + +# Find the IDL compiler +# REMARKS The compiler can only be found after it has build. Having both the compiler and the unittest project build, causes an +# error during the scanning phase of CMake. +#find_program(SDVIDL NAMES "sdv_idl_compiler" PATHS "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../../bin/" NO_CACHE) +set (SDVIDL "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../../bin/sdv_idl_compiler") +message("Use IDL compiler: ${SDVIDL}") + +# Compile the IDL +add_custom_command( + OUTPUT ${PROJECT_SOURCE_DIR}/generated/test_component.h ${PROJECT_SOURCE_DIR}/generated/ps/proxystub.cpp ${PROJECT_SOURCE_DIR}/generated/serdes/test_component_serdes.h + DEPENDS sdv_idl_compiler + MAIN_DEPENDENCY test_component.idl + COMMENT "Build test_component.idl" + COMMAND ${SDVIDL} "${PROJECT_SOURCE_DIR}/test_component.idl" "-I${PROJECT_SOURCE_DIR}/../../../export" "-O${PROJECT_SOURCE_DIR}/generated" + VERBATIM + ) + +set_source_files_properties(test_component_ps.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h) +set_source_files_properties(test_component_ps.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/ps/proxystub.cpp) +set_source_files_properties(test_component_ps.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/serdes/test_component_serdes.h) +set_source_files_properties(test_component_device.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h) +set_source_files_properties(test_component_basic_service.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h) +set_source_files_properties(test_component_complex_service.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h) +set_source_files_properties(test_component_system_service.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h) +set_source_files_properties(test_component_utility.cpp OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/generated/test_component.h) + +# Create the proxy stub dynamic library +add_library(AppConnect_Component_ps SHARED "test_component_ps.cpp") +set_target_properties(AppConnect_Component_ps PROPERTIES PREFIX "") +set_target_properties(AppConnect_Component_ps PROPERTIES SUFFIX ".sdv") + +# Create the device dynamic library +add_library(AppConnect_Component_Device SHARED "test_component_device.cpp") +set_target_properties(AppConnect_Component_Device PROPERTIES PREFIX "") +set_target_properties(AppConnect_Component_Device PROPERTIES SUFFIX ".sdv") + +# Create the basic service dynamic library +add_library(AppConnect_Component_BasicService SHARED "test_component_basic_service.cpp") +set_target_properties(AppConnect_Component_BasicService PROPERTIES PREFIX "") +set_target_properties(AppConnect_Component_BasicService PROPERTIES SUFFIX ".sdv") + +# Create the complex service dynamic library +add_library(AppConnect_Component_ComplexService SHARED "test_component_complex_service.cpp") +set_target_properties(AppConnect_Component_ComplexService PROPERTIES PREFIX "") +set_target_properties(AppConnect_Component_ComplexService PROPERTIES SUFFIX ".sdv") + +# Create the system service dynamic library +add_library(AppConnect_Component_SystemService SHARED "test_component_system_service.cpp") +set_target_properties(AppConnect_Component_SystemService PROPERTIES PREFIX "") +set_target_properties(AppConnect_Component_SystemService PROPERTIES SUFFIX ".sdv") + +# Create the utility dynamic library +add_library(AppConnect_Component_Utility SHARED "test_component_utility.cpp") +set_target_properties(AppConnect_Component_Utility PROPERTIES PREFIX "") +set_target_properties(AppConnect_Component_Utility PROPERTIES SUFFIX ".sdv") + +# Server instances: Install the device, system, basic and complex service, utility and prox/stub components in server instances +# shared memory: 195 and 196 +# UDS: 199 and 200 +# Tunnel-UDS: 203 and 204 +# Client instances: Install the proxy/stub components +# shared memory: 197 and 198 +# UDS: 201 and 202 +# Tunnel-UDS: 205 and 206 +add_custom_target(AppConnect_Component_Install + COMMAND "$" DIRECT_INSTALL AppConnect_Component --instance195 "AppConnect_Component_*.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component --instance196 "AppConnect_Component_*.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component_ps --instance197 "AppConnect_Component_ps.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component_ps --instance198 "AppConnect_Component_ps.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component --instance199 "AppConnect_Component_*.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component --instance200 "AppConnect_Component_*.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component_ps --instance201 "AppConnect_Component_ps.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component_ps --instance202 "AppConnect_Component_ps.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component --instance203 "AppConnect_Component_*.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component --instance204 "AppConnect_Component_*.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component_ps --instance205 "AppConnect_Component_ps.sdv" "-I$" --overwrite + COMMAND "$" DIRECT_INSTALL AppConnect_Component_ps --instance206 "AppConnect_Component_ps.sdv" "-I$" --overwrite + DEPENDS dependency_sdv_components AppConnect_Component_ps AppConnect_Component_Device AppConnect_Component_BasicService AppConnect_Component_ComplexService AppConnect_Component_SystemService AppConnect_Component_Utility + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} +) + +# Configure listener for server instance +# Shared memory: 196 (195 is default) +# UDS: 199, 200 +# Tunnel-UDS: 203, 204 +# Connections for client instances +# Shared memory: 197 and 198 +# UDS: 201 and 202 +# Tunnel-UDS: 205 and 206 +add_custom_target(AppConnect_Communication_Install + COMMAND "$" LISTENER ADD MySpecialListener DefaultSharedMemory Name=private_channel_196_198 --instance196 + COMMAND "$" CONNECTION ADD DefaultChannelTo195 DefaultSharedMemory Name=DEFAULT_CHANNEL_195 --instance197 + COMMAND "$" CONNECTION ADD MySpecialConnection DefaultSharedMemory Name=private_channel_196_198 --instance198 + COMMAND "$" LISTENER ADD Default unix_domain_sockets Name=DEFAULT_CHANNEL_199 --instance199 + COMMAND "$" LISTENER ADD MySpecialListener unix_domain_sockets Name=private_channel_200_202 --instance200 + COMMAND "$" CONNECTION ADD DefaultChannelTo199 unix_domain_sockets Name=DEFAULT_CHANNEL_199 --instance201 + COMMAND "$" CONNECTION ADD MySpecialConnection unix_domain_sockets Name=private_channel_200_202 --instance202 + COMMAND "$" LISTENER ADD Default unix_domain_sockets_tunnel Name=DEFAULT_CHANNEL_203 --instance203 + COMMAND "$" LISTENER ADD MySpecialListener unix_domain_sockets_tunnel Name=private_channel_204_206 --instance204 + COMMAND "$" CONNECTION ADD DefaultChannelTo203 unix_domain_sockets_tunnel Name=DEFAULT_CHANNEL_203 --instance205 + COMMAND "$" CONNECTION ADD MySpecialConnection unix_domain_sockets_tunnel Name=private_channel_204_206 --instance206 + DEPENDS dependency_sdv_components AppConnect_Component_ps AppConnect_Component_Device AppConnect_Component_BasicService AppConnect_Component_ComplexService AppConnect_Component_SystemService AppConnect_Component_Utility + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} +) + +# Compile the source code of the server +add_executable(AppConnect_Server + "main_server.cpp") +target_link_libraries(AppConnect_Server ${CMAKE_DL_LIBS}) + +# Compile the source code +add_executable(ComponentTest_App_Connect + "main.cpp" + "app_connect_tests_shared_mem.cpp" + "app_connect_tests_uds.cpp" + "app_connect_tests_uds_tunnel.cpp" + ) +target_link_libraries(ComponentTest_App_Connect ${CMAKE_DL_LIBS} GTest::GTest) + +# Add the connect test +add_test(NAME ComponentTest_App_Connect COMMAND ComponentTest_App_Connect) + +# Execute the test +add_custom_command(TARGET ComponentTest_App_Connect POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ComponentTest_App_Connect.xml + VERBATIM +) + +# Build dependencies +add_dependencies(AppConnect_Server dependency_sdv_components) +add_dependencies(ComponentTest_App_Connect dependency_sdv_components) +add_dependencies(ComponentTest_App_Connect AppConnect_Server) +add_dependencies(ComponentTest_App_Connect AppConnect_Component_Install) +add_dependencies(ComponentTest_App_Connect AppConnect_Communication_Install) +add_dependencies(AppConnect_Component_ps dependency_sdv_components) +add_dependencies(AppConnect_Component_Device dependency_sdv_components) +add_dependencies(AppConnect_Component_BasicService dependency_sdv_components) +add_dependencies(AppConnect_Component_ComplexService dependency_sdv_components) +add_dependencies(AppConnect_Component_SystemService dependency_sdv_components) +add_dependencies(AppConnect_Component_Utility dependency_sdv_components) diff --git a/tests/component_tests/app_connect/app_connect_tests_shared_mem.cpp b/tests/component_tests/app_connect/app_connect_tests_shared_mem.cpp new file mode 100644 index 0000000..c2d3197 --- /dev/null +++ b/tests/component_tests/app_connect/app_connect_tests_shared_mem.cpp @@ -0,0 +1,519 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include "../../../sdv_services/process_control/process_control.h" +#include "../../../global/ipc_named_mutex.h" +#include "../../../global/exec_dir_helper.h" +#include "generated/test_component.h" +#include +#include +#include +#include + +// The test use the following instance numbers (all configured as main application): +// - instance 195 server without additional listener configured (channel: DEFAULT_CHANNEL_195) +// - instance 196 server with additional listener configured (channels: DEFAULT_CHANNEL_196 and private_channel_196_198) +// - instance 197 client with 195-default connection configured (channel: DEFAULT_CHANNEL_195) +// - instance 198 client with 196-special connection configured (channel: private_channel_196_198) + +TEST(AppConnect, ServerStart_DefaultSharedMemory) +{ + // The default instance + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 195 +[Console] +RedirectMon = true +)toml"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main); + EXPECT_EQ(control.GetInstanceID(), 195u); + + // Within the server instance all objects should be accessible. + // NOTE: The complex service is running isolated. + + // Test utility + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_TRUE(ptrUtilityHelloObject); + auto pHelloObject = ptrUtilityHelloObject.GetInterface(); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Utility reports:" << std::endl << ss; + + // Test system object + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + // Test device object - executes SayHello on Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - executes SayHello on Device, Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + //// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service + //sStages = {}; + //ss.clear(); + //pHelloObject = sdv::core::GetObject("TestObject_ComplexHelloService"); + //EXPECT_TRUE(pHelloObject); + //if (pHelloObject) ss = pHelloObject->SayHello(sStages); + //EXPECT_FALSE(ss.empty()); + //EXPECT_TRUE(sStages.bDevice); + //EXPECT_TRUE(sStages.bBasicService); + //EXPECT_TRUE(sStages.bComplexService); + //EXPECT_TRUE(sStages.bSystemService); + //EXPECT_TRUE(sStages.bUtility); + //std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppConnect, ServerStart_PrivateSharedMemory) +{ + // The default instance + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 196 +[Console] +RedirectMon = true +)toml"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main); + EXPECT_EQ(control.GetInstanceID(), 196u); + + // Test utility + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_TRUE(ptrUtilityHelloObject); + auto pHelloObject = ptrUtilityHelloObject.GetInterface(); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Utility reports:" << std::endl << ss; + + // Test system object + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + // Test device object - executes SayHello on Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - executes SayHello on Device, Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + //// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service + // sStages = {}; + // ss.clear(); + // pHelloObject = sdv::core::GetObject("TestObject_ComplexHelloService"); + // EXPECT_TRUE(pHelloObject); + // if (pHelloObject) ss = pHelloObject->SayHello(sStages); + // EXPECT_FALSE(ss.empty()); + // EXPECT_TRUE(sStages.bDevice); + // EXPECT_TRUE(sStages.bBasicService); + // EXPECT_TRUE(sStages.bComplexService); + // EXPECT_TRUE(sStages.bSystemService); + // EXPECT_TRUE(sStages.bUtility); + // std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppConnect, ServerApp_Control_SharedMem) +{ + // The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex + ipc::named_mutex mtx("app_connect_test_mutex"); + std::unique_lock lock(mtx); + + // Use the process control class to manage the server process + // Enable access bypass to allow its usage. + CProcessControl procctrl; + procctrl.EnableProcessControlAccessBypass(); + procctrl.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized); + + // Execute the server process + sdv::process::TProcessID tServerID = procctrl.Execute( + (GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"195"}, sdv::process::EProcessRights::default_rights); + EXPECT_NE(tServerID, 0u); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server + EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1)); + + // Release the mutex; this should shutdown the server + lock.unlock(); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server - should be terminated + EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000)); + + // Just in case termination + procctrl.Terminate(tServerID); + + // End process control + procctrl.Shutdown(); +} + +TEST(AppConnect, ClientConnect_DefaultSharedMem) +{ + // The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex + ipc::named_mutex mtx("app_connect_test_mutex"); + std::unique_lock lock(mtx); + + // Use the process control class to manage the server process + // Enable access bypass to allow its usage. + CProcessControl procctrl; + procctrl.EnableProcessControlAccessBypass(); + procctrl.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized); + + // Execute the server process + sdv::process::TProcessID tServerID = procctrl.Execute( + (GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"195"}, sdv::process::EProcessRights::default_rights); + EXPECT_NE(tServerID, 0u); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server + EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1)); + + // Start the second system using the default instance. This simulates a client + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // When startup succeeds an automatic connection to the server 195 should have been made. + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 197 +[Console] +RedirectMon = true +)toml"); + EXPECT_TRUE(bResult); + + // Test utility - not allowed + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_FALSE(ptrUtilityHelloObject); + + // Test system object - not allowed BUT CURRENTLY WORKING + sStages = {}; + ss.clear(); + auto pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + //// Test device object - not allowed BUT CURRENTLY WORKING + //sStages = {}; + //ss.clear(); + //pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + //EXPECT_TRUE(pHelloObject); + //if (pHelloObject) + // ss = pHelloObject->SayHello(sStages); + //EXPECT_FALSE(ss.empty()); + //EXPECT_TRUE(sStages.bDevice); + //EXPECT_FALSE(sStages.bBasicService); + //EXPECT_FALSE(sStages.bComplexService); + //EXPECT_TRUE(sStages.bSystemService); + //EXPECT_TRUE(sStages.bUtility); + //std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - tries to executes SayHello on Device, Utility and System Service, but not allowed + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + //// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service + // sStages = {}; + // ss.clear(); + // pHelloObject = sdv::core::GetObject("TestObject_ComplexHelloService"); + // EXPECT_TRUE(pHelloObject); + // if (pHelloObject) ss = pHelloObject->SayHello(sStages); + // EXPECT_FALSE(ss.empty()); + // EXPECT_TRUE(sStages.bDevice); + // EXPECT_TRUE(sStages.bBasicService); + // EXPECT_TRUE(sStages.bComplexService); + // EXPECT_TRUE(sStages.bSystemService); + // EXPECT_TRUE(sStages.bUtility); + // std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // Release the mutex; this should shutdown the server + lock.unlock(); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server - should be terminated + EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000)); + + // Just in case termination + procctrl.Terminate(tServerID); + + // End process control + procctrl.Shutdown(); +} + +TEST(AppConnect, ClientConnect_PrivateSharedMem) +{ + // The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex + ipc::named_mutex mtx("app_connect_test_mutex"); + std::unique_lock lock(mtx); + + // Use the process control class to manage the server process + // Enable access bypass to allow its usage. + CProcessControl procctrl; + procctrl.EnableProcessControlAccessBypass(); + procctrl.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized); + + // Execute the server process + sdv::process::TProcessID tServerID = procctrl.Execute( + (GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"196"}, sdv::process::EProcessRights::default_rights); + EXPECT_NE(tServerID, 0u); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server + EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1)); + + // Start the second system using the default instance. This simulates a client + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // When startup succeeds an automatic connection to the server 196 should have been made. + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 198 +[Console] +RedirectMon = true +)toml"); + EXPECT_TRUE(bResult); + + // Test utility - not allowed + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_FALSE(ptrUtilityHelloObject); + + // Test system object - not allowed BUT CURRENTLY WORKING + sStages = {}; + ss.clear(); + auto pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + // Test device object - not allowed BUT CURRENTLY WORKING + //sStages = {}; + //ss.clear(); + //pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + //EXPECT_TRUE(pHelloObject); + //if (pHelloObject) + // ss = pHelloObject->SayHello(sStages); + //EXPECT_FALSE(ss.empty()); + //EXPECT_TRUE(sStages.bDevice); + //EXPECT_FALSE(sStages.bBasicService); + //EXPECT_FALSE(sStages.bComplexService); + //EXPECT_TRUE(sStages.bSystemService); + //EXPECT_TRUE(sStages.bUtility); + //std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - executes SayHello on Device, Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + //// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service + //sStages = {}; + //ss.clear(); + //pHelloObject = sdv::core::GetObject("TestObject_ComplexHelloService"); + //EXPECT_TRUE(pHelloObject); + //if (pHelloObject) ss = pHelloObject->SayHello(sStages); + //EXPECT_FALSE(ss.empty()); + //EXPECT_TRUE(sStages.bDevice); + //EXPECT_TRUE(sStages.bBasicService); + //EXPECT_TRUE(sStages.bComplexService); + //EXPECT_TRUE(sStages.bSystemService); + //EXPECT_TRUE(sStages.bUtility); + //std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // Release the mutex; this should shutdown the server + lock.unlock(); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server - should be terminated + EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000)); + + // Just in case termination + procctrl.Terminate(tServerID); + + // End process control + procctrl.Shutdown(); +} diff --git a/tests/component_tests/app_connect/app_connect_tests_uds.cpp b/tests/component_tests/app_connect/app_connect_tests_uds.cpp new file mode 100644 index 0000000..7fb5de9 --- /dev/null +++ b/tests/component_tests/app_connect/app_connect_tests_uds.cpp @@ -0,0 +1,519 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include "../../../sdv_services/process_control/process_control.h" +#include "../../../global/ipc_named_mutex.h" +#include "../../../global/exec_dir_helper.h" +#include "generated/test_component.h" +#include +#include +#include +#include + +// The test use the following instance numbers (all configured as main application): +// - instance 199 server without additional listener configured (channel: DEFAULT_CHANNEL_199) +// - instance 200 server with additional listener configured (channels: DEFAULT_CHANNEL_200 and private_channel_200_202) +// - instance 201 client with 199-default connection configured (channel: DEFAULT_CHANNEL_199) +// - instance 202 client with 200-special connection configured (channel: private_channel_200_202) + +TEST(AppConnect_UDS, ServerStart_DefaultUnixDomainSockets) +{ + // The default instance + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 199 +[Console] +RedirectMon = true +)toml"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main); + EXPECT_EQ(control.GetInstanceID(), 199u); + + // Within the server instance all objects should be accessible. + // NOTE: The complex service is running isolated. + + // Test utility + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_TRUE(ptrUtilityHelloObject); + auto pHelloObject = ptrUtilityHelloObject.GetInterface(); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Utility reports:" << std::endl << ss; + + // Test system object + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + // Test device object - executes SayHello on Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - executes SayHello on Device, Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + //// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service + //sStages = {}; + //ss.clear(); + //pHelloObject = sdv::core::GetObject("TestObject_ComplexHelloService"); + //EXPECT_TRUE(pHelloObject); + //if (pHelloObject) ss = pHelloObject->SayHello(sStages); + //EXPECT_FALSE(ss.empty()); + //EXPECT_TRUE(sStages.bDevice); + //EXPECT_TRUE(sStages.bBasicService); + //EXPECT_TRUE(sStages.bComplexService); + //EXPECT_TRUE(sStages.bSystemService); + //EXPECT_TRUE(sStages.bUtility); + //std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppConnect_UDS, ServerStart_PrivateUnixDomainSockets) +{ + // The default instance + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 200 +[Console] +RedirectMon = true +)toml"); + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main); + EXPECT_EQ(control.GetInstanceID(), 200u); + + // Test utility + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_TRUE(ptrUtilityHelloObject); + auto pHelloObject = ptrUtilityHelloObject.GetInterface(); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Utility reports:" << std::endl << ss; + + // Test system object + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + // Test device object - executes SayHello on Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - executes SayHello on Device, Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + //// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service + // sStages = {}; + // ss.clear(); + // pHelloObject = sdv::core::GetObject("TestObject_ComplexHelloService"); + // EXPECT_TRUE(pHelloObject); + // if (pHelloObject) ss = pHelloObject->SayHello(sStages); + // EXPECT_FALSE(ss.empty()); + // EXPECT_TRUE(sStages.bDevice); + // EXPECT_TRUE(sStages.bBasicService); + // EXPECT_TRUE(sStages.bComplexService); + // EXPECT_TRUE(sStages.bSystemService); + // EXPECT_TRUE(sStages.bUtility); + // std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppConnect_UDS, ServerApp_Control_UnixDomainSockets) +{ + // The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex + ipc::named_mutex mtx("app_connect_test_mutex"); + std::unique_lock lock(mtx); + + // Use the process control class to manage the server process + // Enable access bypass to allow its usage. + CProcessControl procctrl; + procctrl.EnableProcessControlAccessBypass(); + procctrl.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized); + + // Execute the server process + sdv::process::TProcessID tServerID = procctrl.Execute( + (GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"199"}, sdv::process::EProcessRights::default_rights); + EXPECT_NE(tServerID, 0u); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server + EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1)); + + // Release the mutex; this should shutdown the server + lock.unlock(); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server - should be terminated + EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000)); + + // Just in case termination + procctrl.Terminate(tServerID); + + // End process control + procctrl.Shutdown(); +} + +TEST(AppConnect_UDS, ClientConnect_DefaultUnixDomainSockets) +{ + // The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex + ipc::named_mutex mtx("app_connect_test_mutex"); + std::unique_lock lock(mtx); + + // Use the process control class to manage the server process + // Enable access bypass to allow its usage. + CProcessControl procctrl; + procctrl.EnableProcessControlAccessBypass(); + procctrl.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized); + + // Execute the server process + sdv::process::TProcessID tServerID = procctrl.Execute( + (GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"199"}, sdv::process::EProcessRights::default_rights); + EXPECT_NE(tServerID, 0u); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server + EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1)); + + // Start the second system using the default instance. This simulates a client + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // When startup succeeds an automatic connection to the server 199 should have been made. + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 201 +[Console] +RedirectMon = true +)toml"); + EXPECT_TRUE(bResult); + + // Test utility - not allowed + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_FALSE(ptrUtilityHelloObject); + + // Test system object - not allowed BUT CURRENTLY WORKING + sStages = {}; + ss.clear(); + auto pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + //// Test device object - not allowed BUT CURRENTLY WORKING + //sStages = {}; + //ss.clear(); + //pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + //EXPECT_TRUE(pHelloObject); + //if (pHelloObject) + // ss = pHelloObject->SayHello(sStages); + //EXPECT_FALSE(ss.empty()); + //EXPECT_TRUE(sStages.bDevice); + //EXPECT_FALSE(sStages.bBasicService); + //EXPECT_FALSE(sStages.bComplexService); + //EXPECT_TRUE(sStages.bSystemService); + //EXPECT_TRUE(sStages.bUtility); + //std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - tries to executes SayHello on Device, Utility and System Service, but not allowed + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + //// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service + // sStages = {}; + // ss.clear(); + // pHelloObject = sdv::core::GetObject("TestObject_ComplexHelloService"); + // EXPECT_TRUE(pHelloObject); + // if (pHelloObject) ss = pHelloObject->SayHello(sStages); + // EXPECT_FALSE(ss.empty()); + // EXPECT_TRUE(sStages.bDevice); + // EXPECT_TRUE(sStages.bBasicService); + // EXPECT_TRUE(sStages.bComplexService); + // EXPECT_TRUE(sStages.bSystemService); + // EXPECT_TRUE(sStages.bUtility); + // std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // Release the mutex; this should shutdown the server + lock.unlock(); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server - should be terminated + EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000)); + + // Just in case termination + procctrl.Terminate(tServerID); + + // End process control + procctrl.Shutdown(); +} + +TEST(AppConnect_UDS, ClientConnect_PrivateUnixDomainSockets) +{ + // The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex + ipc::named_mutex mtx("app_connect_test_mutex"); + std::unique_lock lock(mtx); + + // Use the process control class to manage the server process + // Enable access bypass to allow its usage. + CProcessControl procctrl; + procctrl.EnableProcessControlAccessBypass(); + procctrl.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized); + + // Execute the server process + sdv::process::TProcessID tServerID = procctrl.Execute( + (GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"200"}, sdv::process::EProcessRights::default_rights); + EXPECT_NE(tServerID, 0u); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server + EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1)); + + // Start the second system using the default instance. This simulates a client + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // When startup succeeds an automatic connection to the server 200 should have been made. + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 202 +[Console] +RedirectMon = true +)toml"); + EXPECT_TRUE(bResult); + + // Test utility - not allowed + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_FALSE(ptrUtilityHelloObject); + + // Test system object - not allowed BUT CURRENTLY WORKING + sStages = {}; + ss.clear(); + auto pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + //// Test device object - not allowed BUT CURRENTLY WORKING + //sStages = {}; + //ss.clear(); + //pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + //EXPECT_TRUE(pHelloObject); + //if (pHelloObject) + // ss = pHelloObject->SayHello(sStages); + //EXPECT_FALSE(ss.empty()); + //EXPECT_TRUE(sStages.bDevice); + //EXPECT_FALSE(sStages.bBasicService); + //EXPECT_FALSE(sStages.bComplexService); + //EXPECT_TRUE(sStages.bSystemService); + //EXPECT_TRUE(sStages.bUtility); + //std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - tries to executes SayHello on Device, Utility and System Service, but not allowed + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + //// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service + // sStages = {}; + // ss.clear(); + // pHelloObject = sdv::core::GetObject("TestObject_ComplexHelloService"); + // EXPECT_TRUE(pHelloObject); + // if (pHelloObject) ss = pHelloObject->SayHello(sStages); + // EXPECT_FALSE(ss.empty()); + // EXPECT_TRUE(sStages.bDevice); + // EXPECT_TRUE(sStages.bBasicService); + // EXPECT_TRUE(sStages.bComplexService); + // EXPECT_TRUE(sStages.bSystemService); + // EXPECT_TRUE(sStages.bUtility); + // std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // Release the mutex; this should shutdown the server + lock.unlock(); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server - should be terminated + EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000)); + + // Just in case termination + procctrl.Terminate(tServerID); + + // End process control + procctrl.Shutdown(); +} diff --git a/tests/component_tests/app_connect/app_connect_tests_uds_tunnel.cpp b/tests/component_tests/app_connect/app_connect_tests_uds_tunnel.cpp new file mode 100644 index 0000000..2fec0dc --- /dev/null +++ b/tests/component_tests/app_connect/app_connect_tests_uds_tunnel.cpp @@ -0,0 +1,557 @@ +/******************************************************************************** + * Copyright (c) 2065-2066 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Denisa Ros - initial API and implementation + ********************************************************************************/ + +#include +#include "../../../sdv_services/process_control/process_control.h" +#include "../../../global/ipc_named_mutex.h" +#include "../../../global/exec_dir_helper.h" +#include "generated/test_component.h" +#include +#include +#include +#include +#include +#include + +namespace +{ +void ExpectConfiguredProvider(uint32_t instanceId, const std::string& configName, bool isListener, const std::string& expectedProvider) +{ + auto* pConnections = sdv::core::GetObject("AppSettingsService"); + if (pConnections) + { + const sdv::u8string cfg = isListener ? pConnections->GetListenerConfig(configName) + : pConnections->GetConnectionConfig(configName); + ASSERT_FALSE(cfg.empty()) << "Missing IPC config '" << configName << "'"; + + const std::string cfgStr = cfg; + EXPECT_NE(cfgStr.find("Name = \"" + expectedProvider + "\""), std::string::npos) + << "Unexpected provider config for '" << configName << "':\n" << cfgStr; + return; + } + + // In main app mode this service is not guaranteed to be directly accessible via repository object lookup. + // Fallback: validate the installed instance settings file where listener/connection definitions are persisted. + std::filesystem::path installDir; + if (const char* pInstall = std::getenv("SDV_COMPONENT_INSTALL")) + installDir = pInstall; + else + installDir = (GetExecDirectory() / ".." / ".." / "bin").lexically_normal(); + + const auto settingsPath = installDir / std::to_string(instanceId) / "settings.toml"; + std::ifstream settingsStream(settingsPath); + ASSERT_TRUE(settingsStream.is_open()) << "Cannot open settings file: " << settingsPath.generic_u8string(); + + std::stringstream buffer; + buffer << settingsStream.rdbuf(); + const std::string content = buffer.str(); + + const std::string blockStart = isListener ? "[[Settings.Listener]]" : "[[Settings.Connection]]"; + const std::string providerSection = isListener ? "[Settings.Listener.Provider]" : "[Settings.Connection.Provider]"; + const std::string expectedName = "Name = \"" + configName + "\""; + const std::string expectedProviderName = "Name = \"" + expectedProvider + "\""; + + size_t pos = content.find(blockStart); + while (pos != std::string::npos) + { + const size_t nextPos = content.find(blockStart, pos + blockStart.size()); + const std::string block = content.substr(pos, (nextPos == std::string::npos) ? std::string::npos : nextPos - pos); + if (block.find(expectedName) != std::string::npos) + { + EXPECT_NE(block.find(providerSection), std::string::npos) + << "Missing provider section in config block for '" << configName << "'"; + EXPECT_NE(block.find(expectedProviderName), std::string::npos) + << "Unexpected provider in settings block for '" << configName << "':\n" << block; + return; + } + pos = nextPos; + } + + FAIL() << "Missing IPC config block '" << configName << "' in " << settingsPath.generic_u8string(); +} +} // namespace + + +// The test use the following instance numbers (all configured as main application): +// - instance 203 server without additional listener configured (channel: DEFAULT_CHANNEL_203) +// - instance 204 server with additional listener configured (channels: private_channel_204_206) +// - instance 205 client with 203-default connection configured (channel: DEFAULT_CHANNEL_203) +// - instance 206 client with 204-special connection configured (channel: private_channel_204_206) +TEST(AppConnect_UDS_Tunnel, ServerStart_DefaultUDS_Tunnel) +{ + // The default instance + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 203 +[Console] +RedirectMon = true +)toml"); + + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main); + EXPECT_EQ(control.GetInstanceID(), 203u); + + // Test utility + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_TRUE(ptrUtilityHelloObject); + auto pHelloObject = ptrUtilityHelloObject.GetInterface(); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Utility reports:" << std::endl << ss; + + // Test system object + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + // Test device object - executes SayHello on Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - executes SayHello on Device, Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppConnect_UDS_Tunnel, ServerStart_PrivateUDS_Tunnel) +{ + // The default instance + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 204 +[Console] +RedirectMon = true +)toml"); + + EXPECT_TRUE(bResult); + EXPECT_TRUE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main); + EXPECT_EQ(control.GetInstanceID(), 204u); + ExpectConfiguredProvider(204u, "MySpecialListener", true, "unix_domain_sockets_tunnel"); + + // Test utility + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_TRUE(ptrUtilityHelloObject); + auto pHelloObject = ptrUtilityHelloObject.GetInterface(); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Utility reports:" << std::endl << ss; + + // Test system object + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + // Test device object - executes SayHello on Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - executes SayHello on Device, Utility and System Service + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_TRUE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_TRUE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + //// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service + // sStages = {}; + // ss.clear(); + // pHelloObject = sdv::core::GetObject("TestObject_ComplexHelloService"); + // EXPECT_TRUE(pHelloObject); + // if (pHelloObject) ss = pHelloObject->SayHello(sStages); + // EXPECT_FALSE(ss.empty()); + // EXPECT_TRUE(sStages.bDevice); + // EXPECT_TRUE(sStages.bBasicService); + // EXPECT_TRUE(sStages.bComplexService); + // EXPECT_TRUE(sStages.bSystemService); + // EXPECT_TRUE(sStages.bUtility); + // std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); +} + +TEST(AppConnect_UDS_Tunnel, ServerApp_Control_UDS_Tunnel) +{ + // The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex + ipc::named_mutex mtx("app_connect_tunnel_test_mutex"); + std::unique_lock lock(mtx); + + // Use the process control class to manage the server process + // Enable access bypass to allow its usage. + CProcessControl procctrl; + procctrl.EnableProcessControlAccessBypass(); + procctrl.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized); + + // Execute the server process + sdv::process::TProcessID tServerID = procctrl.Execute( + (GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"203"}, sdv::process::EProcessRights::default_rights); + EXPECT_NE(tServerID, 0u); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server + EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1)); + + // Release the mutex; this should shutdown the server + lock.unlock(); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server - should be terminated + EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000)); + + // Just in case termination + procctrl.Terminate(tServerID); + + // End process control + procctrl.Shutdown(); +} + +TEST(AppConnect_UDS_Tunnel, ClientConnect_DefaultUDS_Tunnel) +{ + // The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex + ipc::named_mutex mtx("app_connect_tunnel_test_mutex"); + std::unique_lock lock(mtx); + + // Use the process control class to manage the server process + // Enable access bypass to allow its usage. + CProcessControl procctrl; + procctrl.EnableProcessControlAccessBypass(); + procctrl.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized); + + // Execute the server process + sdv::process::TProcessID tServerID = procctrl.Execute( + (GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"203"}, sdv::process::EProcessRights::default_rights); + EXPECT_NE(tServerID, 0u); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server + EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1)); + + // Start the second system using the default instance. This simulates a client + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // When startup succeeds an automatic connection to the server 203 should have been made. + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 205 +[Console] +RedirectMon = true +)toml"); + EXPECT_TRUE(bResult); + ExpectConfiguredProvider(205u, "DefaultChannelTo203", false, "unix_domain_sockets_tunnel"); + + // Test utility - not allowed + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_FALSE(ptrUtilityHelloObject); + + // Test system object - not allowed BUT CURRENTLY WORKING + sStages = {}; + ss.clear(); + auto pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + //// Test device object - not allowed BUT CURRENTLY WORKING + //sStages = {}; + //ss.clear(); + //pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + //EXPECT_TRUE(pHelloObject); + //if (pHelloObject) + // ss = pHelloObject->SayHello(sStages); + //EXPECT_FALSE(ss.empty()); + //EXPECT_TRUE(sStages.bDevice); + //EXPECT_FALSE(sStages.bBasicService); + //EXPECT_FALSE(sStages.bComplexService); + //EXPECT_TRUE(sStages.bSystemService); + //EXPECT_TRUE(sStages.bUtility); + //std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - tries to executes SayHello on Device, Utility and System Service, but not allowed + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // Release the mutex; this should shutdown the server + lock.unlock(); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server - should be terminated + EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000)); + + // Just in case termination + procctrl.Terminate(tServerID); + + // End process control + procctrl.Shutdown(); +} + +TEST(AppConnect_UDS_Tunnel, ClientConnect_PrivateUDS_Tunnel) +{ + // The lifetime of the server app is managed through a named mutex being available for locking. Lock the mutex + ipc::named_mutex mtx("app_connect_tunnel_test_mutex"); + std::unique_lock lock(mtx); + + // Use the process control class to manage the server process + // Enable access bypass to allow its usage. + CProcessControl procctrl; + procctrl.EnableProcessControlAccessBypass(); + procctrl.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(procctrl.GetObjectState(), sdv::EObjectState::initialized); + + // Execute the server process + sdv::process::TProcessID tServerID = procctrl.Execute( + (GetExecDirectory() / "AppConnect_Server").generic_u8string(), {"204"}, sdv::process::EProcessRights::default_rights); + EXPECT_NE(tServerID, 0u); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server + EXPECT_FALSE(procctrl.WaitForTerminate(tServerID, 1)); + + // Start the second system using the default instance. This simulates a client + sdv::app::CAppControl control; + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // When startup succeeds an automatic connection to the server 204 should have been made. + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 206 +[Console] +RedirectMon = true +)toml"); + EXPECT_TRUE(bResult); + ExpectConfiguredProvider(206u, "MySpecialConnection", false, "unix_domain_sockets_tunnel"); + + // Test utility - not allowed + SStages sStages{}; + std::string ss; + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + EXPECT_FALSE(ptrUtilityHelloObject); + + // Test system object - not allowed BUT CURRENTLY WORKING + sStages = {}; + ss.clear(); + auto pHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_FALSE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_TRUE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "System service reports:" << std::endl << ss; + + //// Test device object - not allowed BUT CURRENTLY WORKING + //sStages = {}; + //ss.clear(); + //pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + //EXPECT_TRUE(pHelloObject); + //if (pHelloObject) + // ss = pHelloObject->SayHello(sStages); + //EXPECT_FALSE(ss.empty()); + //EXPECT_TRUE(sStages.bDevice); + //EXPECT_FALSE(sStages.bBasicService); + //EXPECT_FALSE(sStages.bComplexService); + //EXPECT_TRUE(sStages.bSystemService); + //EXPECT_TRUE(sStages.bUtility); + //std::cout << "Device reports:" << std::endl << ss; + + // Test basic service object - tries to executes SayHello on Device, Utility and System Service, but not allowed + sStages = {}; + ss.clear(); + pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + EXPECT_TRUE(pHelloObject); + if (pHelloObject) + ss = pHelloObject->SayHello(sStages); + EXPECT_FALSE(ss.empty()); + EXPECT_FALSE(sStages.bDevice); + EXPECT_TRUE(sStages.bBasicService); + EXPECT_FALSE(sStages.bComplexService); + EXPECT_FALSE(sStages.bSystemService); + EXPECT_FALSE(sStages.bUtility); + std::cout << "Basic service reports:" << std::endl << ss; + + //// Test complex service object - executes SayHello on Complex Service, Device, Utility and System Service + // sStages = {}; + // ss.clear(); + // pHelloObject = sdv::core::GetObject("TestObject_ComplexHelloService"); + // EXPECT_TRUE(pHelloObject); + // if (pHelloObject) ss = pHelloObject->SayHello(sStages); + // EXPECT_FALSE(ss.empty()); + // EXPECT_TRUE(sStages.bDevice); + // EXPECT_TRUE(sStages.bBasicService); + // EXPECT_TRUE(sStages.bComplexService); + // EXPECT_TRUE(sStages.bSystemService); + // EXPECT_TRUE(sStages.bUtility); + // std::cout << "Basic service reports:" << std::endl << ss; + + control.Shutdown(); + EXPECT_FALSE(control.IsRunning()); + EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); + EXPECT_EQ(control.GetInstanceID(), 0u); + + // Release the mutex; this should shutdown the server + lock.unlock(); + + // Wait for 1 second + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Test for the running server - should be terminated + EXPECT_TRUE(procctrl.WaitForTerminate(tServerID, 10000)); + + // Just in case termination + procctrl.Terminate(tServerID); + + // End process control + procctrl.Shutdown(); +} diff --git a/tests/component_tests/app_connect/main.cpp b/tests/component_tests/app_connect/main.cpp new file mode 100644 index 0000000..64d8f7b --- /dev/null +++ b/tests/component_tests/app_connect/main.cpp @@ -0,0 +1,31 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include "../../../global/process_watchdog.h" +#include "../../../sdv_services/process_control/process_control.cpp" + +/** + * @brief Main function + */ +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain(int argc, wchar_t* argv[]) +#else +extern "C" int main(int argc, char* argv[]) +#endif +{ + CProcessWatchdog watchdog; + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/component_tests/app_connect/main_server.cpp b/tests/component_tests/app_connect/main_server.cpp new file mode 100644 index 0000000..23f581e --- /dev/null +++ b/tests/component_tests/app_connect/main_server.cpp @@ -0,0 +1,99 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include "../../../global/process_watchdog.h" +#include "../../../global/ipc_named_mutex.h" +#include +#include +#include +#include + +/** + * @brief Main function + */ +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain(int argc, wchar_t* argv[]) +#else +extern "C" int main(int argc, char* argv[]) +#endif +{ + CProcessWatchdog watchdog; + + // Workaround for GCC to make certain that POSIX thread library is loaded before the components are loaded. + // REASON: If the first call to a thread is done in a dynamic library, the application is already classified as single + // threaded and a termination is initiated. + // See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev + // NOTE EVE 27.05.2025: in release builds, starting and ending the thread right after each other causes incorrect behavior and + // leads in some cases to create a deadlock in the join-function. The solution is to add delays in the thread processing. + std::atomic_bool bThreadStarted = false; + std::thread thread = std::thread( + [&]() + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + bThreadStarted = true; + }); + while (!bThreadStarted) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + if (thread.joinable()) + thread.join(); + + std::cout << "RuntimeDirectory = " << sdv::app::CAppControl::GetFrameworkRuntimeDirectory().generic_u8string() << std::endl; + std::cout << "InstallDirectory = " << sdv::app::CAppControl::GetComponentInstallDirectory().generic_u8string() << std::endl; + + std::cout << "Connect test application" << std::endl; + std::cout << "Copyright (C): 2026 ZF Friedrichshafen AG" << std::endl; + std::cout << "Author: Erik Verhoeven" << std::endl << std::endl; + + // Get the instance number from the command line. + if (argc != 2) + { + std::cout << "Missing argument; usage: AppConnect_Client " << std::endl; + return -1; + } +#if defined(_WIN32) && defined(_UNICODE) + size_t nInstance = wcstoul(argv[1], nullptr, 10); +#else + size_t nInstance = atoi(argv[1]); +#endif + std::cout << "Listening for connections on instance #" << nInstance << std::endl; + + sdv::app::CAppControl control; + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = )toml" + std::to_string(nInstance)); + if (!bResult) + { + std::cout << "Failed to start system..." << std::endl; + return -2; + } + + // Lifetime of the server is managed by the locking of a named mutex. As long as locking doesn't work, the server should run. + // Test every 250ms + ipc::named_mutex mtx("app_connect_test_mutex"); + bool bRunning = true; + while (bRunning) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + if (mtx.try_lock()) + { + bRunning = false; + mtx.unlock(); + } + } + + control.Shutdown(); + std::cout << "Connect test application terminated... (instance #" << nInstance << ")" << std::endl; + return 0; +} diff --git a/tests/component_tests/app_connect/test_component.idl b/tests/component_tests/app_connect/test_component.idl new file mode 100644 index 0000000..4e0f46f --- /dev/null +++ b/tests/component_tests/app_connect/test_component.idl @@ -0,0 +1,46 @@ + /******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include + + /** + * @brief Stages the call might go through. + */ + struct SStages +{ + boolean bDevice; + boolean bBasicService; + boolean bComplexService; + boolean bSystemService; + boolean bUtility; +}; + +/** + * @brief Hello interface + */ +interface IHello +{ + /** + * @brief Say hello + * @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The + * basic service calls the device. The device calls the system service and the utility. + * @return The greetings string. + */ + u8string SayHello(inout SStages sStages) const; + + /** + * @brief Get the PID of the process the component is running in... + */ + sdv::process::TProcessID GetPID() const; +}; diff --git a/tests/component_tests/app_connect/test_component_basic_service.cpp b/tests/component_tests/app_connect/test_component_basic_service.cpp new file mode 100644 index 0000000..bdf2e68 --- /dev/null +++ b/tests/component_tests/app_connect/test_component_basic_service.cpp @@ -0,0 +1,72 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "generated/test_component.h" + +inline sdv::process::TProcessID GetProcessID() +{ + static sdv::process::TProcessID tProcessID = 0; + if (!tProcessID) + { + sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject("ProcessControlService"); + if (!pProcessInfo) return 0; + tProcessID = pProcessInfo->GetProcessID(); + } + return tProcessID; +} + +/** + * @brief Basic hello service + */ +class CBasicHelloService : public sdv::CSdvObject, public IHello +{ +public: + + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(IHello) + END_SDV_INTERFACE_MAP() + + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::basic_service) + DECLARE_OBJECT_CLASS_NAME("TestObject_BasicHelloService") + + /** + * @brief Say hello. Overload of IHello::SayHello. + * @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The + * basic service calls the device. The device calls the system service and the utility. + * @return The greetings string. + */ + virtual sdv::u8string SayHello(/*inout*/ SStages& sStages) const override + { + sStages.bBasicService = true; + + // Request for the device + auto pHelloObject = sdv::core::GetObject("TestObject_HelloDevice"); + return "PID#" + std::to_string(GetPID()) + " Hello from basic service\n" + + std::string(pHelloObject ? pHelloObject->SayHello(sStages) : ""); + } + + /** + * @brief Get the PID of the process the component is running in... Overload of IHello::GetPID. + */ + sdv::process::TProcessID GetPID() const + { + return GetProcessID(); + } +}; +DEFINE_SDV_OBJECT(CBasicHelloService) diff --git a/tests/component_tests/app_connect/test_component_complex_service.cpp b/tests/component_tests/app_connect/test_component_complex_service.cpp new file mode 100644 index 0000000..dc9c793 --- /dev/null +++ b/tests/component_tests/app_connect/test_component_complex_service.cpp @@ -0,0 +1,81 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "generated/test_component.h" + +inline sdv::process::TProcessID GetProcessID() +{ + static sdv::process::TProcessID tProcessID = 0; + if (!tProcessID) + { + sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject("ProcessControlService"); + if (!pProcessInfo) return 0; + tProcessID = pProcessInfo->GetProcessID(); + } + return tProcessID; +} + +/** + * @brief Complex hello service + */ +class CComplexHelloService : public sdv::CSdvObject, public IHello +{ +public: + CComplexHelloService() + { + std::cout << "CComplexHelloService constructor" << std::endl; + } + ~CComplexHelloService() + { + std::cout << "CComplexHelloService destructor" << std::endl; + } + + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(IHello) + END_SDV_INTERFACE_MAP() + + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::complex_service) + DECLARE_OBJECT_CLASS_NAME("TestObject_ComplexHelloService") + + /** + * @brief Say hello. Overload of IHello::SayHello. + * @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The + * basic service calls the device. The device calls the system service and the utility. + * @return The greetings string. + */ + virtual sdv::u8string SayHello(/*inout*/ SStages& sStages) const override + { + sStages.bComplexService = true; + + // Request for the basic server + auto pHelloObject = sdv::core::GetObject("TestObject_BasicHelloService"); + return "PID#" + std::to_string(GetPID()) + " Hello from complex service\n" + + std::string(pHelloObject ? pHelloObject->SayHello(sStages) : ""); + } + + /** + * @brief Get the PID of the process the component is running in... Overload of IHello::GetPID. + */ + sdv::process::TProcessID GetPID() const + { + return GetProcessID(); + } +}; +DEFINE_SDV_OBJECT(CComplexHelloService) + diff --git a/tests/component_tests/app_connect/test_component_device.cpp b/tests/component_tests/app_connect/test_component_device.cpp new file mode 100644 index 0000000..1d8c8c6 --- /dev/null +++ b/tests/component_tests/app_connect/test_component_device.cpp @@ -0,0 +1,76 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "generated/test_component.h" + +inline sdv::process::TProcessID GetProcessID() +{ + static sdv::process::TProcessID tProcessID = 0; + if (!tProcessID) + { + sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject("ProcessControlService"); + if (!pProcessInfo) return 0; + tProcessID = pProcessInfo->GetProcessID(); + } + return tProcessID; +} + +/** + * @brief Hello device + */ +class CHelloDevice : public sdv::CSdvObject, public IHello +{ +public: + + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(IHello) + END_SDV_INTERFACE_MAP() + + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) + DECLARE_OBJECT_CLASS_NAME("TestObject_HelloDevice") + + /** + * @brief Say hello. Overload of IHello::SayHello. + * @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The + * basic service calls the device. The device calls the system service and the utility. + * @return The greetings string. + */ + virtual sdv::u8string SayHello(/*inout*/ SStages& sStages) const override + { + sStages.bDevice = true; + + // Request for the system service + auto pSystemHelloObject = sdv::core::GetObject("TestObject_SystemHelloService"); + auto ptrUtilityHelloObject = sdv::core::CreateUtility("TestObject_HelloUtility"); + auto pUtilityHelloObject = ptrUtilityHelloObject.GetInterface(); + return "PID#" + std::to_string(GetPID()) + " Hello from device\n" + + std::string(pSystemHelloObject ? pSystemHelloObject->SayHello(sStages) : "") + + std::string(pUtilityHelloObject ? pUtilityHelloObject->SayHello(sStages) : ""); + } + + /** + * @brief Get the PID of the process the component is running in... Overload of IHello::GetPID. + */ + sdv::process::TProcessID GetPID() const + { + return GetProcessID(); + } +}; +DEFINE_SDV_OBJECT(CHelloDevice) + diff --git a/tests/unit_tests/app_connect/app_connect_local.cpp b/tests/component_tests/app_connect/test_component_ps.cpp similarity index 69% rename from tests/unit_tests/app_connect/app_connect_local.cpp rename to tests/component_tests/app_connect/test_component_ps.cpp index 2b6c398..11b64ac 100644 --- a/tests/unit_tests/app_connect/app_connect_local.cpp +++ b/tests/component_tests/app_connect/test_component_ps.cpp @@ -11,10 +11,4 @@ * Erik Verhoeven - initial API and implementation ********************************************************************************/ -/** -- Default instantiate listener -- Instantiate with specific info -- Multiple instantiate with identical info (failure) -- Multiple instantiate with different info -- Default instantiate client and get connection information -- */ \ No newline at end of file +#include "generated/ps/proxystub.cpp" \ No newline at end of file diff --git a/tests/component_tests/app_connect/test_component_system_service.cpp b/tests/component_tests/app_connect/test_component_system_service.cpp new file mode 100644 index 0000000..37c6250 --- /dev/null +++ b/tests/component_tests/app_connect/test_component_system_service.cpp @@ -0,0 +1,69 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "generated/test_component.h" + +inline sdv::process::TProcessID GetProcessID() +{ + static sdv::process::TProcessID tProcessID = 0; + if (!tProcessID) + { + sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject("ProcessControlService"); + if (!pProcessInfo) return 0; + tProcessID = pProcessInfo->GetProcessID(); + } + return tProcessID; +} + +/** + * @brief Hello utlity + */ +class CSystemHelloService : public sdv::CSdvObject, public IHello +{ +public: + + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(IHello) + END_SDV_INTERFACE_MAP() + + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) + DECLARE_OBJECT_CLASS_NAME("TestObject_SystemHelloService") + + /** + * @brief Say hello. Overload of IHello::SayHello. + * @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The + * basic service calls the device. The device calls the system service and the utility. + * @return The greetings string. + */ + virtual sdv::u8string SayHello(/*inout*/ SStages& sStages) const override + { + sStages.bSystemService = true; + + return "PID#" + std::to_string(GetPID()) + " Hello from system service\n"; + } + + /** + * @brief Get the PID of the process the component is running in... Overload of IHello::GetPID. + */ + sdv::process::TProcessID GetPID() const + { + return GetProcessID(); + } +}; +DEFINE_SDV_OBJECT(CSystemHelloService) diff --git a/tests/component_tests/app_connect/test_component_utility.cpp b/tests/component_tests/app_connect/test_component_utility.cpp new file mode 100644 index 0000000..2414ea3 --- /dev/null +++ b/tests/component_tests/app_connect/test_component_utility.cpp @@ -0,0 +1,70 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "generated/test_component.h" + +inline sdv::process::TProcessID GetProcessID() +{ + static sdv::process::TProcessID tProcessID = 0; + if (!tProcessID) + { + sdv::process::IProcessInfo* pProcessInfo = sdv::core::GetObject("ProcessControlService"); + if (!pProcessInfo) return 0; + tProcessID = pProcessInfo->GetProcessID(); + } + return tProcessID; +} + +/** + * @brief Hello utlity + */ +class CHelloUtility : public sdv::CSdvObject, public IHello +{ +public: + + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(IHello) + END_SDV_INTERFACE_MAP() + + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::utility) + DECLARE_OBJECT_CLASS_NAME("TestObject_HelloUtility") + + /** + * @brief Say hello. Overload of IHello::SayHello. + * @param[in, out] sStages The struct stages will be filled by the components. The complex service calls the basic service. The + * basic service calls the device. The device calls the system service and the utility. + * @return The greetings string. + */ + virtual sdv::u8string SayHello(/*inout*/ SStages& sStages) const override + { + sStages.bUtility = true; + + return "PID#" + std::to_string(GetPID()) + " Hello from basic service\n"; + } + + /** + * @brief Get the PID of the process the component is running in... Overload of IHello::GetPID. + */ + sdv::process::TProcessID GetPID() const + { + return GetProcessID(); + } +}; +DEFINE_SDV_OBJECT(CHelloUtility) + diff --git a/tests/component_tests/app_control/CMakeLists.txt b/tests/component_tests/app_control/CMakeLists.txt index 2467551..15f935e 100644 --- a/tests/component_tests/app_control/CMakeLists.txt +++ b/tests/component_tests/app_control/CMakeLists.txt @@ -25,9 +25,16 @@ add_executable(ComponentTest_AppControl target_link_libraries(ComponentTest_AppControl ${CMAKE_DL_LIBS} GTest::GTest) -# Add the Data Dispatch Service unittest +# Add the AppControl component test add_test(NAME ComponentTest_AppControl COMMAND ComponentTest_AppControl WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) +# Execute the installation helper utility to create an installation manifest for the core files. +add_custom_target(ComponentTest_AppControl_install_manifest + COMMAND "$" DIRECT_INSTALL ComponentTest_Repository --instance2009 "$" "$" "-I$" --overwrite --user_config+TestObject_ComplexHelloService+TestObject_HelloUtility --interface_config+Example_Object+Example_Object_2+TestObject_HelloDevice+TestObject_BasicHelloService+TestObject_SystemHelloService + DEPENDS ComponentTest_Repository_test_module ComponentTest_Repository_ps + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} +) + # Execute the test # Currently disabled due to issues with MINGW causing the application to sporadically crash. A bug ticket has been filed: # https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/608132 @@ -39,3 +46,4 @@ add_custom_command(TARGET ComponentTest_AppControl POST_BUILD # Build dependencies add_dependencies(ComponentTest_AppControl dependency_sdv_components) add_dependencies(ComponentTest_AppControl ComponentTest_Repository) # Use the repository components for this test +add_dependencies(ComponentTest_AppControl ComponentTest_AppControl_install_manifest) # Use the repository components for this test diff --git a/tests/component_tests/app_control/app_control_test_event_handler.cpp b/tests/component_tests/app_control/app_control_test_event_handler.cpp index ee571e8..3f5c61e 100644 --- a/tests/component_tests/app_control/app_control_test_event_handler.cpp +++ b/tests/component_tests/app_control/app_control_test_event_handler.cpp @@ -254,7 +254,9 @@ TEST(CoreLibrary_AppControl_EventHandler, Startup_Standalone_NoConfig) } } -TEST(CoreLibrary_AppControl_EventHandler, Startup_External_NoConfig) +// Currently disabled due to absence of server accepting connections. +// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/800643 +TEST(CoreLibrary_AppControl_EventHandler, DISABLED_Startup_External_NoConfig) { CAppEventHandler handler; sdv::app::IAppControl* pControl = sdv::core::GetCore(); @@ -331,13 +333,13 @@ TEST(CoreLibrary_AppControl_EventHandler, Startup_Isolated_NoConfig) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup(R"code( + bool bResult = pControl->Startup(R"toml( [Application] Mode = "Isolated" [Console] Report = "Silent" -)code", &handler); +)toml", &handler); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::isolated); @@ -589,11 +591,11 @@ TEST(CoreLibrary_AppControl_EventHandler, Startup_Default_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nInstance=2005", &handler); + bool bResult = pControl->Startup("[Application]\nInstance=2009", &handler); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); sdv::app::EAppOperationState rgeStartupStates[] = { sdv::app::EAppOperationState::initializing, @@ -652,11 +654,11 @@ TEST(CoreLibrary_AppControl_EventHandler, Startup_Standalone_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nMode=\"Standalone\"\nInstance=2005", &handler); + bool bResult = pControl->Startup("[Application]\nMode=\"Standalone\"\nInstance=2009", &handler); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); sdv::app::EAppOperationState rgeStartupStates[] = { sdv::app::EAppOperationState::initializing, @@ -701,7 +703,9 @@ TEST(CoreLibrary_AppControl_EventHandler, Startup_Standalone_DefineInstance) } } -TEST(CoreLibrary_AppControl_EventHandler, Startup_External_DefineInstance) +// Currently disabled due to absence of server accepting connections. +// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/800643 +TEST(CoreLibrary_AppControl_EventHandler, DISABLED_Startup_External_DefineInstance) { CAppEventHandler handler; sdv::app::IAppControl* pControl = sdv::core::GetCore(); @@ -715,11 +719,11 @@ TEST(CoreLibrary_AppControl_EventHandler, Startup_External_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nMode=\"External\"\nInstance=2005", &handler); + bool bResult = pControl->Startup("[Application]\nMode=\"External\"\nInstance=2009", &handler); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::external); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); sdv::app::EAppOperationState rgeStartupStates[] = { sdv::app::EAppOperationState::initializing, @@ -778,16 +782,16 @@ TEST(CoreLibrary_AppControl_EventHandler, Startup_Isolated_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - std::string ssConfig = R"code([Application] + std::string ssConfig = R"toml([Application] Mode = "Isolated" -Instance = 2005 -Connection = ")code"; +Instance = 2009 +Connection = ")toml"; ssConfig += Base64EncodePlainText("test") + "\""; bool bResult = pControl->Startup(ssConfig, &handler); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::isolated); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); sdv::app::EAppOperationState rgeStartupStates[] = { sdv::app::EAppOperationState::initializing, @@ -846,11 +850,11 @@ TEST(CoreLibrary_AppControl_EventHandler, Startup_Main_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nMode=\"Main\"\nInstance=2005", &handler); + bool bResult = pControl->Startup("[Application]\nMode=\"Main\"\nInstance=2009", &handler); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::main); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); sdv::app::EAppOperationState rgeStartupStates[] = { sdv::app::EAppOperationState::initializing, @@ -909,11 +913,11 @@ TEST(CoreLibrary_AppControl_EventHandler, Startup_Essential_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nMode=\"Essential\"\nInstance=2005", &handler); + bool bResult = pControl->Startup("[Application]\nMode=\"Essential\"\nInstance=2009", &handler); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::essential); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); sdv::app::EAppOperationState rgeStartupStates[] = { sdv::app::EAppOperationState::initializing, @@ -972,11 +976,11 @@ TEST(CoreLibrary_AppControl_EventHandler, Startup_Maintenance_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nMode=\"Maintenance\"\nInstance=2005", &handler); + bool bResult = pControl->Startup("[Application]\nMode=\"Maintenance\"\nInstance=2009", &handler); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::maintenance); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); sdv::app::EAppOperationState rgeStartupStates[] = { sdv::app::EAppOperationState::initializing, @@ -1042,8 +1046,10 @@ TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Default) EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); @@ -1080,8 +1086,10 @@ TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Standalone) EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); @@ -1097,7 +1105,9 @@ TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Standalone) EXPECT_EQ(pContext->GetInstanceID(), 0u); } -TEST(CoreLibrary_AppControl_EventHandler, RunLoop_External) +// Currently disabled due to absence of server accepting connections. +// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/800643 +TEST(CoreLibrary_AppControl_EventHandler, DISABLED_RunLoop_External) { CAppEventHandler handler; sdv::app::IAppControl* pControl = sdv::core::GetCore(); @@ -1118,8 +1128,10 @@ TEST(CoreLibrary_AppControl_EventHandler, RunLoop_External) EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); @@ -1149,10 +1161,10 @@ TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Isolated) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - std::string ssConfig = R"code([Application] + std::string ssConfig = R"toml([Application] Mode = "Isolated" Instance = 2007 -Connection = ")code"; +Connection = ")toml"; ssConfig += Base64EncodePlainText("test") + "\""; bool bResult = pControl->Startup(ssConfig, &handler); EXPECT_TRUE(bResult); @@ -1161,8 +1173,10 @@ Connection = ")code"; EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); ASSERT_NE(pShutdownRequest, nullptr); @@ -1201,8 +1215,10 @@ TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Main) EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); ASSERT_NE(pShutdownRequest, nullptr); @@ -1241,8 +1257,10 @@ TEST(CoreLibrary_AppControl_EventHandler, RunLoop_Essential) EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); diff --git a/tests/component_tests/app_control/app_control_test_mgnt_class.cpp b/tests/component_tests/app_control/app_control_test_mgnt_class.cpp index 32a9110..56895c5 100644 --- a/tests/component_tests/app_control/app_control_test_mgnt_class.cpp +++ b/tests/component_tests/app_control/app_control_test_mgnt_class.cpp @@ -77,8 +77,8 @@ TEST(AppControl, Startup_Standalone_NoConfig) EXPECT_EQ(control.GetInstanceID(), 0u); } -// Testing the external application requires a process running that was initialized as main with the same instance ID. This process -// needs to be started before and shutdown after the execution of this test. +// Currently disabled due to absence of server accepting connections. +// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/800643 TEST(AppControl, DISABLED_Startup_External_NoConfig) { sdv::app::CAppControl control; @@ -105,13 +105,13 @@ TEST(AppControl, Startup_Isolated_NoConfig) EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); EXPECT_EQ(control.GetInstanceID(), 0u); - bool bResult = control.Startup(R"code( + bool bResult = control.Startup(R"toml( [Application] Mode = "Isolated" [Console] Report = "Silent" -)code"); +)toml"); EXPECT_TRUE(bResult); EXPECT_TRUE(control.IsRunning()); EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::isolated); @@ -187,11 +187,11 @@ TEST(AppControl, Startup_Default_DefineInstance) EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); EXPECT_EQ(control.GetInstanceID(), 0u); - bool bResult = control.Startup("[Application]\nInstance=2005"); + bool bResult = control.Startup("[Application]\nInstance=2009"); EXPECT_TRUE(bResult); EXPECT_TRUE(control.IsRunning()); EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::standalone); - EXPECT_EQ(control.GetInstanceID(), 2005u); + EXPECT_EQ(control.GetInstanceID(), 2009u); control.Shutdown(); EXPECT_FALSE(control.IsRunning()); @@ -206,11 +206,11 @@ TEST(AppControl, Startup_Standalone_DefineInstance) EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); EXPECT_EQ(control.GetInstanceID(), 0u); - bool bResult = control.Startup("[Application]\nMode=\"Standalone\"\nInstance=2005"); + bool bResult = control.Startup("[Application]\nMode=\"Standalone\"\nInstance=2009"); EXPECT_TRUE(bResult); EXPECT_TRUE(control.IsRunning()); EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::standalone); - EXPECT_EQ(control.GetInstanceID(), 2005u); + EXPECT_EQ(control.GetInstanceID(), 2009u); control.Shutdown(); EXPECT_FALSE(control.IsRunning()); @@ -218,8 +218,8 @@ TEST(AppControl, Startup_Standalone_DefineInstance) EXPECT_EQ(control.GetInstanceID(), 0u); } -// Testing the external application requires a process running that was initialized as main with the same instance ID. This process -// needs to be started before and shutdown after the execution of this test. +// Currently disabled due to absence of server accepting connections. +// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/800643 TEST(AppControl, DISABLED_Startup_External_DefineInstance) { sdv::app::CAppControl control; @@ -227,11 +227,11 @@ TEST(AppControl, DISABLED_Startup_External_DefineInstance) EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); EXPECT_EQ(control.GetInstanceID(), 0u); - bool bResult = control.Startup("[Application]\nMode=\"External\"\nInstance=2005"); + bool bResult = control.Startup("[Application]\nMode=\"External\"\nInstance=2009"); EXPECT_TRUE(bResult); EXPECT_TRUE(control.IsRunning()); EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::external); - EXPECT_EQ(control.GetInstanceID(), 2005u); + EXPECT_EQ(control.GetInstanceID(), 2009u); control.Shutdown(); EXPECT_FALSE(control.IsRunning()); @@ -246,16 +246,16 @@ TEST(AppControl, Startup_Isolated_DefineInstance) EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); EXPECT_EQ(control.GetInstanceID(), 0u); - std::string ssConfig = R"code([Application] + std::string ssConfig = R"toml([Application] Mode = "Isolated" -Instance = 2005 -Connection = ")code"; +Instance = 2009 +Connection = ")toml"; ssConfig += Base64EncodePlainText("test") + "\""; bool bResult = control.Startup(ssConfig); EXPECT_TRUE(bResult); EXPECT_TRUE(control.IsRunning()); EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::isolated); - EXPECT_EQ(control.GetInstanceID(), 2005u); + EXPECT_EQ(control.GetInstanceID(), 2009u); control.Shutdown(); EXPECT_FALSE(control.IsRunning()); @@ -270,11 +270,11 @@ TEST(AppControl, Startup_Main_DefineInstance) EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); EXPECT_EQ(control.GetInstanceID(), 0u); - bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2005"); + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2009"); EXPECT_TRUE(bResult); EXPECT_TRUE(control.IsRunning()); EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::main); - EXPECT_EQ(control.GetInstanceID(), 2005u); + EXPECT_EQ(control.GetInstanceID(), 2009u); control.Shutdown(); EXPECT_FALSE(control.IsRunning()); @@ -289,11 +289,11 @@ TEST(AppControl, Startup_Essential_DefineInstance) EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); EXPECT_EQ(control.GetInstanceID(), 0u); - bool bResult = control.Startup("[Application]\nMode=\"Essential\"\nInstance=2005"); + bool bResult = control.Startup("[Application]\nMode=\"Essential\"\nInstance=2009"); EXPECT_TRUE(bResult); EXPECT_TRUE(control.IsRunning()); EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::essential); - EXPECT_EQ(control.GetInstanceID(), 2005u); + EXPECT_EQ(control.GetInstanceID(), 2009u); control.Shutdown(); EXPECT_FALSE(control.IsRunning()); @@ -308,11 +308,11 @@ TEST(AppControl, Startup_Maintenance_DefineInstance) EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); EXPECT_EQ(control.GetInstanceID(), 0u); - bool bResult = control.Startup("[Application]\nMode=\"Maintenance\"\nInstance=2005"); + bool bResult = control.Startup("[Application]\nMode=\"Maintenance\"\nInstance=2009"); EXPECT_TRUE(bResult); EXPECT_TRUE(control.IsRunning()); EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::maintenance); - EXPECT_EQ(control.GetInstanceID(), 2005u); + EXPECT_EQ(control.GetInstanceID(), 2009u); control.Shutdown(); EXPECT_FALSE(control.IsRunning()); @@ -334,8 +334,10 @@ TEST(AppControl, RunLoop_Default) EXPECT_EQ(control.GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (!control.IsRunning()) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); @@ -363,8 +365,10 @@ TEST(AppControl, RunLoop_Standalone) EXPECT_EQ(control.GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (!control.IsRunning()) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); @@ -378,8 +382,8 @@ TEST(AppControl, RunLoop_Standalone) EXPECT_EQ(control.GetInstanceID(), 0u); } -// Testing the external application requires a process running that was initialized as main with the same instance ID. This process -// needs to be started before and shutdown after the execution of this test. +// Currently disabled due to absence of server accepting connections. +// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/800643 TEST(AppControl, DISABLED_RunLoop_External) { sdv::app::CAppControl control; @@ -394,8 +398,10 @@ TEST(AppControl, DISABLED_RunLoop_External) EXPECT_EQ(control.GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (!control.IsRunning()) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); @@ -416,10 +422,10 @@ TEST(AppControl, RunLoop_Isolated) EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); EXPECT_EQ(control.GetInstanceID(), 0u); - std::string ssConfig = R"code([Application] + std::string ssConfig = R"toml([Application] Mode = "Isolated" Instance = 2007 -Connection = ")code"; +Connection = ")toml"; ssConfig += Base64EncodePlainText("test") + "\""; bool bResult = control.Startup(ssConfig); EXPECT_TRUE(bResult); @@ -428,8 +434,10 @@ Connection = ")code"; EXPECT_EQ(control.GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (!control.IsRunning()) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); ASSERT_NE(pShutdownRequest, nullptr); @@ -459,8 +467,10 @@ TEST(AppControl, RunLoop_Main) EXPECT_EQ(control.GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (!control.IsRunning()) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); ASSERT_NE(pShutdownRequest, nullptr); @@ -490,8 +500,10 @@ TEST(AppControl, RunLoop_Essential) EXPECT_EQ(control.GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (!control.IsRunning()) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); @@ -526,4 +538,3 @@ TEST(AppControl, RunLoop_Maintenance) EXPECT_EQ(control.GetAppContext(), sdv::app::EAppContext::no_context); EXPECT_EQ(control.GetInstanceID(), 0u); } - diff --git a/tests/component_tests/app_control/app_control_test_no_event_handler.cpp b/tests/component_tests/app_control/app_control_test_no_event_handler.cpp index 01a87a4..677ce6d 100644 --- a/tests/component_tests/app_control/app_control_test_no_event_handler.cpp +++ b/tests/component_tests/app_control/app_control_test_no_event_handler.cpp @@ -51,7 +51,7 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Default_NoConfig) sdv::app::IAppControl* pControl = sdv::core::GetCore(); ASSERT_NE(pControl, nullptr); sdv::app::IAppContext* pContext = sdv::core::GetCore(); - ASSERT_NE(pContext, nullptr); + ASSERT_NE(pContext, nullptr); sdv::app::IAppOperation* pOperation = sdv::core::GetCore(); ASSERT_NE(pOperation, nullptr); @@ -96,7 +96,9 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Standalone_NoConfig) EXPECT_EQ(pContext->GetInstanceID(), 0u); } -TEST(CoreLibrary_AppControl_NoEventHandler, Startup_External_NoConfig) +// Currently disabled due to absence of server accepting connections. +// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/800643 +TEST(CoreLibrary_AppControl_NoEventHandler, DISABLED_Startup_External_NoConfig) { sdv::app::IAppControl* pControl = sdv::core::GetCore(); ASSERT_NE(pControl, nullptr); @@ -134,13 +136,13 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Isolated_NoConfig) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup(R"code( + bool bResult = pControl->Startup(R"toml( [Application] Mode = "Isolated" [Console] Report = "Silent" -)code", nullptr); +)toml", nullptr); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::isolated); @@ -240,11 +242,11 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Default_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nInstance=2005", nullptr); + bool bResult = pControl->Startup("[Application]\nInstance=2009", nullptr); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); pControl->Shutdown(true); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); @@ -265,11 +267,11 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Standalone_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nMode=\"Standalone\"\nInstance=2005", nullptr); + bool bResult = pControl->Startup("[Application]\nMode=\"Standalone\"\nInstance=2009", nullptr); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::standalone); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); pControl->Shutdown(true); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); @@ -277,7 +279,9 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Standalone_DefineInstance) EXPECT_EQ(pContext->GetInstanceID(), 0u); } -TEST(CoreLibrary_AppControl_NoEventHandler, Startup_External_DefineInstance) +// Currently disabled due to absence of server accepting connections. +// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/800643 +TEST(CoreLibrary_AppControl_NoEventHandler, DISABLED_Startup_External_DefineInstance) { sdv::app::IAppControl* pControl = sdv::core::GetCore(); ASSERT_NE(pControl, nullptr); @@ -290,11 +294,11 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_External_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nMode=\"External\"\nInstance=2005", nullptr); + bool bResult = pControl->Startup("[Application]\nMode=\"External\"\nInstance=2009", nullptr); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::external); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); pControl->Shutdown(true); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); @@ -315,16 +319,16 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Isolated_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - std::string ssConfig = R"code([Application] + std::string ssConfig = R"toml([Application] Mode = "Isolated" -Instance = 2005 -Connection = ")code"; +Instance = 2009 +Connection = ")toml"; ssConfig += Base64EncodePlainText("test") + "\""; bool bResult = pControl->Startup(ssConfig, nullptr); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::isolated); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); pControl->Shutdown(true); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); @@ -345,11 +349,11 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Main_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nMode=\"Main\"\nInstance=2005", nullptr); + bool bResult = pControl->Startup("[Application]\nMode=\"Main\"\nInstance=2009", nullptr); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::main); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); pControl->Shutdown(true); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); @@ -370,11 +374,11 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Essential_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nMode=\"Essential\"\nInstance=2005", nullptr); + bool bResult = pControl->Startup("[Application]\nMode=\"Essential\"\nInstance=2009", nullptr); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::essential); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); pControl->Shutdown(true); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); @@ -395,11 +399,11 @@ TEST(CoreLibrary_AppControl_NoEventHandler, Startup_Maintenance_DefineInstance) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - bool bResult = pControl->Startup("[Application]\nMode=\"Maintenance\"\nInstance=2005", nullptr); + bool bResult = pControl->Startup("[Application]\nMode=\"Maintenance\"\nInstance=2009", nullptr); EXPECT_TRUE(bResult); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::running); EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::maintenance); - EXPECT_EQ(pContext->GetInstanceID(), 2005u); + EXPECT_EQ(pContext->GetInstanceID(), 2009u); pControl->Shutdown(true); EXPECT_EQ(pOperation->GetOperationState(), sdv::app::EAppOperationState::not_started); @@ -427,8 +431,10 @@ TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Default) EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); @@ -462,8 +468,10 @@ TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Standalone) EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); @@ -477,7 +485,9 @@ TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Standalone) EXPECT_EQ(pContext->GetInstanceID(), 0u); } -TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_External) +// Currently disabled due to absence of server accepting connections. +// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/800643 +TEST(CoreLibrary_AppControl_NoEventHandler, DISABLED_RunLoop_External) { sdv::app::IAppControl* pControl = sdv::core::GetCore(); ASSERT_NE(pControl, nullptr); @@ -497,8 +507,10 @@ TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_External) EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); @@ -525,10 +537,10 @@ TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Isolated) EXPECT_EQ(pContext->GetContextType(), sdv::app::EAppContext::no_context); EXPECT_EQ(pContext->GetInstanceID(), 0u); - std::string ssConfig = R"code([Application] + std::string ssConfig = R"toml([Application] Mode = "Isolated" Instance = 2007 -Connection = ")code"; +Connection = ")toml"; ssConfig += Base64EncodePlainText("test") + "\""; bool bResult = pControl->Startup(ssConfig, nullptr); EXPECT_TRUE(bResult); @@ -537,8 +549,10 @@ Connection = ")code"; EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); ASSERT_NE(pShutdownRequest, nullptr); @@ -574,8 +588,10 @@ TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Main) EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); sdv::app::IAppShutdownRequest* pShutdownRequest = sdv::core::GetObject("AppControlService"); ASSERT_NE(pShutdownRequest, nullptr); @@ -611,8 +627,10 @@ TEST(CoreLibrary_AppControl_NoEventHandler, RunLoop_Essential) EXPECT_EQ(pContext->GetInstanceID(), 2007u); auto tpStart = std::chrono::high_resolution_clock::now(); - std::thread threadShutdownRequest([]() + sdv::core::secure_thread threadShutdownRequest([&]() { + while (pOperation->GetOperationState() != sdv::app::EAppOperationState::running) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(250)); RequestShutdown(2007u); }); diff --git a/tests/component_tests/config_install/module_install_test.cpp b/tests/component_tests/config_install/module_install_test.cpp index 1f0becc..9b94e0f 100644 --- a/tests/component_tests/config_install/module_install_test.cpp +++ b/tests/component_tests/config_install/module_install_test.cpp @@ -69,35 +69,26 @@ public: std::filesystem::path GetDefaultInstallDir(); }; -inline std::filesystem::path CModuleControl_Install::GetDefaultInstallDir() +std::filesystem::path CModuleControl_Install::GetDefaultInstallDir() { -// // The default directory for Windows is located at $ProgramData/ and for Posix at the executable. - std::filesystem::path pathInstallDir; -//#ifdef _WIN32 -// wchar_t* szPath = nullptr; -// HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, 0, &szPath); -// if (SUCCEEDED(hr) && szPath) -// pathInstallDir = szPath; -// if (szPath) CoTaskMemFree(szPath); -//#elif defined __unix__ - pathInstallDir = GetExecDirectory(); -//#else -//#error OS is not supported! -//#endif // Append the installation directory with //. - pathInstallDir /= "2006"; - - return pathInstallDir; + return (GetExecDirectory() / "../../bin/2006").lexically_normal(); } TEST_F(CModuleControl_Install, MainApp_DefaultInstallDir) { sdv::app::CAppControl control; - bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2006"); + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 2006 + +[Console] +RedirectMon = true +)toml"); EXPECT_TRUE(bResult); // The default installation directory should be created. - EXPECT_TRUE(std::filesystem::exists(GetDefaultInstallDir())); + EXPECT_TRUE(std::filesystem::exists(control.GetComponentInstallDirectory())); control.Shutdown(); } @@ -106,10 +97,10 @@ TEST_F(CModuleControl_Install, IsolatedApp_DefaultInstallDir) { sdv::app::CAppControl control; std::string ssConnectionString = Base64EncodePlainText(""); - std::string ssConfig = R"code([Application] + std::string ssConfig = R"toml([Application] Mode = "Isolated" Instance = 2006 -Connection = ")code"; +Connection = ")toml"; ssConfig += Base64EncodePlainText("test") + "\""; bool bResult = control.Startup(ssConfig); EXPECT_TRUE(bResult); @@ -146,7 +137,13 @@ TEST_F(CModuleControl_Install, MainApp_BlockDefaultInstallDir) fstream.close(); sdv::app::CAppControl control; - bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2006"); + bool bResult = control.Startup(R"toml([Application] +Mode = "Main" +Instance = 2006 + +[Console] +RedirectMon = true +)toml"); EXPECT_FALSE(bResult); // The default installation directory should be created. @@ -168,7 +165,9 @@ TEST_F(CModuleControl_Install, IsolatedApp_BlockDefaultInstallDir) fstream.close(); sdv::app::CAppControl control; - bool bResult = control.Startup("[Application]\nMode=\"Isolated\"\nInstance=2006"); + bool bResult = control.Startup(R"toml([Application] +Mode = "Isolated" +Instance = 2006)toml"); EXPECT_FALSE(bResult); // The default installation directory should be created. @@ -197,12 +196,12 @@ TEST_F(CModuleControl_Install, OtherApp_BlockDefaultInstallDir) TEST_F(CModuleControl_Install, MainApp_CustomInstallDirRelative) { sdv::app::CAppControl control; - bool bResult = control.Startup(R"code( + bool bResult = control.Startup(R"toml( [Application] Mode="Main" Instance=2006 InstallDir = "module_test1" -)code"); +)toml"); EXPECT_TRUE(bResult); // The default installation directory should be created. @@ -214,12 +213,12 @@ InstallDir = "module_test1" TEST_F(CModuleControl_Install, IsolatedApp_CustomInstallDirRelative) { sdv::app::CAppControl control; - bool bResult = control.Startup(std::string(R"code( + bool bResult = control.Startup(std::string(R"toml( [Application] Mode="Isolated" Instance=2006 InstallDir = "module_test1" -Connection = ")code") + Base64EncodePlainText("test") + "\""); +Connection = ")toml") + Base64EncodePlainText("test") + "\""); EXPECT_TRUE(bResult); // The default installation directory should be created. @@ -231,9 +230,9 @@ Connection = ")code") + Base64EncodePlainText("test") + "\""); TEST_F(CModuleControl_Install, OtherApp_CustomInstallDirRelative) { sdv::app::CAppControl control; - bool bResult = control.Startup(R"code([Application] + bool bResult = control.Startup(R"toml([Application] Install.Dir = "module_test1" -)code"); +)toml"); EXPECT_TRUE(bResult); // The default installation directory should be created. @@ -245,11 +244,11 @@ Install.Dir = "module_test1" TEST_F(CModuleControl_Install, MainApp_CustomInstallDirAbsolute) { sdv::app::CAppControl control; - bool bResult = control.Startup(std::string(R"code( + bool bResult = control.Startup(std::string(R"toml( [Application] Mode="Main" Instance=2006 -InstallDir = ")code") + (GetExecDirectory() / "module_test2").generic_u8string() + "\""); +InstallDir = ")toml") + (GetExecDirectory() / "module_test2").generic_u8string() + "\""); EXPECT_TRUE(bResult); // The default installation directory should be created. @@ -261,12 +260,12 @@ InstallDir = ")code") + (GetExecDirectory() / "module_test2").generic_u8string() TEST_F(CModuleControl_Install, IsolatedApp_CustomInstallDirAbsolute) { sdv::app::CAppControl control; - bool bResult = control.Startup(std::string(R"code( + bool bResult = control.Startup(std::string(R"toml( [Application] Mode="Isolated" Instance=2006 -InstallDir = ")code") + (GetExecDirectory() / "module_test2").generic_u8string() + "\"" + R"code( -Connection = ")code" + Base64EncodePlainText("test") + "\""); +InstallDir = ")toml") + (GetExecDirectory() / "module_test2").generic_u8string() + "\"" + R"toml( +Connection = ")toml" + Base64EncodePlainText("test") + "\""); EXPECT_TRUE(bResult); // The default installation directory should be created. @@ -278,8 +277,8 @@ Connection = ")code" + Base64EncodePlainText("test") + "\""); TEST_F(CModuleControl_Install, OtherApp_CustomInstallDirAbsolute) { sdv::app::CAppControl control; - bool bResult = control.Startup(std::string(R"code([Application] -InstallDir = ")code") + (GetExecDirectory() / "module_test2").generic_u8string() + "\""); + bool bResult = control.Startup(std::string(R"toml([Application] +InstallDir = ")toml") + (GetExecDirectory() / "module_test2").generic_u8string() + "\""); EXPECT_TRUE(bResult); // The default installation directory should be created. diff --git a/tests/component_tests/data_dispatch_service/data_dispatch_service_test.cpp b/tests/component_tests/data_dispatch_service/data_dispatch_service_test.cpp index 4233c03..24fe300 100644 --- a/tests/component_tests/data_dispatch_service/data_dispatch_service_test.cpp +++ b/tests/component_tests/data_dispatch_service/data_dispatch_service_test.cpp @@ -997,12 +997,12 @@ TEST(DataDispatchServiceTest, DirectRxTxSignalConcurrency) // Start the threads std::unique_lock lockStart(mtxStart); uint32_t uiCnt = 0; - std::thread rgPublishThreads[16]; - for (std::thread& rThread : rgPublishThreads) - rThread = std::thread(fnPublisher, uiCnt++); - std::thread rgConsumeThreads[16]; - for (std::thread& rThread : rgConsumeThreads) - rThread = std::thread(fnConsumer, uiCnt++); + sdv::core::secure_thread rgPublishThreads[16]; + for (sdv::core::secure_thread& rThread : rgPublishThreads) + rThread = sdv::core::secure_thread(fnPublisher, uiCnt++); + sdv::core::secure_thread rgConsumeThreads[16]; + for (sdv::core::secure_thread& rThread : rgConsumeThreads) + rThread = sdv::core::secure_thread(fnConsumer, uiCnt++); // Trigger the "GO" while (uiInitCnt < 32) std::this_thread::sleep_for(std::chrono::milliseconds(10)); @@ -1057,9 +1057,9 @@ TEST(DataDispatchServiceTest, DirectRxTxSignalConcurrency) // Wait for all threads to finalize appcontrol.SetConfigMode(); bShutdown = true; - for (std::thread& rThread : rgPublishThreads) + for (sdv::core::secure_thread& rThread : rgPublishThreads) if (rThread.joinable()) rThread.join(); - for (std::thread& rThread : rgConsumeThreads) + for (sdv::core::secure_thread& rThread : rgConsumeThreads) if (rThread.joinable()) rThread.join(); signalTx1a.Reset(); diff --git a/tests/component_tests/data_dispatch_service/transaction_test.cpp b/tests/component_tests/data_dispatch_service/transaction_test.cpp index 6f58d63..92dada6 100644 --- a/tests/component_tests/data_dispatch_service/transaction_test.cpp +++ b/tests/component_tests/data_dispatch_service/transaction_test.cpp @@ -1546,12 +1546,12 @@ TEST(DataDispatchServiceTest, TransactionalRxTxSignalConcurrency) // Start the threads std::unique_lock lockStart(mtxStart); uint32_t uiCnt = 0; - std::thread rgPublishThreads[16]; - for (std::thread& rThread : rgPublishThreads) - rThread = std::thread(fnPublisher, uiCnt++); - std::thread rgConsumeThreads[nConsumeThreads]; - for (std::thread& rThread :rgConsumeThreads) - rThread = std::thread(fnConsumer, uiCnt++); + sdv::core::secure_thread rgPublishThreads[16]; + for (sdv::core::secure_thread& rThread : rgPublishThreads) + rThread = sdv::core::secure_thread(fnPublisher, uiCnt++); + sdv::core::secure_thread rgConsumeThreads[nConsumeThreads]; + for (sdv::core::secure_thread& rThread :rgConsumeThreads) + rThread = sdv::core::secure_thread(fnConsumer, uiCnt++); // Trigger the "GO" while (uiInitCnt < 32) std::this_thread::sleep_for(std::chrono::milliseconds(10)); @@ -1630,11 +1630,11 @@ TEST(DataDispatchServiceTest, TransactionalRxTxSignalConcurrency) // Wait for all threads to finalize appcontrol.SetConfigMode(); bShutdownPublisher = true; - for (std::thread& rThread : rgPublishThreads) + for (sdv::core::secure_thread& rThread : rgPublishThreads) if (rThread.joinable()) rThread.join(); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); bShutdownConsumer = true; - for (std::thread& rThread : rgConsumeThreads) + for (sdv::core::secure_thread& rThread : rgConsumeThreads) if (rThread.joinable()) rThread.join(); // Check consume ranges diff --git a/tests/component_tests/dbc_util/dbc_util_test.cpp b/tests/component_tests/dbc_util/dbc_util_test.cpp index 8b55ef4..70f019b 100644 --- a/tests/component_tests/dbc_util/dbc_util_test.cpp +++ b/tests/component_tests/dbc_util/dbc_util_test.cpp @@ -107,7 +107,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndian) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -217,7 +217,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmit64BitBigEndian) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -361,7 +361,7 @@ TEST(DbcUtilCanDLTest, ReceiveBigEndian) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -479,7 +479,7 @@ TEST(DbcUtilCanDLTest, Receive64BitBigEndian) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -539,7 +539,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndian) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -649,7 +649,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmit64BitLittleEndian) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -793,7 +793,7 @@ TEST(DbcUtilCanDLTest, ReceiveLittleEndian) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -911,7 +911,7 @@ TEST(DbcUtilCanDLTest, Receive64BitLittleEndian) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -971,7 +971,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransactionalTransmitBigEndian) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -1064,7 +1064,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransactionalTransmitLittleEndian) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -1157,7 +1157,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndianAllDataTypes) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -1443,7 +1443,7 @@ TEST(DbcUtilCanDLTest, ReceiveBigEndianAllDataTypes) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -1629,7 +1629,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndianAllDataTypes) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -1915,7 +1915,7 @@ TEST(DbcUtilCanDLTest, ReceiveLittleEndianAllDataTypes) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -2101,7 +2101,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitBigEndianScaledDataTypes) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -2271,7 +2271,7 @@ TEST(DbcUtilCanDLTest, ReceiveBigEndianScaledDataType) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -2378,7 +2378,7 @@ TEST(DbcUtilCanDLTest, SpontaneousTransmitLittleEndianScaledDataTypes) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -2548,7 +2548,7 @@ TEST(DbcUtilCanDLTest, ReceiveLittleEndianScaledDataType) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -2655,7 +2655,7 @@ TEST(DbcUtilCanDLTest, CyclicTransmit) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -2804,7 +2804,7 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -2971,7 +2971,7 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousTransmit) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -3112,7 +3112,7 @@ TEST(DbcUtilCanDLTest, SpontaneousDelayTransmit) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -3250,7 +3250,7 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousDelayTransmit) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); @@ -3394,7 +3394,7 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit) // Start the data link CDbcStructDataLink dl; EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialization_pending); - dl.Initialize(""); + dl.Initialize(sdv::SObjectInfo()); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::initialized); dl.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(dl.GetObjectState(), sdv::EObjectState::running); diff --git a/tests/component_tests/param_utils/CMakeLists.txt b/tests/component_tests/param_utils/CMakeLists.txt new file mode 100644 index 0000000..be64482 --- /dev/null +++ b/tests/component_tests/param_utils/CMakeLists.txt @@ -0,0 +1,36 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 ZF Friedrichshafen AG +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Erik Verhoeven - initial API and implementation +#******************************************************************************* + +# Define project +project (ComponentTest_ParamUtils VERSION 1.0 LANGUAGES CXX) + +# Data maneger executable +add_executable(ComponentTest_ParamUtils + "main.cpp" + "get_parameter.cpp" + "resolve_text.cpp" + ) + +target_link_libraries(ComponentTest_ParamUtils ${CMAKE_DL_LIBS} GTest::GTest) + +# Add the ParamUtils component test +add_test(NAME ComponentTest_ParamUtils COMMAND ComponentTest_ParamUtils WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + +# Execute the test +add_custom_command(TARGET ComponentTest_ParamUtils POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ComponentTest_ParamUtils.xml + VERBATIM +) + +# Build dependencies +add_dependencies(ComponentTest_ParamUtils dependency_sdv_components) diff --git a/tests/component_tests/param_utils/get_parameter.cpp b/tests/component_tests/param_utils/get_parameter.cpp new file mode 100644 index 0000000..0aefa87 --- /dev/null +++ b/tests/component_tests/param_utils/get_parameter.cpp @@ -0,0 +1,213 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include +#include + +TEST(ParamUtils, GetParameter_FailParamInterface) +{ + sdv::app::CAppControl control; + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2010"); + ASSERT_TRUE(bResult); + + // Get the app settings service + sdv::TInterfaceAccessPtr ptrSystemSettings = sdv::core::GetObject("AppSettingsService"); + ASSERT_TRUE(ptrSystemSettings); + + // Request the IParameters interface + sdv::IParameters* pParameters = ptrSystemSettings.GetInterface(); + ASSERT_NE(pParameters, nullptr); + + // Invalid parameters interface + sdv::any_t anyFail = sdv::core::GetParameter(nullptr, "Application.InstallDir"); + EXPECT_TRUE(anyFail.empty()); + bool bExcept = false; + try + { + sdv::core::GetParameter(nullptr, "Application.InstallDir", false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_TRUE(bExcept); + + // Invalid parameters name + anyFail = sdv::core::GetParameter(pParameters, "ParamDoesNotExist"); + EXPECT_TRUE(anyFail.empty()); + bExcept = false; + try + { + sdv::core::GetParameter(pParameters, "ParamDoesNotExist", false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_TRUE(bExcept); + + control.Shutdown(); +} + +TEST(ParamUtils, GetParameter_FailObjectInterface) +{ + sdv::app::CAppControl control; + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2010"); + ASSERT_TRUE(bResult); + + // Get the app settings service + sdv::TInterfaceAccessPtr ptrSystemSettings = sdv::core::GetObject("AppSettingsService"); + ASSERT_TRUE(ptrSystemSettings); + + // Invalid object interface + sdv::any_t anyFail = sdv::core::GetParameter(sdv::TInterfaceAccessPtr(), "Application.InstallDir"); + EXPECT_TRUE(anyFail.empty()); + bool bExcept = false; + try + { + sdv::core::GetParameter(sdv::TInterfaceAccessPtr(), "Application.InstallDir", false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_TRUE(bExcept); + + // Invalid parameters name + anyFail = sdv::core::GetParameter(ptrSystemSettings, "ParamDoesNotExist"); + EXPECT_TRUE(anyFail.empty()); + bExcept = false; + try + { + sdv::core::GetParameter(ptrSystemSettings, "ParamDoesNotExist", false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_TRUE(bExcept); + + control.Shutdown(); +} + +TEST(ParamUtils, GetParameter_FailObjectName) +{ + sdv::app::CAppControl control; + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2010"); + ASSERT_TRUE(bResult); + + // Invalid object name + sdv::any_t anyFail = sdv::core::GetParameter("InvalidObject", "Application.InstallDir"); + EXPECT_TRUE(anyFail.empty()); + bool bExcept = false; + try + { + sdv::core::GetParameter("InvalidObject", "Application.InstallDir", false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_TRUE(bExcept); + + // Invalid parameters name + anyFail = sdv::core::GetParameter("AppSettingsService", "ParamDoesNotExist"); + EXPECT_TRUE(anyFail.empty()); + bExcept = false; + try + { + sdv::core::GetParameter("AppSettingsService", "ParamDoesNotExist", false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_TRUE(bExcept); + + control.Shutdown(); +} + +TEST(ParamUtils, GetParameter_ParamInterface) +{ + sdv::app::CAppControl control; + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2010"); + ASSERT_TRUE(bResult); + + // Get the app settings service + sdv::TInterfaceAccessPtr ptrSystemSettings = sdv::core::GetObject("AppSettingsService"); + ASSERT_TRUE(ptrSystemSettings); + + // Request the IParameters interface + sdv::IParameters* pParameters = ptrSystemSettings.GetInterface(); + ASSERT_NE(pParameters, nullptr); + + // Request the install directory through parameters + sdv::any_t anyInstall = sdv::core::GetParameter(pParameters, "Application.InstallDir"); + EXPECT_FALSE(anyInstall.empty()); + EXPECT_EQ(std::filesystem::relative(anyInstall.get(), control.GetAppDirectory()), "../../bin/2010"); + + control.Shutdown(); +} + +TEST(ParamUtils, GetParameter_ObjectInterface) +{ + sdv::app::CAppControl control; + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2010"); + ASSERT_TRUE(bResult); + + // Get the app settings service + sdv::TInterfaceAccessPtr ptrSystemSettings = sdv::core::GetObject("AppSettingsService"); + ASSERT_TRUE(ptrSystemSettings); + + // Request the install directory through parameters + sdv::any_t anyInstall = sdv::core::GetParameter(ptrSystemSettings, "Application.InstallDir"); + EXPECT_FALSE(anyInstall.empty()); + EXPECT_EQ(std::filesystem::relative(anyInstall.get(), control.GetAppDirectory()), "../../bin/2010"); + + control.Shutdown(); +} + +TEST(ParamUtils, GetParameter_ObjectName) +{ + sdv::app::CAppControl control; + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2010"); + ASSERT_TRUE(bResult); + + // Request the install directory through parameters + sdv::any_t anyInstall = sdv::core::GetParameter("AppSettingsService", "Application.InstallDir"); + EXPECT_FALSE(anyInstall.empty()); + EXPECT_EQ(std::filesystem::relative(anyInstall.get(), control.GetAppDirectory()), "../../bin/2010"); + + control.Shutdown(); +} + +TEST(ParamUtils, GetParameter_EnumExpand) +{ + sdv::app::CAppControl control; + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2010"); + ASSERT_TRUE(bResult); + + // Request the mode through parameters + sdv::any_t anyMode = sdv::core::GetParameter("AppSettingsService", "Application.Mode"); + EXPECT_FALSE(anyMode.empty()); + EXPECT_EQ(static_cast(anyMode), static_cast(sdv::app::EAppContext::main)); + + // Request the expanded mode through parameters + std::string ssMode = sdv::core::GetParameterExpand("AppSettingsService", "Application.Mode"); + EXPECT_FALSE(ssMode.empty()); + EXPECT_EQ(ssMode, "Main"); + + control.Shutdown(); +} + diff --git a/tests/unit_tests/toml_parser/generate_toml_miscellaneous.cpp b/tests/component_tests/param_utils/main.cpp similarity index 64% rename from tests/unit_tests/toml_parser/generate_toml_miscellaneous.cpp rename to tests/component_tests/param_utils/main.cpp index f3b56fc..3da742d 100644 --- a/tests/unit_tests/toml_parser/generate_toml_miscellaneous.cpp +++ b/tests/component_tests/param_utils/main.cpp @@ -12,10 +12,16 @@ ********************************************************************************/ #include +#include "../../../global/process_watchdog.h" -#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" -#include "../../../sdv_services/core/toml_parser/parser_toml.h" +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain(int argc, wchar_t* argv[]) +#else +extern "C" int main(int argc, char* argv[]) +#endif +{ + CProcessWatchdog watchdog; -// Test TODO: -// Shift nodes up and down within one container -// Remove formatting from node \ No newline at end of file + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/component_tests/param_utils/resolve_text.cpp b/tests/component_tests/param_utils/resolve_text.cpp new file mode 100644 index 0000000..8cd8160 --- /dev/null +++ b/tests/component_tests/param_utils/resolve_text.cpp @@ -0,0 +1,168 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include +#include + +TEST(ParamUtils, ResolveText_FailObjectName) +{ + sdv::app::CAppControl control; + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2010"); + ASSERT_TRUE(bResult); + + // Invalid parameters interface + std::string ssText = sdv::core::ResolveText("$(InvalidObject:Application.InstallDir)"); + EXPECT_TRUE(ssText.empty()); + bool bExcept = false; + try + { + sdv::core::ResolveText("$(InvalidObject:Application.InstallDir)", true, false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_TRUE(bExcept); + + // Invalid parameter name + ssText = sdv::core::ResolveText("$(AppSettingsService:ParamDoesNotExist)"); + EXPECT_TRUE(ssText.empty()); + bExcept = false; + try + { + sdv::core::ResolveText("$(AppSettingsService:ParamDoesNotExist)", true, false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_TRUE(bExcept); + + control.Shutdown(); +} + +TEST(ParamUtils, ResolveText_EscapingDollarCharacter) +{ + sdv::app::CAppControl control; + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2010"); + ASSERT_TRUE(bResult); + + // Not escaping $ - this is allowed! + std::string ssText = sdv::core::ResolveText("There is a $ in this text."); + EXPECT_FALSE(ssText.empty()); + EXPECT_EQ(ssText, "There is a $ in this text."); + bool bExcept = false; + ssText.clear(); + try + { + ssText = sdv::core::ResolveText("There is a $ in this text.", true, false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_FALSE(bExcept); + EXPECT_EQ(ssText, "There is a $ in this text."); + + // Missing (-character - this is the same as not escaping $ + ssText = sdv::core::ResolveText("$AppSettingsService:Application.InstallDir)"); + EXPECT_FALSE(ssText.empty()); + EXPECT_EQ(ssText, "$AppSettingsService:Application.InstallDir)"); + bExcept = false; + ssText.clear(); + try + { + ssText = sdv::core::ResolveText("$AppSettingsService:Application.InstallDir)", true, false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_FALSE(bExcept); + EXPECT_EQ(ssText, "$AppSettingsService:Application.InstallDir)"); + + // Missing )-character - this is an error + ssText = sdv::core::ResolveText("$(AppSettingsService:Application.InstallDir"); + EXPECT_TRUE(ssText.empty()); + bExcept = false; + try + { + sdv::core::ResolveText("$(AppSettingsService:Application.InstallDir", true, false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_TRUE(bExcept); + + // Invalid object-param pair + ssText = sdv::core::ResolveText("$(Application.InstallDir)"); + EXPECT_TRUE(ssText.empty()); + bExcept = false; + try + { + sdv::core::ResolveText("$(Application.InstallDir)", true, false); + } + catch (const std::runtime_error&) + { + bExcept = true; + } + EXPECT_TRUE(bExcept); + + // Resolvinf during first iteration + ssText = sdv::core::ResolveText("$(AppSettingsService:Application.Mode)"); + EXPECT_EQ(ssText, "Main"); + + // Resolving during second iteration + ssText = sdv::core::ResolveText("\\$(AppSettingsService:Application.Mode)"); + EXPECT_EQ(ssText, "Main"); + + // No further iteration + ssText = sdv::core::ResolveText("\\$(AppSettingsService:Application.Mode)", false); + EXPECT_EQ(ssText, "$(AppSettingsService:Application.Mode)"); + + control.Shutdown(); +} + +TEST(ParamUtils, ResolveText_Object) +{ + sdv::app::CAppControl control; + bool bResult = control.Startup("[Application]\nMode=\"Main\"\nInstance=2010"); + ASSERT_TRUE(bResult); + + // Request the install directory through parameters + std::string ssText = sdv::core::ResolveText("$(AppSettingsService:Application.InstallDir)/MyPackage"); + EXPECT_FALSE(ssText.empty()); + EXPECT_EQ(std::filesystem::relative(ssText, control.GetAppDirectory()), "../../bin/2010/MyPackage"); + + // Request the install directory through parameters + ssText = sdv::core::ResolveText("The application mode is \"$( AppSettingsService : Application.Mode )\" and the instance numbver is $(AppSettingsService:Application.Instance)."); + EXPECT_FALSE(ssText.empty()); + EXPECT_EQ(ssText, "The application mode is \"Main\" and the instance numbver is 2010."); + + ssText = sdv::core::ResolveText("The application mode is \"$(AppSettingsService:Application.Mode)\" and the instance number " + "is $(AppSettingsService:Application.Instance)."); + EXPECT_FALSE(ssText.empty()); + EXPECT_EQ(ssText, "The application mode is \"Main\" and the instance number is 2010."); + + // Request the install directory through parameters + ssText = sdv::core::ResolveText("Valid text with a \\$ in the text..."); + EXPECT_FALSE(ssText.empty()); + EXPECT_EQ(ssText, "Valid text with a $ in the text..."); + + control.Shutdown(); +} + +// TODO: Recursive test +// e.g. \$($(MyObject1:Name):$(MyObject2:ParamName)) diff --git a/tests/component_tests/repository/CMakeLists.txt b/tests/component_tests/repository/CMakeLists.txt index eff7440..199126a 100644 --- a/tests/component_tests/repository/CMakeLists.txt +++ b/tests/component_tests/repository/CMakeLists.txt @@ -57,7 +57,7 @@ set_target_properties(ComponentTest_Repository_test_module PROPERTIES SUFFIX ".s # Execute the installation helper utility to create an installation manifest for the core files. add_custom_target(ComponentTest_Repository_install_manifest - COMMAND "$" DIRECT_INSTALL ComponentTest_Repository -T. --instance2005 "$" "$" "-I$" --overwrite --user_config+TestObject_ComplexHelloService+TestObject_HelloUtility --interface_config+Example_Object+Example_Object_2+TestObject_HelloDevice+TestObject_BasicHelloService+TestObject_SystemHelloService + COMMAND "$" DIRECT_INSTALL ComponentTest_Repository --instance2005 "$" "$" "-I$" --overwrite --user_config+TestObject_ComplexHelloService+TestObject_HelloUtility --interface_config+Example_Object+Example_Object_2+TestObject_HelloDevice+TestObject_BasicHelloService+TestObject_SystemHelloService DEPENDS ComponentTest_Repository_test_module ComponentTest_Repository_ps WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) diff --git a/tests/component_tests/repository/isolation_test.cpp b/tests/component_tests/repository/isolation_test.cpp index 3cfe7eb..02e7fc2 100644 --- a/tests/component_tests/repository/isolation_test.cpp +++ b/tests/component_tests/repository/isolation_test.cpp @@ -44,14 +44,18 @@ public: */ virtual void SetUp() { - bool bRet = Startup(R"code( + bool bRet = Startup(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Main" Instance = 2005 -)code"); + +[Console] +Report = "Silent" +RedirectMon = true +)toml"); if (!bRet) std::cout << "Failed to start the application for instance 2005." << std::endl; } diff --git a/tests/component_tests/repository/repository_test.cpp b/tests/component_tests/repository/repository_test.cpp index 3781e42..410fc3c 100644 --- a/tests/component_tests/repository/repository_test.cpp +++ b/tests/component_tests/repository/repository_test.cpp @@ -42,7 +42,7 @@ public: SDV_INTERFACE_ENTRY(sdv::IObjectControl) END_SDV_INTERFACE_MAP() - virtual void Initialize([[maybe_unused]] const sdv::u8string& ssObjectConfig) override + virtual void Initialize([[maybe_unused]] const sdv::SObjectInfo& sObjectInfo) override { FAIL() << "Error: Initialize should not be called by Repo Service!"; // m_eObjectState = sdv::EObjectState::initialization_failure; @@ -83,13 +83,13 @@ public: TEST(RepositoryTest, LoadNonexistentModule) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -105,13 +105,13 @@ Mode = "Essential" TEST(RepositoryTest, CreateNonexistantClass) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -129,13 +129,13 @@ Mode = "Essential" TEST(RepositoryTest, GetNonexistantObject) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -158,13 +158,13 @@ Mode = "Essential" TEST(RepositoryTest, InstantiateAndGet) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -189,13 +189,13 @@ Mode = "Essential" TEST(RepositoryTest, InstantiateInitFail) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -218,13 +218,13 @@ Mode = "Essential" TEST(RepositoryTest, InstantiateDuplicateCreateObjectName) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -255,13 +255,13 @@ Mode = "Essential" TEST(RepositoryTest, InstantiateDuplicateExternalObjectName) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -290,13 +290,13 @@ Mode = "Essential" TEST(RepositoryTest, InstantiateDuplicateObjectNameMixed) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -327,13 +327,13 @@ Mode = "Essential" TEST(RepositoryTest, InstantiateTwoNamedInstances) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -365,13 +365,13 @@ Mode = "Essential" TEST(RepositoryTest, ChainCreateSimple) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -398,13 +398,13 @@ Mode = "Essential" TEST(RepositoryTest, ChainCreateParallel) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -417,43 +417,47 @@ Mode = "Essential" ASSERT_TRUE(pModuleControl->Load((GetExecDirectory() / "ComponentTest_Repository_test_module.sdv").generic_u8string())); - std::thread testThread([pRepositoryControl, pObjectAccess]() + sdv::core::secure_thread testThread( + [pRepositoryControl, pObjectAccess]() { for (uint32_t i = 0; i < LoopCount; ++i) { std::string count = std::to_string(i); - bool bRes = pRepositoryControl->CreateObject("TestObject_CreateChain", "Bar_" + count, - "chained_object = \"BarFoo_" + count + "\""); + bool bRes = pRepositoryControl->CreateObject( + "TestObject_CreateChain", "Bar_" + count, "chained_object = \"BarFoo_" + count + "\""); EXPECT_TRUE(bRes); EXPECT_NE(nullptr, pObjectAccess->GetObject("BarFoo_" + count)); } }); + // TEMP EVE 05.05.2026: The parallel execution of the creation threads causes a sporadic crash. + // See: https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/799698 + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + for (uint32_t i = 0; i < LoopCount; ++i) { std::string count = std::to_string(i); - bool bRes = - pRepositoryControl->CreateObject("TestObject_CreateChain", "Foo_" + count, "chained_object =\"FooBar_" + count + "\""); + bool bRes = pRepositoryControl->CreateObject( + "TestObject_CreateChain", "Foo_" + count, "chained_object =\"FooBar_" + count + "\""); EXPECT_TRUE(bRes); EXPECT_NE(nullptr, pObjectAccess->GetObject("FooBar_" + count)); } testThread.join(); - control.SetRunningMode(); control.Shutdown(); } TEST(RepositoryTest, ChainCreateParallelLock) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -474,7 +478,7 @@ Mode = "Essential" //attempt to create deadlock by taking lock during initialize and by taking lock before call to create object - std::thread testThread([&]() + sdv::core::secure_thread testThread([&]() { for (uint32_t i = 0; i < LoopCount; ++i) { @@ -507,13 +511,13 @@ Mode = "Essential" TEST(RepositoryTest, ChainCreateParallelThreadLock) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -534,7 +538,7 @@ Mode = "Essential" //attempt to create deadlock by taking lock during initialize and by taking lock before call to create object - std::thread testThread([&]() + sdv::core::secure_thread testThread([&]() { for (uint32_t i = 0; i < LoopCount; ++i) { @@ -567,13 +571,13 @@ Mode = "Essential" TEST(RepositoryTest, InstantiateDuringShutdown) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -598,13 +602,13 @@ Mode = "Essential" TEST(RepositoryTest, CreateUtility) { - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Essential" -)code"); +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -621,14 +625,18 @@ TEST(RepositoryTest, MainApplication_GetInstalledAndLoadedComponent) { // Start the app control. The application automatically loads the installation manifests and the application configuration, // starting automatically the objects. - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Main" Instance = 2005 -)code"); + +[Console] +Report = "Silent" +RedirectMon = true +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); @@ -644,7 +652,7 @@ TEST(RepositoryTest, MainApplication_GetInstalledComponent) { // Start the app control. The application automatically loads the installation manifests, but since there is no application // configuration, it doesn't load the objects. - sdv::app::CAppControl control(R"code( + sdv::app::CAppControl control(R"toml( [LogHandler] ViewFilter = "Fatal" @@ -652,7 +660,11 @@ ViewFilter = "Fatal" Mode = "Main" Instance = 2005 AppConfig = "" # override application config -)code"); + +[Console] +Report = "Silent" +RedirectMon = true +)toml"); ASSERT_TRUE(control.IsRunning()); control.SetConfigMode(); diff --git a/tests/component_tests/repository/test_component.cpp b/tests/component_tests/repository/test_component.cpp index 414c9df..78c71ea 100644 --- a/tests/component_tests/repository/test_component.cpp +++ b/tests/component_tests/repository/test_component.cpp @@ -306,7 +306,7 @@ public: } }; - std::thread testThread(fun); + sdv::core::secure_thread testThread(fun); testThread.join(); diff --git a/tests/include/test_watchdog.h b/tests/include/test_watchdog.h index af8cc32..e38537e 100644 --- a/tests/include/test_watchdog.h +++ b/tests/include/test_watchdog.h @@ -64,7 +64,7 @@ public: */ CProcessWatchdog(int64_t iWatchdogTimeS = 300ll) { - m_threadWatchdog = std::thread(&CProcessWatchdog::WatchdogThreadFunc, this, iWatchdogTimeS ); + m_threadWatchdog = sdv::core::secure_thread(&CProcessWatchdog::WatchdogThreadFunc, this, iWatchdogTimeS ); } /** @@ -110,8 +110,8 @@ private: } } - std::atomic_bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate. - std::thread m_threadWatchdog; ///< The watchdog thread. + std::atomic_bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate. + sdv::core::secure_thread m_threadWatchdog; ///< The watchdog thread. }; #endif // !defined TEST_WATCHDOG_H diff --git a/tests/manual_tests/silkit_can_com_tests/include/can_com_test_helper.h b/tests/manual_tests/silkit_can_com_tests/include/can_com_test_helper.h index e308798..02d4865 100644 --- a/tests/manual_tests/silkit_can_com_tests/include/can_com_test_helper.h +++ b/tests/manual_tests/silkit_can_com_tests/include/can_com_test_helper.h @@ -27,9 +27,9 @@ class CTestCanSilKit : public CCANSilKit { public: - virtual void Initialize(const sdv::u8string& ssObjectConfig) override + virtual void Initialize(const sdv::SObjectInfo& sObjectInfo) override { - return CCANSilKit::Initialize(ssObjectConfig); + return CCANSilKit::Initialize(sObjectInfo); } virtual sdv::EObjectState GetObjectState() const override diff --git a/tests/manual_tests/silkit_can_com_tests/src/can_com_manual_test_silkit.cpp b/tests/manual_tests/silkit_can_com_tests/src/can_com_manual_test_silkit.cpp index 779b121..032447e 100644 --- a/tests/manual_tests/silkit_can_com_tests/src/can_com_manual_test_silkit.cpp +++ b/tests/manual_tests/silkit_can_com_tests/src/can_com_manual_test_silkit.cpp @@ -31,9 +31,9 @@ class CANSilkitTest : public ::testing::Test class CTestCANSilkit : public CCANSilKit { public: - virtual void Initialize(const sdv::u8string& ssObjectConfig) override + virtual void Initialize(const sdv::SObjectInfo& sObjectInfo) override { - CCANSilKit::Initialize(ssObjectConfig); + CCANSilKit::Initialize(sObjectInfo); } virtual void Shutdown() override @@ -113,7 +113,9 @@ bool InitializeAppControl(sdv::app::CAppControl* appcontrol, const std::string& void InitializeCanComObject(CTestCANSilkit& canComObj, const std::string config, MockCANReceiver& mockRcv) { - ASSERT_NO_THROW(canComObj.Initialize(config.c_str())); + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = config; + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.SetOperationMode(sdv::EOperationMode::configuring)); ASSERT_NO_THROW(canComObj.RegisterReceiver(&mockRcv)); @@ -142,7 +144,8 @@ TEST_F(CANSilkitTest, ValidConfigString) sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string ssObjectConfig = + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"( DebugInfo = true SyncMode = true @@ -156,7 +159,7 @@ TEST_F(CANSilkitTest, ValidConfigString) }""" )"; CTestCANSilkit canComObj; - ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); ASSERT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.Send(testMsg, 0)); @@ -170,7 +173,8 @@ TEST_F(CANSilkitTest, InvalidConfigIdentifier) sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string ssObjectConfig = + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"( DebugInfo = true SyncMode = true @@ -185,7 +189,7 @@ TEST_F(CANSilkitTest, InvalidConfigIdentifier) )"; CTestCANSilkit canComObj; - ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialization_failure); ASSERT_NO_THROW(canComObj.Shutdown()); @@ -197,7 +201,8 @@ TEST_F(CANSilkitTest, SendReceiveTest) sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string ssConfig1 = + sdv::SObjectInfo sObjectInfo1{}; + sObjectInfo1.ssConfig = R"( DebugInfo = true SyncMode = true @@ -210,7 +215,8 @@ TEST_F(CANSilkitTest, SendReceiveTest) }, }""" )"; - sdv::u8string ssConfig2 = + sdv::SObjectInfo sObjectInfo2{}; + sObjectInfo2.ssConfig = R"( DebugInfo = true SyncMode = true @@ -228,9 +234,9 @@ TEST_F(CANSilkitTest, SendReceiveTest) CTestCANSilkit canComObj2; MockCANReceiver mockRcv; - canComObj1.Initialize(ssConfig1.c_str()); + canComObj1.Initialize(sObjectInfo1); ASSERT_EQ(canComObj1.GetObjectState(), sdv::EObjectState::initialized); - canComObj2.Initialize(ssConfig2.c_str()); + canComObj2.Initialize(sObjectInfo2); ASSERT_EQ(canComObj2.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj1.SetOperationMode(sdv::EOperationMode::running)); diff --git a/tests/manual_tests/silkit_can_com_tests/src/can_com_test_silkit.cpp b/tests/manual_tests/silkit_can_com_tests/src/can_com_test_silkit.cpp index 3468b1a..22d35db 100644 --- a/tests/manual_tests/silkit_can_com_tests/src/can_com_test_silkit.cpp +++ b/tests/manual_tests/silkit_can_com_tests/src/can_com_test_silkit.cpp @@ -53,7 +53,7 @@ public: virtual bool OnInitialize() override { m_StopThread = false; - //m_thSend2DatalinkThread = std::thread(&CDummyCANSockets::Send2DatalinkThread, this); + //m_thSend2DatalinkThread = sdv::core::secure_thread(&CDummyCANSockets::Send2DatalinkThread, this); return true; } @@ -116,7 +116,7 @@ public: private: std::atomic_bool m_StopThread = false; - std::thread m_thSend2DatalinkThread; + sdv::core::secure_thread m_thSend2DatalinkThread; mutable std::mutex m_mtxReceivers; std::set m_setReceivers; }; @@ -304,7 +304,7 @@ Class = "CAN_data_link" // Create an object of the CDummyCANSilKit class CDummyCANSilKit canComObj; - canComObj.Initialize(""); + canComObj.Initialize(sdv::SObjectInfo()); sdv::can::ISend* pCanSend = &canComObj; EXPECT_NE(pCanSend, nullptr); @@ -358,7 +358,7 @@ TEST_F(CANCommunicationTest, ExtractAndCompareSignals) // Create a normal object of the CDummyCANSilKit class CDummyCANSilKit canComObj; - canComObj.Initialize(""); + canComObj.Initialize(sdv::SObjectInfo()); sdv::can::ISend* pCanSend = &canComObj; EXPECT_NE(pCanSend, nullptr); diff --git a/tests/manual_tests/silkit_can_com_tests/src/can_reader.cpp b/tests/manual_tests/silkit_can_com_tests/src/can_reader.cpp index 98c84f7..23596aa 100644 --- a/tests/manual_tests/silkit_can_com_tests/src/can_reader.cpp +++ b/tests/manual_tests/silkit_can_com_tests/src/can_reader.cpp @@ -15,7 +15,8 @@ void ReceiveData() sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string config = R"( + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"( DebugInfo = true SyncMode = true SilKitParticipantName = "can_reader" @@ -29,7 +30,7 @@ void ReceiveData() )"; CTestCanSilKit canComObj; - canComObj.Initialize(config.c_str()); + canComObj.Initialize(sObjectInfo); canComObj.SetOperationMode(sdv::EOperationMode::configuring); CanReceiver mockRcv; diff --git a/tests/manual_tests/silkit_can_com_tests/src/can_writer.cpp b/tests/manual_tests/silkit_can_com_tests/src/can_writer.cpp index ca3683d..d6e772b 100644 --- a/tests/manual_tests/silkit_can_com_tests/src/can_writer.cpp +++ b/tests/manual_tests/silkit_can_com_tests/src/can_writer.cpp @@ -15,7 +15,8 @@ int main() sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string config = R"( + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"( DebugInfo = true SyncMode = true SilKitParticipantName = "can_writer" @@ -29,7 +30,7 @@ int main() )"; CTestCanSilKit canComObj; - canComObj.Initialize(config.c_str()); + canComObj.Initialize(sObjectInfo); canComObj.SetOperationMode(sdv::EOperationMode::running); uint8_t msgId = 65; diff --git a/tests/sdv_core_reloc.toml b/tests/sdv_core_reloc.toml index 63ff404..87e35fc 100644 --- a/tests/sdv_core_reloc.toml +++ b/tests/sdv_core_reloc.toml @@ -1,2 +1,16 @@ -# Location of the SDV core library -directory = "../../bin" \ No newline at end of file +# This file is used to overwrite the location of the SDV Core Library regardless of the values of Vehicle API environment variables. +# For test and examples of the SDV Vehicle API Framework, the default location is the binary directory of the target builds. This +# file redirects the location to the binary folder of the SDV Vehicle API Core Framework. +# The environment variables will be updated for the application and all child-applications when using the core loader. The +# locations the environment variables will direct to are: +# - SDV_FRAMEWORK_RUNTIME "../../bin" +# - SDV_COMPONENT_INSTALL "../../bin" +# - SDV_FRAMEWORK_DEV_TOOLS "../../bin" +# - SDV_FRAMEWORK_DEV_INCLUDE "../../../../export" + +[CoreLocation] +Version = 100 +Runtime = "../../bin" +Install = "../../bin" +DevTools = "../../bin" +Include = "../../../../export" \ No newline at end of file diff --git a/tests/unit_tests/app_connect/CMakeLists.txt b/tests/unit_tests/app_settings/CMakeLists.txt similarity index 57% rename from tests/unit_tests/app_connect/CMakeLists.txt rename to tests/unit_tests/app_settings/CMakeLists.txt index 065bea0..2ae426e 100644 --- a/tests/unit_tests/app_connect/CMakeLists.txt +++ b/tests/unit_tests/app_settings/CMakeLists.txt @@ -12,22 +12,22 @@ #******************************************************************************* # Define project -project (UnitTest_App_Connect VERSION 1.0 LANGUAGES CXX) +project (UnitTest_App_Settings VERSION 1.0 LANGUAGES CXX) # Compile the source code -add_executable(UnitTest_App_Connect +add_executable(UnitTest_App_Settings "main.cpp" - "app_connect_local.cpp") -target_link_libraries(UnitTest_App_Connect ${CMAKE_DL_LIBS} GTest::GTest) + "startup_config.cpp" "settings_config.cpp") +target_link_libraries(UnitTest_App_Settings ${CMAKE_DL_LIBS} GTest::GTest) # Add the IDL Compiler unittest -add_test(NAME UnitTest_App_Connect COMMAND UnitTest_App_Connect) +add_test(NAME UnitTest_App_Settings COMMAND UnitTest_App_Settings) # Execute the test -add_custom_command(TARGET UnitTest_App_Connect POST_BUILD - COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_App_Connect.xml +add_custom_command(TARGET UnitTest_App_Settings POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_App_Settings.xml VERBATIM ) # Build dependencies -add_dependencies(UnitTest_App_Connect dependency_sdv_components) +add_dependencies(UnitTest_App_Settings dependency_sdv_components) diff --git a/tests/unit_tests/app_settings/main.cpp b/tests/unit_tests/app_settings/main.cpp new file mode 100644 index 0000000..6e711a0 --- /dev/null +++ b/tests/unit_tests/app_settings/main.cpp @@ -0,0 +1,41 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include "../../../global/process_watchdog.h" +#include "../../../global/localmemmgr.h" +#include "../../../sdv_services/core/app_settings.cpp" +#include "../../../sdv_services/core/toml_parser/parser_toml.cpp" +#include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp" +#include "../../../sdv_services/core/toml_parser/lexer_toml.cpp" +#include "../../../sdv_services/core/toml_parser/lexer_toml_token.cpp" +#include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" +#include "../../../sdv_services/core/toml_parser/code_snippet.cpp" +#include "../../../sdv_services/core/toml_parser/parser_node_indexer.cpp" +#include "../../../sdv_services/core/toml_parser/miscellaneous.cpp" + +/** + * @brief Main function + */ +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain(int argc, wchar_t* argv[]) +#else +extern "C" int main(int argc, char* argv[]) +#endif +{ + CProcessWatchdog watchdog; + + CLocalMemMgr memmgr; + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/unit_tests/app_settings/settings_config.cpp b/tests/unit_tests/app_settings/settings_config.cpp new file mode 100644 index 0000000..0951060 --- /dev/null +++ b/tests/unit_tests/app_settings/settings_config.cpp @@ -0,0 +1,537 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include + +#include "../../../global/exec_dir_helper.h" +#include "../../../sdv_services/core/app_settings.h" +#include "../../../sdv_services/core/toml_parser/miscellaneous.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" +#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" +#include + +// Load settings + // Invalid file + // No file + // Config paths + // Listeners + // Connections +// Save settings + // No file + // Config paths + // Listeners + // Connections + +std::filesystem::path GetSettingsFilePath() +{ + // The settings file is located at the exe directory with sub-directory 2000. + return GetExecDirectory() / "2000" / "settings.toml"; +} + +toml_parser::CParser ReadSettingsFile() +{ + std::ifstream fstream(GetSettingsFilePath()); + if (!fstream.is_open()) return {}; + toml_parser::CParser parser(std::string((std::istreambuf_iterator(fstream)), std::istreambuf_iterator())); + fstream.close(); + return parser; +} + +bool WriteSettingsFile(const std::string& rssSettings) +{ + std::filesystem::create_directories(GetSettingsFilePath().remove_filename()); + std::ofstream fstream(GetSettingsFilePath(), std::ios::trunc); + if (!fstream.is_open()) return false; + fstream << rssSettings; + return true; +} + +void DeleteSettingsFile() +{ + if (std::filesystem::exists(GetSettingsFilePath().remove_filename())) + std::filesystem::remove_all(GetSettingsFilePath().remove_filename()); +} + +TEST(AppSettingsTest_SettingsConfig, LoadInvalidConfig) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + const std::string ssInvalidToml = R"toml([Settings] +Version = hundred # Should be valid number)toml"; + const std::string ssInvalidValue = R"toml([Settings] +Version = "hundred" # Invalid value)toml"; + const std::string ssInvalidVersion = R"toml([Settings] +Version = 99)toml"; + const std::string ssNoVersion = R"toml([Settings])toml"; + + WriteSettingsFile(ssInvalidToml); + EXPECT_FALSE(settings.LoadSettings()); + WriteSettingsFile(ssInvalidValue); + EXPECT_FALSE(settings.LoadSettings()); + WriteSettingsFile(ssInvalidVersion); + EXPECT_FALSE(settings.LoadSettings()); + WriteSettingsFile(ssNoVersion); + EXPECT_FALSE(settings.LoadSettings()); +} + +TEST(AppSettingsTest_SettingsConfig, LoadNoConfig) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + // Read empty file - this is not an error + EXPECT_TRUE(settings.LoadSettings()); +} + +TEST(AppSettingsTest_SettingsConfig, SaveConfigWrongMode) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + EXPECT_TRUE(settings.SaveSettings()); + + // Verify for a file + EXPECT_FALSE(std::filesystem::exists(GetSettingsFilePath())); +} + +TEST(AppSettingsTest_SettingsConfig, SaveDefaultConfig) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Maintenance" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + EXPECT_TRUE(settings.SaveSettings()); + + // Verify for a file + EXPECT_TRUE(std::filesystem::exists(GetSettingsFilePath())); + + // Verify settings + auto parser = ReadSettingsFile(); + sdv::toml::CNodeCollection tableSettings(parser.Root().GetNodeDirect("Settings")); + EXPECT_TRUE(tableSettings); + EXPECT_EQ(tableSettings.GetDirect("Version").GetValue(), 100u); + sdv::toml::CNode nodePlatformConfig = tableSettings.GetDirect("PlatformConfig"); + EXPECT_TRUE(nodePlatformConfig); + sdv::toml::CNode nodeVehIfcConfig = tableSettings.GetDirect("VehIfcConfig"); + EXPECT_TRUE(nodeVehIfcConfig); + sdv::toml::CNode nodeVehAbstrConfig = tableSettings.GetDirect("VehAbstrConfig"); + EXPECT_TRUE(nodeVehAbstrConfig); + sdv::toml::CNode nodeAppConfig = tableSettings.GetDirect("AppConfig"); + EXPECT_TRUE(nodeAppConfig); + sdv::toml::CNodeCollection arrayListener = tableSettings.GetDirect("Listener"); + EXPECT_FALSE(arrayListener); + sdv::toml::CNodeCollection arrayConnection = tableSettings.GetDirect("Connection"); + EXPECT_FALSE(arrayConnection); +} + +TEST(AppSettingsTest_SettingsConfig, LoadSettings_SystemConfigs) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::platform_config), "platform.toml"); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::vehicle_interface_config), "vehicle_ifc.toml"); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::vehicle_abstraction_config), "vehicle_abstract.toml"); + + WriteSettingsFile(R"toml([Settings] +Version = 100 +PlatformConfig = "abc.toml" +VehIfcConfig = "def.toml" +VehAbstrConfig = "ghi.toml")toml"); + + // Read settings file + EXPECT_TRUE(settings.LoadSettings()); + + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::platform_config), "abc.toml"); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::vehicle_interface_config), "def.toml"); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::vehicle_abstraction_config), "ghi.toml"); +} + +TEST(AppSettingsTest_SettingsConfig, SaveSettings_SystemConfigs) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Maintenance" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::platform_config), "platform.toml"); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::vehicle_interface_config), "vehicle_ifc.toml"); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::vehicle_abstraction_config), "vehicle_abstract.toml"); + settings.DisableConfig(CAppSettings::EConfigType::platform_config); + settings.DisableConfig(CAppSettings::EConfigType::vehicle_interface_config); + settings.DisableConfig(CAppSettings::EConfigType::vehicle_abstraction_config); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::platform_config), ""); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::vehicle_interface_config), ""); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::vehicle_abstraction_config), ""); + + EXPECT_TRUE(settings.SaveSettings()); + + // Verify settings + auto parser = ReadSettingsFile(); + sdv::toml::CNodeCollection tableSettings(parser.Root().GetNodeDirect("Settings")); + EXPECT_TRUE(tableSettings); + EXPECT_TRUE(tableSettings.GetDirect("PlatformConfig").GetValueAsPath().empty()); + EXPECT_TRUE(tableSettings.GetDirect("VehIfcConfig").GetValueAsPath().empty()); + EXPECT_TRUE(tableSettings.GetDirect("VehAbstrConfig").GetValueAsPath().empty()); + + settings.EnableConfig(CAppSettings::EConfigType::platform_config); + settings.EnableConfig(CAppSettings::EConfigType::vehicle_interface_config); + settings.EnableConfig(CAppSettings::EConfigType::vehicle_abstraction_config); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::platform_config), "platform.toml"); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::vehicle_interface_config), "vehicle_ifc.toml"); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::vehicle_abstraction_config), "vehicle_abstract.toml"); + + EXPECT_TRUE(settings.SaveSettings()); + + // Verify settings + auto parser2 = ReadSettingsFile(); + sdv::toml::CNodeCollection tableSettings2(parser2.Root().GetNodeDirect("Settings")); + EXPECT_TRUE(tableSettings2); + EXPECT_EQ(tableSettings2.GetDirect("PlatformConfig").GetValueAsPath(), "platform.toml"); + EXPECT_EQ(tableSettings2.GetDirect("VehIfcConfig").GetValueAsPath(), "vehicle_ifc.toml"); + EXPECT_EQ(tableSettings2.GetDirect("VehAbstrConfig").GetValueAsPath(), "vehicle_abstract.toml"); +} + +TEST(AppSettingsTest_SettingsConfig, LoadSettings_UserConfig) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::user_config), "app_config.toml"); + + WriteSettingsFile(R"toml([Settings] +Version = 100 +AppConfig = "xyz.toml")toml"); + + // Read settings file + EXPECT_TRUE(settings.LoadSettings()); + + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::user_config), "xyz.toml"); + + settings.Reset(); + + // For all configurations except main, isolated and maintenance, the user config is supplied through the app config. + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Standalone" +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()) + R"toml( +Config = "klm.toml")toml")); + + EXPECT_EQ(settings.GetUserConfigPath(), "klm.toml"); +} + +TEST(AppSettingsTest_SettingsConfig, SaveSettings_UserConfig) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Maintenance" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::user_config), "app_config.toml"); + settings.DisableConfig(CAppSettings::EConfigType::user_config); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::user_config), ""); + + EXPECT_TRUE(settings.SaveSettings()); + + // Verify settings + auto parser = ReadSettingsFile(); + sdv::toml::CNodeCollection tableSettings(parser.Root().GetNodeDirect("Settings")); + EXPECT_TRUE(tableSettings); + EXPECT_TRUE(tableSettings.GetDirect("AppConfig").GetValueAsPath().empty()); + + settings.EnableConfig(CAppSettings::EConfigType::user_config); + EXPECT_EQ(settings.GetConfigPath(CAppSettings::EConfigType::user_config), "app_config.toml"); + + EXPECT_TRUE(settings.SaveSettings()); + + // Verify settings + auto parser2 = ReadSettingsFile(); + sdv::toml::CNodeCollection tableSettings2(parser2.Root().GetNodeDirect("Settings")); + EXPECT_TRUE(tableSettings2); + EXPECT_EQ(tableSettings2.GetDirect("AppConfig").GetValueAsPath(), "app_config.toml"); +} + +TEST(AppSettingsTest_SettingsConfig, LoadSettings_Listeners) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + // Default listener shoule be available. + EXPECT_EQ(settings.GetListeners().size(), 1u); + + WriteSettingsFile(R"toml([Settings] +Version = 100 + +[[Settings.Listener]] +Name = "MyListener1" +[Settings.Listener.Provider] +Name = "MyListenerProvider1" +[Settings.Listener.IpcChannel] +abc = "def" + +[[Settings.Listener]] +Name = "MyListener2" +[Settings.Listener.Provider] +Name = "MyListenerProvider2" +[Settings.Listener.IpcChannel] +xyz = 1234 +)toml"); + + // Read settings file - note: the listeners are part of a listener map. The order is not fixed. + // There will be an additional default listener. + EXPECT_TRUE(settings.LoadSettings()); + auto seqListeners = settings.GetListeners(); + ASSERT_EQ(seqListeners.size(), 3u); + EXPECT_TRUE(seqListeners[0] == "MyListener1" || seqListeners[1] == "MyListener1" || seqListeners[2] == "MyListener1"); + EXPECT_TRUE(seqListeners[0] == "MyListener2" || seqListeners[1] == "MyListener2" || seqListeners[2] == "MyListener2"); + EXPECT_TRUE(seqListeners[0] == "Default" || seqListeners[1] == "Default" || seqListeners[2] == "Default"); + EXPECT_TRUE(settings.GetListenerConfig("MyListener0").empty()); + EXPECT_TRUE(toml_parser::CompareEqual(settings.GetListenerConfig("MyListener1"), R"toml([Provider] +Name = "MyListenerProvider1" + +[IpcChannel] +abc = "def" +)toml")); + EXPECT_TRUE(toml_parser::CompareEqual(settings.GetListenerConfig("MyListener2"), R"toml([Provider] +Name = "MyListenerProvider2" + +[IpcChannel] +xyz = 1234 +)toml")); +} + +TEST(AppSettingsTest_SettingsConfig, SaveSettings_Listeners) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Maintenance" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + EXPECT_EQ(settings.GetListeners().size(), 1u); + + EXPECT_TRUE(settings.AddListenerConfig("MyListener2", R"toml([Provider] +Name = "MyListenerProvider2" +[IpcChannel] +abc = "def")toml")); + EXPECT_EQ(settings.GetListeners().size(), 2u); + + EXPECT_TRUE(settings.AddListenerConfig("MyListener1", R"toml([Provider] +Name = "MyListenerProvider1" +[IpcChannel] +xyz = 1234)toml")); + EXPECT_EQ(settings.GetListeners().size(), 3u); + + EXPECT_TRUE(settings.SaveSettings()); + + // Verify settings + auto parser = ReadSettingsFile(); + sdv::toml::CNodeCollection tableSettings(parser.Root().GetNodeDirect("Settings")); + [[maybe_unused]] std::string ss = parser.GenerateTOML(); + EXPECT_TRUE(tableSettings); + sdv::toml::CNodeCollection tableListener = tableSettings.GetDirect("Listener[0]"); + if (tableListener.GetDirect("Name").GetValueAsString() != "MyListener1") + tableListener = tableSettings.GetDirect("Listener[1]"); + EXPECT_TRUE(tableListener); + EXPECT_EQ(tableListener.GetDirect("Name").GetValueAsString(), "MyListener1"); + EXPECT_EQ(tableListener.GetDirect("Provider.Name").GetValueAsString(), "MyListenerProvider1"); + EXPECT_EQ(tableListener.GetDirect("IpcChannel.xyz").GetValue(), 1234); + tableListener = tableSettings.GetDirect("Listener[0]"); + if (tableListener.GetDirect("Name").GetValueAsString() != "MyListener2") + tableListener = tableSettings.GetDirect("Listener[1]"); + EXPECT_TRUE(tableListener); + EXPECT_EQ(tableListener.GetDirect("Name").GetValueAsString(), "MyListener2"); + EXPECT_EQ(tableListener.GetDirect("Provider.Name").GetValueAsString(), "MyListenerProvider2"); + EXPECT_EQ(tableListener.GetDirect("IpcChannel.abc").GetValueAsString(), "def"); + + // Remove one listener + settings.RemoveListenerConfig("MyListener1"); + EXPECT_EQ(settings.GetListeners().size(), 2u); + EXPECT_TRUE(settings.SaveSettings()); + + // Verify settings + auto parser2 = ReadSettingsFile(); + sdv::toml::CNodeCollection tableSettings2(parser2.Root().GetNodeDirect("Settings")); + EXPECT_TRUE(tableSettings2); + tableListener = tableSettings2.GetDirect("Listener[0]"); + EXPECT_TRUE(tableListener); + EXPECT_EQ(tableListener.GetDirect("Name").GetValueAsString(), "MyListener2"); + EXPECT_EQ(tableListener.GetDirect("Provider.Name").GetValueAsString(), "MyListenerProvider2"); + EXPECT_EQ(tableListener.GetDirect("IpcChannel.abc").GetValueAsString(), "def"); +} + +TEST(AppSettingsTest_SettingsConfig, LoadSettings_Connections) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + // Default connection only + EXPECT_EQ(settings.GetConnections().size(), 1); + + WriteSettingsFile(R"toml([Settings] +Version = 100 + +[[Settings.Connection]] +Name = "MyConnection1" +[Settings.Connection.Provider] +Name = "MyConnectionProvider1" +[Settings.Connection.IpcChannel] +abc = "def" + +[[Settings.Connection]] +Name = "MyConnection2" +[Settings.Connection.Provider] +Name = "MyConnectionProvider2" +[Settings.Connection.IpcChannel] +xyz = 1234 +)toml"); + + // Read settings file - note: the order of connections is preserved in the settings file. + // There will be an additional default connection. + EXPECT_TRUE(settings.LoadSettings()); + auto seqConnections = settings.GetConnections(); + ASSERT_EQ(seqConnections.size(), 3u); + EXPECT_EQ(seqConnections[0], "Default"); + EXPECT_EQ(seqConnections[1], "MyConnection1"); + EXPECT_EQ(seqConnections[2], "MyConnection2"); + EXPECT_TRUE(settings.GetConnectionConfig("MyConnection0").empty()); + EXPECT_TRUE(toml_parser::CompareEqual(settings.GetConnectionConfig("MyConnection1"), R"toml([Provider] +Name = "MyConnectionProvider1" + +[IpcChannel] +abc = "def" +)toml")); + EXPECT_TRUE(toml_parser::CompareEqual(settings.GetConnectionConfig("MyConnection2"), R"toml([Provider] +Name = "MyConnectionProvider2" + +[IpcChannel] +xyz = 1234 +)toml")); +} + +TEST(AppSettingsTest_SettingsConfig, SaveSettings_Connections) +{ + // Settings for the main application with a specific instance ID + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + DeleteSettingsFile(); // Needed to prevent settings file parsing during startup. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Maintenance" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + + EXPECT_EQ(settings.GetConnections().size(), 1u); + + EXPECT_TRUE(settings.AddConnectionConfig("MyConnection2", R"toml([Provider] +Name = "MyConnectionProvider2" +[IpcChannel] +abc = "def")toml")); + EXPECT_EQ(settings.GetConnections().size(), 2u); + + EXPECT_TRUE(settings.AddConnectionConfig("MyConnection1", R"toml([Provider] +Name = "MyConnectionProvider1" +[IpcChannel] +xyz = 1234)toml")); + EXPECT_EQ(settings.GetConnections().size(), 3u); + + EXPECT_TRUE(settings.SaveSettings()); + + // Verify settings + auto parser = ReadSettingsFile(); + sdv::toml::CNodeCollection tableSettings(parser.Root().GetNodeDirect("Settings")); + EXPECT_TRUE(tableSettings); + sdv::toml::CNodeCollection tableConnection = tableSettings.GetDirect("Connection[0]"); + EXPECT_TRUE(tableConnection); + EXPECT_EQ(tableConnection.GetDirect("Name").GetValueAsString(), "MyConnection2"); + EXPECT_EQ(tableConnection.GetDirect("Provider.Name").GetValueAsString(), "MyConnectionProvider2"); + EXPECT_EQ(tableConnection.GetDirect("IpcChannel.abc").GetValueAsString(), "def"); + tableConnection = tableSettings.GetDirect("Connection[1]"); + EXPECT_TRUE(tableConnection); + EXPECT_EQ(tableConnection.GetDirect("Name").GetValueAsString(), "MyConnection1"); + EXPECT_EQ(tableConnection.GetDirect("Provider.Name").GetValueAsString(), "MyConnectionProvider1"); + EXPECT_EQ(tableConnection.GetDirect("IpcChannel.xyz").GetValue(), 1234); + + // Remove one listener + settings.RemoveConnectionConfig("MyConnection1"); + EXPECT_EQ(settings.GetConnections().size(), 2u); + EXPECT_TRUE(settings.SaveSettings()); + + // Verify settings + auto parser2 = ReadSettingsFile(); + sdv::toml::CNodeCollection tableSettings2(parser2.Root().GetNodeDirect("Settings")); + EXPECT_TRUE(tableSettings2); + tableConnection = tableSettings2.GetDirect("Connection[0]"); + EXPECT_TRUE(tableConnection); + EXPECT_EQ(tableConnection.GetDirect("Name").GetValueAsString(), "MyConnection2"); + EXPECT_EQ(tableConnection.GetDirect("Provider.Name").GetValueAsString(), "MyConnectionProvider2"); + EXPECT_EQ(tableConnection.GetDirect("IpcChannel.abc").GetValueAsString(), "def"); +} diff --git a/tests/unit_tests/app_settings/startup_config.cpp b/tests/unit_tests/app_settings/startup_config.cpp new file mode 100644 index 0000000..7182d76 --- /dev/null +++ b/tests/unit_tests/app_settings/startup_config.cpp @@ -0,0 +1,341 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include "../../../sdv_services/core/app_settings.h" +#include "../../../global/exec_dir_helper.h" +#include "../../../sdv_services/core/toml_parser/miscellaneous.h" + +// Load startup config + // Invalid config + // Empty config (default config) + // Application types + // Instance ID + // Log handler + // Console output + // Config path + +TEST(AppSettingsTest_StartupConfig, InvalidConfig) +{ + const std::string ssInvalidToml = R"toml([Application] +Mode = Standalone # Missing quotes)toml"; + const std::string ssInvalidValue = R"toml([Application] +Mode = "Supadupa" # Invalid value)toml"; + const std::string ssAdditionalValues = R"toml([Application] +Mode = "Standalone" +SpecialAction = "Fly to the moon" # Additional value)toml"; + + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + EXPECT_FALSE(settings.ProcessAppStartupConfig(ssInvalidToml)); + EXPECT_FALSE(settings.ProcessAppStartupConfig(ssInvalidValue)); + EXPECT_TRUE(settings.ProcessAppStartupConfig(ssAdditionalValues)); +} + +TEST(AppSettingsTest_StartupConfig, DefaultConfig) +{ + const std::string ssEmptyToml; + + CAppSettings settings; + EXPECT_EQ(settings.GetInstanceID(), 0u); + EXPECT_TRUE(settings.ProcessAppStartupConfig(ssEmptyToml)); + EXPECT_TRUE(settings.IsStandaloneApplication()); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::info); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::error); + EXPECT_EQ(settings.GetConsoleReporting(), CAppSettings::EAppConsoleReporting::normal); + EXPECT_EQ(settings.GetInstanceID(), 1000u); +} + +TEST(AppSettingsTest_StartupConfig, ApplicationTypes) +{ + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + EXPECT_EQ(settings.GetContextType(), sdv::app::EAppContext::no_context); + EXPECT_TRUE(settings.ProcessAppStartupConfig("")); + EXPECT_EQ(settings.GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_TRUE(settings.IsStandaloneApplication()); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "External")toml")); + EXPECT_EQ(settings.GetContextType(), sdv::app::EAppContext::external); + EXPECT_TRUE(settings.IsExternalApplication()); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Standalone")toml")); + EXPECT_EQ(settings.GetContextType(), sdv::app::EAppContext::standalone); + EXPECT_TRUE(settings.IsStandaloneApplication()); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Isolated")toml")); + EXPECT_EQ(settings.GetContextType(), sdv::app::EAppContext::isolated); + EXPECT_TRUE(settings.IsIsolatedApplication()); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main")toml")); + EXPECT_EQ(settings.GetContextType(), sdv::app::EAppContext::main); + EXPECT_TRUE(settings.IsMainApplication()); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Essential")toml")); + EXPECT_EQ(settings.GetContextType(), sdv::app::EAppContext::essential); + EXPECT_TRUE(settings.IsEssentialApplication()); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Maintenance")toml")); + EXPECT_EQ(settings.GetContextType(), sdv::app::EAppContext::maintenance); + EXPECT_TRUE(settings.IsMaintenanceApplication()); +} + +TEST(AppSettingsTest_StartupConfig, InstanceID) +{ + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + EXPECT_EQ(settings.GetInstanceID(), 0u); + + // Change type to main; no instance ID yet + settings.SetContextType(sdv::app::EAppContext::main); + EXPECT_EQ(settings.GetInstanceID(), 0u); + + // Set instance ID, but no type + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Instance = 1234)toml")); + EXPECT_EQ(settings.GetInstanceID(), 1234u); + + // Change types, only main, isolated and maintenance should provide the instance ID. + settings.SetContextType(sdv::app::EAppContext::standalone); + EXPECT_EQ(settings.GetInstanceID(), 1234u); + settings.SetContextType(sdv::app::EAppContext::external); + EXPECT_EQ(settings.GetInstanceID(), 1234u); + settings.SetContextType(sdv::app::EAppContext::isolated); + EXPECT_EQ(settings.GetInstanceID(), 1234u); + settings.SetContextType(sdv::app::EAppContext::main); + EXPECT_EQ(settings.GetInstanceID(), 1234u); + settings.SetContextType(sdv::app::EAppContext::essential); + EXPECT_EQ(settings.GetInstanceID(), 1234u); + settings.SetContextType(sdv::app::EAppContext::maintenance); + EXPECT_EQ(settings.GetInstanceID(), 1234u); + settings.Reset(); + EXPECT_EQ(settings.GetInstanceID(), 0u); +} + +TEST(AppSettingsTest_StartupConfig, LogHandler) +{ + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + + // No config + EXPECT_TRUE(settings.GetLoggerClass().empty()); + EXPECT_TRUE(settings.GetLoggerModulePath().empty()); + EXPECT_TRUE(settings.GetLoggerProgramTag().empty()); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::info); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::error); + + // Default config + EXPECT_TRUE(settings.ProcessAppStartupConfig("")); + EXPECT_EQ(settings.GetLoggerClass(), "DefaultLoggerService"); + EXPECT_TRUE(settings.GetLoggerModulePath().empty()); + EXPECT_TRUE(settings.GetLoggerProgramTag().empty()); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::info); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::error); + + // Default logger when main or isolated + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main")toml")); + EXPECT_EQ(settings.GetLoggerClass(), "DefaultLoggerService"); + EXPECT_TRUE(settings.GetLoggerModulePath().empty()); + EXPECT_TRUE(settings.GetLoggerProgramTag().empty()); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::info); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::info); + + // Default logger when main or isolated + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Isolated")toml")); + EXPECT_EQ(settings.GetLoggerClass(), "DefaultLoggerService"); + EXPECT_TRUE(settings.GetLoggerModulePath().empty()); + EXPECT_TRUE(settings.GetLoggerProgramTag().empty()); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::info); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::info); + + // Default logger for all others + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Standalone")toml")); + EXPECT_EQ(settings.GetLoggerClass(), "DefaultLoggerService"); + EXPECT_TRUE(settings.GetLoggerModulePath().empty()); + EXPECT_TRUE(settings.GetLoggerProgramTag().empty()); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::info); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::error); + + // Set severity level for logger + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +Filter = "Trace")toml")); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::trace); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +Filter = "Debug")toml")); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::debug); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +Filter = "Info")toml")); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::info); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +Filter = "Warning")toml")); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::warning); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +Filter = "Error")toml")); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::error); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +Filter = "Fatal")toml")); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::fatal); + EXPECT_FALSE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +Filter = "Wrong")toml")); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::fatal); + + // Set severity level for console + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +ViewFilter = "Trace")toml")); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::trace); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +ViewFilter = "Debug")toml")); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::debug); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +ViewFilter = "Info")toml")); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::info); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +ViewFilter = "Warning")toml")); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::warning); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +ViewFilter = "Error")toml")); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::error); + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +ViewFilter = "Fatal")toml")); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::fatal); + EXPECT_FALSE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +ViewFilter = "Wrong")toml")); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::fatal); + + // Explicit logger + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([LogHandler] +Class = "logger_class" +Path = "logger.xyz" +Tag = "logger_tag" +Filter = "Trace" +ViewFilter = "Warning" +UnkownValue = "hi")toml")); + EXPECT_EQ(settings.GetLoggerClass(), "logger_class"); + EXPECT_EQ(settings.GetLoggerModulePath(), "logger.xyz"); + EXPECT_EQ(settings.GetLoggerProgramTag(), "logger_tag"); + EXPECT_EQ(settings.GetLoggerSeverityFilter(), sdv::core::ELogSeverity::trace); + EXPECT_EQ(settings.GetConsoleSeverityFilter(), sdv::core::ELogSeverity::warning); +} + +TEST(AppSettingsTest_StartupConfig, ConnectionRetries) +{ + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + + // Default is 5 retries + EXPECT_EQ(settings.GetConnectRetries(), 5u); + EXPECT_TRUE(settings.ProcessAppStartupConfig("")); + EXPECT_EQ(settings.GetConnectRetries(), 5u); + + // Set 0 retries - this is not an error and minimizes to 3 + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Connections] +Retries = 0)toml")); + EXPECT_EQ(settings.GetConnectRetries(), 3u); + + // Set 10 retries + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Connections] +Retries = 10)toml")); + EXPECT_EQ(settings.GetConnectRetries(), 10u); + + // Set 100 retries - this is not an error and maximizes to 30 + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Connections] +Retries = 100)toml")); + EXPECT_EQ(settings.GetConnectRetries(), 30u); +} + +TEST(AppSettingsTest_StartupConfig, InstallDir) +{ + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + + // Empty install directory + EXPECT_TRUE(settings.GetRootDir().empty()); + EXPECT_TRUE(settings.GetInstallDir().empty()); + + // Install directory has significance with main, isolated and maintenance apps only. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + EXPECT_TRUE(settings.GetRootDir().empty()); + EXPECT_TRUE(settings.GetInstallDir().empty()); + + // Use main and a specific instance ID + std::filesystem::remove_all(GetExecDirectory() / "2000"); // Needed to prevent settings file parsing. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main" +Instance = 2000 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + EXPECT_EQ(settings.GetRootDir(), GetExecDirectory()); + EXPECT_EQ(settings.GetInstallDir(), GetExecDirectory() / "2000"); + + // Use isolated and a specific instance ID + std::filesystem::remove_all(GetExecDirectory() / "2001"); // Needed to prevent settings file parsing. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Isolated" +Instance = 2001 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + EXPECT_EQ(settings.GetRootDir(), GetExecDirectory()); + EXPECT_EQ(settings.GetInstallDir(), GetExecDirectory() / "2001"); + + // Use maintenance and a specific instance ID + std::filesystem::remove_all(GetExecDirectory() / "2002"); // Needed to prevent settings file parsing. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Maintenance" +Instance = 2002 +InstallDir = )toml" + toml_parser::QuoteText(GetExecDirectory().generic_u8string()))); + EXPECT_EQ(settings.GetRootDir(), GetExecDirectory()); + EXPECT_EQ(settings.GetInstallDir(), GetExecDirectory() / "2002"); + + // Relative path + std::filesystem::remove_all(GetExecDirectory() / "2003"); // Needed to prevent settings file parsing. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Maintenance" +Instance = 2003 +InstallDir = "..")toml")); + EXPECT_EQ(settings.GetRootDir(), GetExecDirectory() / ".."); + EXPECT_EQ(settings.GetInstallDir(), GetExecDirectory() / ".." / "2003"); +} + +TEST(AppSettingsTest_StartupConfig, CustomConfigFile) +{ + CAppSettings settings; + settings.SetConsoleReporting(CAppSettings::EAppConsoleReporting::silent); + + // Default empty + EXPECT_TRUE(settings.GetUserConfigPath().empty()); + + // Application config path has no significance with main, isolated and maintenance apps only. + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Config = "abc.def")toml")); + EXPECT_EQ(settings.GetUserConfigPath(), "abc.def"); + + // Use main + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Main" +Config = "def.hij")toml")); + EXPECT_EQ(settings.GetUserConfigPath(), "abc.def"); + + // Use isolated + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Isolated" +Config = "hij.klm")toml")); + EXPECT_EQ(settings.GetUserConfigPath(), "abc.def"); + + // Use maintenance + EXPECT_TRUE(settings.ProcessAppStartupConfig(R"toml([Application] +Mode = "Maintenance" +Config = "klm.nop")toml")); + EXPECT_EQ(settings.GetUserConfigPath(), "abc.def"); +} \ No newline at end of file diff --git a/tests/unit_tests/core_loader/core_loader_test.cpp b/tests/unit_tests/core_loader/core_loader_test.cpp index 093fc54..982b677 100644 --- a/tests/unit_tests/core_loader/core_loader_test.cpp +++ b/tests/unit_tests/core_loader/core_loader_test.cpp @@ -283,7 +283,7 @@ TEST(CoreLoaderTest, EnvVarLoading) EXPECT_EQ(executor.Execute(), 1); } -TEST(CoreLoaderTest, CfgFileLoadingRel) +TEST(CoreLoaderTest, CoreRelocFileLoadingRel) { CTestExecute executor; @@ -291,14 +291,17 @@ TEST(CoreLoaderTest, CfgFileLoadingRel) fstream << R"cfg(# Some bogus information unknown_var = 2 +[CoreLocation] +Version = 100 + # Core library path -directory = "../../../bin")cfg"; +Runtime = "../../../bin")cfg"; fstream.close(); EXPECT_EQ(executor.Execute(), 1); } -TEST(CoreLoaderTest, CfgFileLoadingAbs) +TEST(CoreLoaderTest, CoreRelocFileLoadingAbs) { CTestExecute executor; @@ -306,15 +309,18 @@ TEST(CoreLoaderTest, CfgFileLoadingAbs) fstream << R"cfg(# Some bogus information unknown_var = 2 +[CoreLocation] +Version = 100 + # Core library path -directory = ")cfg" << (executor.GetExeDir() / "../../../bin").lexically_normal().generic_string() << "\""; +Runtime = ")cfg" << (executor.GetExeDir() / "../../../bin").lexically_normal().generic_string() << "\""; fstream.close(); EXPECT_EQ(executor.Execute(), 1); } #ifdef _WIN32 -TEST(CoreLoaderTest, CfgFileLoadingRelWin) +TEST(CoreLoaderTest, CoreRelocFileLoadingRelWin) { CTestExecute executor; @@ -322,14 +328,17 @@ TEST(CoreLoaderTest, CfgFileLoadingRelWin) fstream << R"cfg(# Some bogus information unknown_var = 2 +[CoreLocation] +Version = 100 + # Core library path -directory = "..\\..\\..\\bin")cfg"; +Runtime = "..\\..\\..\\bin")cfg"; fstream.close(); EXPECT_EQ(executor.Execute(), 1); } -TEST(CoreLoaderTest, CfgFileLoadingAbsWin) +TEST(CoreLoaderTest, CoreRelocFileLoadingAbsWin) { CTestExecute executor; @@ -352,8 +361,11 @@ TEST(CoreLoaderTest, CfgFileLoadingAbsWin) fstream << R"cfg(# Some bogus information unknown_var = 2 +[CoreLocation] +Version = 100 + # Core library path -directory = ")cfg" << ssDir << "\""; +Runtime = ")cfg" << ssDir << "\""; fstream.close(); EXPECT_EQ(executor.Execute(), 1); diff --git a/tests/unit_tests/dbc_parser/dbc_parser_test.cpp b/tests/unit_tests/dbc_parser/dbc_parser_test.cpp index 752e8bc..69c8ba7 100644 --- a/tests/unit_tests/dbc_parser/dbc_parser_test.cpp +++ b/tests/unit_tests/dbc_parser/dbc_parser_test.cpp @@ -156,7 +156,7 @@ TEST_F(CDbcParserTest, SourceContent) TEST_F(CDbcParserTest, SourceLineColumn) { - std::string ssSource = R"code(VERSION "" + std::string ssSource = R"dbc(VERSION "" NS_ : @@ -169,7 +169,7 @@ NS_ : CAT_ FILTER BA_DEF_DEF_ - EV_DATA_)code"; + EV_DATA_)dbc"; dbc::CDbcSource src(ssSource); EXPECT_EQ(src.CalcLine(), 1); @@ -370,10 +370,10 @@ TEST_F(CDbcParserTest, NewSymbols) EXPECT_NO_THROW(parser.Parse(srcOneNS)); // All symbols - std::string ssAllNS = R"code(NS_ : NS_DESC_ CM_ BA_DEF_ BA_ VAL_ CAT_DEF_ CAT_ FILTER BA_DEF_DEF_ EV_DATA_ + std::string ssAllNS = R"dbc(NS_ : NS_DESC_ CM_ BA_DEF_ BA_ VAL_ CAT_DEF_ CAT_ FILTER BA_DEF_DEF_ EV_DATA_ ENVVAR_DATA_ SGTYPE_ SGTYPE_VAL_ BA_DEF_SGTYPE_ BA_SGTYPE_ SIG_TYPE_REF_ VAL_TABLE_ SIG_GROUP_ SIG_VALTYPE_ SIGTYPE_VALTYPE_ BO_TX_BU_ BA_DEF_REL_ BA_REL_ BA_DEF_DEF_REL_ BU_SG_REL_ - BU_EV_REL_ BU_BO_REL_ SG_MUL_VAL_)code"; + BU_EV_REL_ BU_BO_REL_ SG_MUL_VAL_)dbc"; dbc::CDbcSource srcAllNS(ssAllNS); EXPECT_NO_THROW(parser.Parse(srcAllNS)); @@ -628,7 +628,7 @@ TEST_F(CDbcParserTest, SignalDef) EXPECT_TRUE(parser.HasNodeDef("nodeRx2")); // Message definition - std::string ssValidMsgDef1 = R"code( + std::string ssValidMsgDef1 = R"dbc( BO_ 1 msg_big_endian: 8 nodeTx SG_ sig1 : 7|4@0+ (1,0) [0|8191] "Nm" nodeRx1 SG_ sig2 : 3|8@0+ (1,0) [0|8191] "Nm" nodeRx1, nodeRx2 @@ -651,7 +651,7 @@ TEST_F(CDbcParserTest, SignalDef) SG_ sig5a m 1: 24|8@1+ (1,0) [0|8191] "Nm" Vector__XXX SG_ sig2b m 0: 4|32@1+ (1,0) [0|8191] "Nm" nodeRx1, nodeRx2 SG_ sig6 : 32|32@1+ (1,0) [0|8191] "Nm" Vector__XXX -)code"; +)dbc"; dbc::CDbcSource srcValidMsgDef1(ssValidMsgDef1); EXPECT_NO_THROW(parser.Parse(srcValidMsgDef1)); EXPECT_TRUE(parser.HasSignalDef("msg_big_endian", "sig1")); @@ -725,32 +725,32 @@ TEST_F(CDbcParserTest, SignalDef) EXPECT_TRUE(parser.GetSignalDefExtId(3, "sig6").second); // Duplicate signals - std::string ssDuplicateSigDef1 = R"code( + std::string ssDuplicateSigDef1 = R"dbc( BO_ 4 msg4: 8 nodeTx SG_ sig1 : 52|13@0+ (1,0) [0|8191] "Nm" nodeRx1 SG_ sig2 : 36|13@0+ (1,0) [0|8191] "Nm" nodeRx1, nodeRx2 SG_ sig1 : 18|13@0+ (1,0) [0|8191] "Nm" Vector__XXX - )code"; + )dbc"; dbc::CDbcSource srcDuplicateSigDef1(ssDuplicateSigDef1); EXPECT_THROW(parser.Parse(srcDuplicateSigDef1), dbc::SDbcParserException); // Invalid signals - std::string ssInvalidStartBit = R"code( + std::string ssInvalidStartBit = R"dbc( BO_ 5 msg5: 8 nodeTx SG_ sig1 : 64|13@0+ (1,0) [0|8191] "Nm" nodeRx1 - )code"; + )dbc"; dbc::CDbcSource srcInvalidStartBit(ssInvalidStartBit); EXPECT_THROW(parser.Parse(srcInvalidStartBit), dbc::SDbcParserException); - std::string ssInvalidLengthLittleEndian = R"code( + std::string ssInvalidLengthLittleEndian = R"dbc( BO_ 6 msg6: 8 nodeTx SG_ sig1 : 33|32@1+ (1,0) [0|8191] "Nm" nodeRx1 - )code"; + )dbc"; dbc::CDbcSource srcInvalidLengthLittleEndian(ssInvalidLengthLittleEndian); EXPECT_THROW(parser.Parse(srcInvalidLengthLittleEndian), dbc::SDbcParserException); - std::string ssInvalidLengthBigEndian = R"code( + std::string ssInvalidLengthBigEndian = R"dbc( BO_ 7 msg7: 8 nodeTx SG_ sig1 : 38|32@0+ (1,0) [0|8191] "Nm" nodeRx1 - )code"; + )dbc"; dbc::CDbcSource srcInvalidLengthBigEndian(ssInvalidLengthBigEndian); EXPECT_THROW(parser.Parse(srcInvalidLengthBigEndian), dbc::SDbcParserException); } @@ -767,13 +767,13 @@ TEST_F(CDbcParserTest, SignalTypeDef) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( BO_ 1 msg_float: 8 nodeTx SG_ sig_float : 0|32@1- (1,0) [0|8191] "Nm" nodeRx SG_ sig_int : 32|32@1+ (1,0) [0|8191] "Nm" nodeRx BO_ 2 msg_double: 8 nodeTx SG_ sig_double : 0|64@1- (1,0) [0|8191] "Nm" nodeRx - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); EXPECT_TRUE(parser.HasMsgDef("msg_float")); @@ -789,11 +789,11 @@ TEST_F(CDbcParserTest, SignalTypeDef) EXPECT_EQ(prSigDouble.first.eValType, dbc::SSignalDef::EValueType::signed_integer); // Set the signal value types. - std::string ssSigValType = R"code( + std::string ssSigValType = R"dbc( SIG_VALTYPE_ 1 sig_float 1; SIG_VALTYPE_ 1 sig_int 0; SIG_VALTYPE_ 2 sig_double 2; - )code"; + )dbc"; dbc::CDbcSource srcSigValType(ssSigValType); EXPECT_NO_THROW(parser.Parse(srcSigValType)); @@ -877,11 +877,11 @@ TEST_F(CDbcParserTest, SignalValueDescriptions) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( BO_ 1 msg_enum: 8 nodeTx SG_ sig_enum : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig_enum_empty : 0|32@1+ (1,0) [0|0] "" nodeRx - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); EXPECT_TRUE(parser.HasMsgDef("msg_enum")); @@ -933,11 +933,11 @@ TEST_F(CDbcParserTest, EnvironmentVariableDef) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Environment variable definition - std::string ssEnvVarDef = R"code( + std::string ssEnvVarDef = R"dbc( EV_ var1 : 0 [10|20] "steps" 15 100 DUMMY_NODE_VECTOR3 nodeTx, nodeRx; EV_ var2 : 1 [0.1|0.2] "tenth steps" 0.5 101 DUMMY_NODE_VECTOR2 nodeTx; EV_ var3 : 2 [0.0|0.0] "string val" 0.0 102 DUMMY_NODE_VECTOR8001 nodeRx; -)code"; +)dbc"; dbc::CDbcSource srcEnvVarDef(ssEnvVarDef); EXPECT_NO_THROW(parser.Parse(srcEnvVarDef)); auto vecEnvVars = parser.GetEnvVarNames(); @@ -996,18 +996,18 @@ TEST_F(CDbcParserTest, EnvVarData) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Environment variable definition - std::string ssEnvVarDef = R"code( + std::string ssEnvVarDef = R"dbc( EV_ var1 : 0 [10|20] "steps" 15 100 DUMMY_NODE_VECTOR3 nodeTx, nodeRx; EV_ var2 : 1 [0.1|0.2] "tenth steps" 0.5 101 DUMMY_NODE_VECTOR2 nodeTx; EV_ var3 : 2 [0.0|0.0] "string val" 0.0 102 DUMMY_NODE_VECTOR8001 nodeRx; -)code"; +)dbc"; dbc::CDbcSource srcEnvVarDef(ssEnvVarDef); EXPECT_NO_THROW(parser.Parse(srcEnvVarDef)); // Environment variable data definition - std::string ssEnvVarDataDef = R"code( + std::string ssEnvVarDataDef = R"dbc( ENVVAR_DATA_ var1 : 10; -)code"; +)dbc"; dbc::CDbcSource srcEnvVarDataDef(ssEnvVarDataDef); EXPECT_NO_THROW(parser.Parse(srcEnvVarDataDef)); auto prEnvVar = parser.GetEnvVarDef("var1"); @@ -1038,11 +1038,11 @@ TEST_F(CDbcParserTest, EnvVarValueDescriptions) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Environment variable definition - std::string ssEnvVarDef = R"code( + std::string ssEnvVarDef = R"dbc( EV_ var1 : 0 [10|20] "steps" 15 100 DUMMY_NODE_VECTOR3 nodeTx, nodeRx; EV_ var2 : 1 [0.1|0.2] "tenth steps" 0.5 101 DUMMY_NODE_VECTOR2 nodeTx; EV_ var3 : 2 [0.0|0.0] "string val" 0.0 102 DUMMY_NODE_VECTOR8001 nodeRx; -)code"; +)dbc"; dbc::CDbcSource srcEnvVarDef(ssEnvVarDef); EXPECT_NO_THROW(parser.Parse(srcEnvVarDef)); @@ -1079,17 +1079,17 @@ TEST_F(CDbcParserTest, ExtSignalTypeDef) dbc::CDbcParser parser; // Value table - std::string ssValueTable = R"code( + std::string ssValueTable = R"dbc( VAL_TABLE_ table1 10 "ten" 20 "twenty"; -)code"; +)dbc"; dbc::CDbcSource srcValueTable(ssValueTable); EXPECT_NO_THROW(parser.Parse(srcValueTable)); // Value table type definition - std::string ssValueTableType = R"code( + std::string ssValueTableType = R"dbc( SGTYPE_ sgtype1 : 32 @ 1+ (1.0, 0) [0|100] "signal unit" 50, table1; SGTYPE_ sgtype2 : 16 @ 0- (-10.5, 10) [-100|200] "signal unit2" 13, Vector__XXX; -)code"; +)dbc"; dbc::CDbcSource srcValueTableType(ssValueTableType); EXPECT_NO_THROW(parser.Parse(srcValueTableType)); auto vecSignalTypes = parser.GetSignalTypeDefNames(); @@ -1129,17 +1129,17 @@ TEST_F(CDbcParserTest, SignalTypeRef) dbc::CDbcParser parser; // Value table - std::string ssValueTable = R"code( + std::string ssValueTable = R"dbc( VAL_TABLE_ table1 10 "ten" 20 "twenty"; VAL_TABLE_ table2 20 "twenty" 30 "thirty"; -)code"; +)dbc"; dbc::CDbcSource srcValueTable(ssValueTable); EXPECT_NO_THROW(parser.Parse(srcValueTable)); // Signal type definition - std::string ssSignalType = R"code( + std::string ssSignalType = R"dbc( SGTYPE_ sgtype1 : 32 @ 1+ (1.0, 0) [0|100] "signal unit" 50, table1; -)code"; +)dbc"; dbc::CDbcSource srcSignalType(ssSignalType); EXPECT_NO_THROW(parser.Parse(srcSignalType)); @@ -1151,11 +1151,11 @@ TEST_F(CDbcParserTest, SignalTypeRef) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( BO_ 1 msg: 8 nodeTx SG_ sig1 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig2 : 0|32@1+ (1,0) [0|0] "" nodeRx - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); @@ -1180,20 +1180,20 @@ TEST_F(CDbcParserTest, SignalGroupDef) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( BO_ 1 msg: 8 nodeTx SG_ sig1 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig2 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig3 : 0|32@1+ (1,0) [0|0] "" nodeRx - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); // Signal group definition - std::string ssSignalGroup = R"code( + std::string ssSignalGroup = R"dbc( SIG_GROUP_ 1 group1 10 : sig1 sig2; SIG_GROUP_ 1 group2 20 : sig3; - )code"; + )dbc"; dbc::CDbcSource srcSignalGroup(ssSignalGroup); EXPECT_NO_THROW(parser.Parse(srcSignalGroup)); auto vecGroupNames = parser.GetSignalGroupDefNames(1); @@ -1220,12 +1220,12 @@ TEST_F(CDbcParserTest, GlobalComments) dbc::CDbcParser parser; // Global comments - std::string ssComments = R"code( + std::string ssComments = R"dbc( CM_ "first comment"; CM_ "second comment"; CM_ "third comment"; CM_ "fourth comment"; - )code"; + )dbc"; dbc::CDbcSource srcComments(ssComments); EXPECT_NO_THROW(parser.Parse(srcComments)); auto vecComments = parser.GetComments(); @@ -1248,12 +1248,12 @@ TEST_F(CDbcParserTest, NodeComments) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Node comments - std::string ssComments = R"code( + std::string ssComments = R"dbc( CM_ BU_ nodeTx "first comment"; CM_ BU_ nodeTx "second comment \"with quotes\""; CM_ BU_ nodeRx "third comment 'with single quotes'"; CM_ BU_ nodeRx "fourth comment"; - )code"; + )dbc"; dbc::CDbcSource srcComments(ssComments); EXPECT_NO_THROW(parser.Parse(srcComments)); auto prNode = parser.GetNodeDef("nodeTx"); @@ -1280,22 +1280,22 @@ TEST_F(CDbcParserTest, MessageComments) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( BO_ 1 msg: 8 nodeTx SG_ sig1 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig2 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig3 : 0|32@1+ (1,0) [0|0] "" nodeRx - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); // Message comments - std::string ssComments = R"code( + std::string ssComments = R"dbc( CM_ BO_ 1 "first comment"; CM_ BO_ 1 "second comment"; CM_ BO_ 1 "third comment"; CM_ BO_ 1 "fourth comment"; - )code"; + )dbc"; dbc::CDbcSource srcComments(ssComments); EXPECT_NO_THROW(parser.Parse(srcComments)); auto prMsgDef = parser.GetMsgDef(1); @@ -1319,22 +1319,22 @@ TEST_F(CDbcParserTest, SignalComments) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Signal definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( BO_ 1 msg: 8 nodeTx SG_ sig1 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig2 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig3 : 0|32@1+ (1,0) [0|0] "" nodeRx - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); // Node comments - std::string ssComments = R"code( + std::string ssComments = R"dbc( CM_ SG_ 1 sig1 "first comment"; CM_ SG_ 1 sig1 "second comment"; CM_ SG_ 1 sig2 "third comment"; CM_ SG_ 1 sig3 "fourth comment"; - )code"; + )dbc"; dbc::CDbcSource srcComments(ssComments); EXPECT_NO_THROW(parser.Parse(srcComments)); auto prSignal = parser.GetSignalDef(1, "sig1"); @@ -1364,21 +1364,21 @@ TEST_F(CDbcParserTest, EnvVarComments) EXPECT_TRUE(parser.HasNodeDef("nodeRx")); // Environment variable definition - std::string ssEnvVarDef = R"code( + std::string ssEnvVarDef = R"dbc( EV_ var1 : 0 [10|20] "steps" 15 100 DUMMY_NODE_VECTOR3 nodeTx, nodeRx; EV_ var2 : 1 [0.1|0.2] "tenth steps" 0.5 101 DUMMY_NODE_VECTOR2 nodeTx; EV_ var3 : 2 [0.0|0.0] "string val" 0.0 102 DUMMY_NODE_VECTOR8001 nodeRx; -)code"; +)dbc"; dbc::CDbcSource srcEnvVarDef(ssEnvVarDef); EXPECT_NO_THROW(parser.Parse(srcEnvVarDef)); // Env var comments - std::string ssComments = R"code( + std::string ssComments = R"dbc( CM_ EV_ var1 "first comment"; CM_ EV_ var1 "second comment"; CM_ EV_ var2 "third comment"; CM_ EV_ var3 "fourth comment"; - )code"; + )dbc"; dbc::CDbcSource srcComments(ssComments); EXPECT_NO_THROW(parser.Parse(srcComments)); auto prEnvVar = parser.GetEnvVarDef("var1"); @@ -1402,14 +1402,14 @@ TEST_F(CDbcParserTest, AttributeDef) // Global attribute definition // The use of "n/a" and "not-used" is not part of the official standard. - std::string ssAttribute = R"code( + std::string ssAttribute = R"dbc( BA_DEF_ "attr1" INT 10 20; BA_DEF_ "attr2" HEX 10 20; BA_DEF_ "attr3" FLOAT 1.0 10.9; BA_DEF_ "attr4" STRING; BA_DEF_ "attr5" ENUM "abc", "def", "ghi"; BA_DEF_ "attr6" ENUM "abc", "n/a", "not-used", "jkl", "n/a", "not-used", "stu"; - )code"; + )dbc"; dbc::CDbcSource srcAttribute(ssAttribute); EXPECT_NO_THROW(parser.Parse(srcAttribute)); auto vecAttr = parser.GetAttributeDefNames(); @@ -1470,27 +1470,27 @@ TEST_F(CDbcParserTest, AttributeDefaultValDef) // Global attribute definition // The use of "n/a" and "not-used" is not part of the official standard. - std::string ssAttribute = R"code( + std::string ssAttribute = R"dbc( BA_DEF_ "attr1" INT 10 20; BA_DEF_ "attr2" HEX 10 20; BA_DEF_ "attr3" FLOAT 1.0 10.9; BA_DEF_ "attr4" STRING; BA_DEF_ "attr5" ENUM "abc", "def", "ghi"; BA_DEF_ "attr6" ENUM "abc", "n/a", "not-used", "jkl", "n/a", "not-used", "stu"; - )code"; + )dbc"; dbc::CDbcSource srcAttribute(ssAttribute); EXPECT_NO_THROW(parser.Parse(srcAttribute)); // Attribute def default value // The use of an index instead of a value for the enum value is not part of the official standard. - std::string ssAttrDefDefaultVal = R"code( + std::string ssAttrDefDefaultVal = R"dbc( BA_DEF_DEF_ "attr1" 15; BA_DEF_DEF_ "attr2" 15; BA_DEF_DEF_ "attr3" 5.5; BA_DEF_DEF_ "attr4" "hello"; BA_DEF_DEF_ "attr5" "def"; BA_DEF_DEF_ "attr6" 6; - )code"; + )dbc"; dbc::CDbcSource srcAttrDefDefaultVal(ssAttrDefDefaultVal); EXPECT_NO_THROW(parser.Parse(srcAttrDefDefaultVal)); @@ -1514,20 +1514,20 @@ TEST_F(CDbcParserTest, GlobalAttributeVal) // Global attribute definition // The use of "n/a" and "not-used" is not part of the official standard. - std::string ssAttribute = R"code( + std::string ssAttribute = R"dbc( BA_DEF_ "attr1" INT 10 20; BA_DEF_ "attr2" HEX 10 20; BA_DEF_ "attr3" FLOAT 1.0 10.9; BA_DEF_ "attr4" STRING; BA_DEF_ "attr5" ENUM "abc", "def", "ghi"; BA_DEF_ "attr6" ENUM "abc", "n/a", "not-used", "jkl", "n/a", "not-used", "stu"; - )code"; + )dbc"; dbc::CDbcSource srcAttribute(ssAttribute); EXPECT_NO_THROW(parser.Parse(srcAttribute)); // Attribute value // The use of an index instead of a value for the enum value is not part of the official standard. - std::string ssAttrVal = R"code( + std::string ssAttrVal = R"dbc( BA_ "attr1" 11; BA_ "attr2" 12; BA_ "attr2" 13; @@ -1535,7 +1535,7 @@ TEST_F(CDbcParserTest, GlobalAttributeVal) BA_ "attr4" "hi"; BA_ "attr5" "ghi"; BA_ "attr6" 6; - )code"; + )dbc"; dbc::CDbcSource srcAttrVal(ssAttrVal); EXPECT_NO_THROW(parser.Parse(srcAttrVal)); auto vecAttr = parser.GetAttributes(); @@ -1562,20 +1562,20 @@ TEST_F(CDbcParserTest, NodeAttributeVal) // Node attribute definition // The use of "n/a" and "not-used" is not part of the official standard. - std::string ssAttribute = R"code( + std::string ssAttribute = R"dbc( BA_DEF_ BU_ "attr1" INT 10 20; BA_DEF_ BU_ "attr2" HEX 10 20; BA_DEF_ BU_ "attr3" FLOAT 1.0 10.9; BA_DEF_ BU_ "attr4" STRING; BA_DEF_ BU_ "attr5" ENUM "abc", "def", "ghi"; BA_DEF_ BU_ "attr6" ENUM "abc", "n/a", "not-used", "jkl", "n/a", "not-used", "stu"; - )code"; + )dbc"; dbc::CDbcSource srcAttribute(ssAttribute); EXPECT_NO_THROW(parser.Parse(srcAttribute)); // Attribute value // The use of an index instead of a value for the enum value is not part of the official standard. - std::string ssAttrVal = R"code( + std::string ssAttrVal = R"dbc( BA_ "attr1" BU_ nodeTx 11; BA_ "attr2" BU_ nodeTx 12; BA_ "attr2" BU_ nodeTx 13; @@ -1583,7 +1583,7 @@ TEST_F(CDbcParserTest, NodeAttributeVal) BA_ "attr4" BU_ nodeRx "hi"; BA_ "attr5" BU_ nodeRx "ghi"; BA_ "attr6" BU_ nodeRx 6; - )code"; + )dbc"; dbc::CDbcSource srcAttrVal(ssAttrVal); EXPECT_NO_THROW(parser.Parse(srcAttrVal)); auto prNodeDef = parser.GetNodeDef("nodeTx"); @@ -1612,31 +1612,31 @@ TEST_F(CDbcParserTest, MessageAttributeVal) // Message attribute definition // The use of "n/a" and "not-used" is not part of the official standard. - std::string ssAttribute = R"code( + std::string ssAttribute = R"dbc( BA_DEF_ BO_ "attr1" INT 10 20; BA_DEF_ BO_ "attr2" HEX 10 20; BA_DEF_ BO_ "attr3" FLOAT 1.0 10.9; BA_DEF_ BO_ "attr4" STRING; BA_DEF_ BO_ "attr5" ENUM "abc", "def", "ghi"; BA_DEF_ BO_ "attr6" ENUM "abc", "n/a", "not-used", "jkl", "n/a", "not-used", "stu"; - )code"; + )dbc"; dbc::CDbcSource srcAttribute(ssAttribute); EXPECT_NO_THROW(parser.Parse(srcAttribute)); // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( BO_ 1 msg: 8 nodeTx SG_ sig1 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig2 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig3 : 0|32@1+ (1,0) [0|0] "" nodeRx BO_ 2 msg2: 8 nodeRx - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); // Attribute value // The use of an index instead of a value for the enum value is not part of the official standard. - std::string ssAttrVal = R"code( + std::string ssAttrVal = R"dbc( BA_ "attr1" BO_ 1 11; BA_ "attr2" BO_ 1 12; BA_ "attr2" BO_ 1 13; @@ -1644,7 +1644,7 @@ TEST_F(CDbcParserTest, MessageAttributeVal) BA_ "attr4" BO_ 2 "hi"; BA_ "attr5" BO_ 2 "ghi"; BA_ "attr6" BO_ 2 6; - )code"; + )dbc"; dbc::CDbcSource srcAttrVal(ssAttrVal); EXPECT_NO_THROW(parser.Parse(srcAttrVal)); auto prMsgDef = parser.GetMsgDef(1); @@ -1673,31 +1673,31 @@ TEST_F(CDbcParserTest, SignalAttributeVal) // Signal attribute definition // The use of "n/a" and "not-used" is not part of the official standard. - std::string ssAttribute = R"code( + std::string ssAttribute = R"dbc( BA_DEF_ SG_ "attr1" INT 10 20; BA_DEF_ SG_ "attr2" HEX 10 20; BA_DEF_ SG_ "attr3" FLOAT 1.0 10.9; BA_DEF_ SG_ "attr4" STRING; BA_DEF_ SG_ "attr5" ENUM "abc", "def", "ghi"; BA_DEF_ SG_ "attr6" ENUM "abc", "n/a", "not-used", "jkl", "n/a", "not-used", "stu"; - )code"; + )dbc"; dbc::CDbcSource srcAttribute(ssAttribute); EXPECT_NO_THROW(parser.Parse(srcAttribute)); // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( BO_ 1 msg: 8 nodeTx SG_ sig1 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig2 : 0|32@1+ (1,0) [0|0] "" nodeRx SG_ sig3 : 0|32@1+ (1,0) [0|0] "" nodeRx BO_ 2 msg2: 8 nodeRx - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); // Attribute value // The use of an index instead of a value for the enum value is not part of the official standard. - std::string ssAttrVal = R"code( + std::string ssAttrVal = R"dbc( BA_ "attr1" SG_ 1 sig1 11; BA_ "attr2" SG_ 1 sig1 12; BA_ "attr2" SG_ 1 sig2 13; @@ -1705,7 +1705,7 @@ TEST_F(CDbcParserTest, SignalAttributeVal) BA_ "attr4" SG_ 1 sig3 "hi"; BA_ "attr5" SG_ 1 sig3 "ghi"; BA_ "attr6" SG_ 1 sig3 6; - )code"; + )dbc"; dbc::CDbcSource srcAttrVal(ssAttrVal); EXPECT_NO_THROW(parser.Parse(srcAttrVal)); auto prSigDef = parser.GetSignalDef(1, "sig1"); @@ -1736,29 +1736,29 @@ TEST_F(CDbcParserTest, EnvVarAttributeVal) // Environment variable attribute definition // The use of "n/a" and "not-used" is not part of the official standard. - std::string ssAttribute = R"code( + std::string ssAttribute = R"dbc( BA_DEF_ EV_ "attr1" INT 10 20; BA_DEF_ EV_ "attr2" HEX 10 20; BA_DEF_ EV_ "attr3" FLOAT 1.0 10.9; BA_DEF_ EV_ "attr4" STRING; BA_DEF_ EV_ "attr5" ENUM "abc", "def", "ghi"; BA_DEF_ EV_ "attr6" ENUM "abc", "n/a", "not-used", "jkl", "n/a", "not-used", "stu"; - )code"; + )dbc"; dbc::CDbcSource srcAttribute(ssAttribute); EXPECT_NO_THROW(parser.Parse(srcAttribute)); // Environment variable definition - std::string ssEnvVarDef = R"code( + std::string ssEnvVarDef = R"dbc( EV_ var1 : 0 [10|20] "steps" 15 100 DUMMY_NODE_VECTOR3 nodeTx, nodeRx; EV_ var2 : 1 [0.1|0.2] "tenth steps" 0.5 101 DUMMY_NODE_VECTOR2 nodeTx; EV_ var3 : 2 [0.0|0.0] "string val" 0.0 102 DUMMY_NODE_VECTOR8001 nodeRx; -)code"; +)dbc"; dbc::CDbcSource srcEnvVarDef(ssEnvVarDef); EXPECT_NO_THROW(parser.Parse(srcEnvVarDef)); // Attribute value // The use of an index instead of a value for the enum value is not part of the official standard. - std::string ssAttrVal = R"code( + std::string ssAttrVal = R"dbc( BA_ "attr1" EV_ var1 11; BA_ "attr2" EV_ var1 12; BA_ "attr2" EV_ var2 13; @@ -1766,7 +1766,7 @@ TEST_F(CDbcParserTest, EnvVarAttributeVal) BA_ "attr4" EV_ var3 "hi"; BA_ "attr5" EV_ var3 "ghi"; BA_ "attr6" EV_ var3 6; - )code"; + )dbc"; dbc::CDbcSource srcAttrVal(ssAttrVal); EXPECT_NO_THROW(parser.Parse(srcAttrVal)); auto prEnvVarDef = parser.GetEnvVarDef("var1"); @@ -1789,7 +1789,7 @@ TEST_F(CDbcParserTest, SignalExtendedMultiplexing) dbc::CDbcParser parser; // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( BO_ 100 MuxMsg: 1 Vector__XXX SG_ Mux_4 m2 : 6|2@1+ (1,0) [0|0] "" Vector__XXX SG_ Mux_3 m3M : 4|2@1+ (1,0) [0|0] "" Vector__XXX @@ -1799,7 +1799,7 @@ TEST_F(CDbcParserTest, SignalExtendedMultiplexing) SG_MUL_VAL_ 100 Mux_2 Mux_1 3-3, 5-10; SG_MUL_VAL_ 100 Mux_3 Mux_2 3-3; SG_MUL_VAL_ 100 Mux_4 Mux_3 2-2; - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); @@ -1842,7 +1842,7 @@ TEST_F(CDbcParserTest, ExampleDBC1) dbc::CDbcParser parser; // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( VERSION "" NS_ : @@ -1891,7 +1891,7 @@ CM_ "CAN communication matrix for power train electronics implemented: turn lights, warning lights, windows"; VAL_ 100 IdleRunning 0 "Running" 1 "Idle" ; - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); } @@ -1900,7 +1900,7 @@ TEST_F(CDbcParserTest, ExampleDBC2) dbc::CDbcParser parser; // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( VERSION "" @@ -2023,7 +2023,7 @@ VAL_ 1 HMI_Lateral_Long_Ctrl_Req 3 "LONGITUDINAL_CTRL_REQ" 2 "LONGITUDINAL_AND_L VAL_ 1 HMI_Algo_Variant_Request 4 "NONE" 3 "VARIANT_4" 2 "VARIANT_3" 1 "VARIANT_2" 0 "VARIANT_1" ; VAL_ 0 MAB_AI4Motion_Algo_Var_Selected 4 "NONE" 3 "VARIANT_4" 2 "VARIANT_3" 1 "VARIANT_2" 0 "VARIANT_1" ; VAL_ 0 MAB_Activation_AI4Motion 3 "LONGITUDINAL_CTRL_REQ" 2 "LONGITUDINAL_AND_LATERAL" 1 "LATERAL_CTRL_REQ" 0 "NONE" ; -SIG_VALTYPE_ 3221225472 New_Signal_25 : 2; )code"; +SIG_VALTYPE_ 3221225472 New_Signal_25 : 2; )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); } @@ -2032,7 +2032,7 @@ TEST_F(CDbcParserTest, ExampleDBC3) dbc::CDbcParser parser; // Message definition - std::string ssMsgDef = R"code( + std::string ssMsgDef = R"dbc( VERSION "" @@ -2135,7 +2135,7 @@ VERSION "" VAL_ 100 DRIVER_HEARTBEAT_cmd 2 "DRIVER_HEARTBEAT_cmd_REBOOT" 1 "DRIVER_HEARTBEAT_cmd_SYNC" 0 "DRIVER_HEARTBEAT_cmd_NOOP" ; VAL_ 500 IO_DEBUG_test_enum 2 "IO_DEBUG_test2_enum_two" 1 "IO_DEBUG_test2_enum_one" ; - )code"; + )dbc"; dbc::CDbcSource srcMsgDef(ssMsgDef); EXPECT_NO_THROW(parser.Parse(srcMsgDef)); } diff --git a/tests/unit_tests/install_package_composer/composer_test_suite.cpp b/tests/unit_tests/install_package_composer/composer_test_suite.cpp index 29bc8ad..462e83c 100644 --- a/tests/unit_tests/install_package_composer/composer_test_suite.cpp +++ b/tests/unit_tests/install_package_composer/composer_test_suite.cpp @@ -27,6 +27,7 @@ #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../../sdv_services/core/toml_parser/miscellaneous.cpp" #include "../../../sdv_services/core/toml_parser/code_snippet.cpp" +#include "../../../sdv_services/core/toml_parser/parser_node_indexer.cpp" #include #if defined(_WIN32) && defined(_UNICODE) diff --git a/tests/unit_tests/ipc_com/CMakeLists.txt b/tests/unit_tests/ipc_com/CMakeLists.txt index 804d20c..d3f5d6a 100644 --- a/tests/unit_tests/ipc_com/CMakeLists.txt +++ b/tests/unit_tests/ipc_com/CMakeLists.txt @@ -45,8 +45,10 @@ set_target_properties(UnitTest_IPC_Communication_ps PROPERTIES SUFFIX ".sdv") # Compile the source code add_executable(UnitTest_IPC_Communication "main.cpp" - - "ipc_com.cpp" + "ipc_com_sharedmem.cpp" + "ipc_com_uds.cpp" + "include.h" + "ipc_com_uds_tunnel.cpp" ) target_link_libraries(UnitTest_IPC_Communication ${CMAKE_DL_LIBS} GTest::GTest) diff --git a/tests/unit_tests/ipc_com/include.h b/tests/unit_tests/ipc_com/include.h new file mode 100644 index 0000000..feb182d --- /dev/null +++ b/tests/unit_tests/ipc_com/include.h @@ -0,0 +1,1300 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include +#include + +#include "../../../sdv_services/ipc_com/com_channel.h" +#include "../../../sdv_services/ipc_com/com_ctrl.h" +#include "../../../sdv_services/ipc_com/marshall_object.h" +#include "../../include/gtest_custom.h" +#include "generated/test_ifc.h" + +inline EHello& operator++(EHello& reHello, int) +{ + switch (reHello) + { + case EHello::hi: + reHello = EHello::huhu; + break; + case EHello::huhu: + reHello = EHello::hello; + break; + case EHello::hello: + reHello = EHello::hallo; + break; + case EHello::hallo: + reHello = EHello::servus; + break; + case EHello::servus: + reHello = EHello::gruessgott; + break; + case EHello::gruessgott: + reHello = EHello::hi; + break; + default: + break; + } + return reHello; +} + +inline EHello& operator--(EHello& reHello, int) +{ + switch (reHello) + { + case EHello::hi: + reHello = EHello::gruessgott; + break; + case EHello::huhu: + reHello = EHello::hi; + break; + case EHello::hello: + reHello = EHello::huhu; + break; + case EHello::hallo: + reHello = EHello::hello; + break; + case EHello::servus: + reHello = EHello::hallo; + break; + case EHello::gruessgott: + reHello = EHello::servus; + break; + default: + break; + } + return reHello; +} + +/** + * - Instantiate service + * - Assign server endpoint + * - Assign server endpoint + assign client endpoint + * - Create channel endpoint + * - Create channel endpoint + connect channel endpoint + * - Create proxy/stub connection + * - Reconnect + * - Multiple connections + * - Get other interface + * - Multiple access interface + * - Random multiple access interface + * - Random multiple access interface with reconnect + */ + +class CInterfaceTest : + public ISayHello, + public IRequestHello, + public IRegisterHelloCallback, + public IMegaTest, + public sdv::IInterfaceAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(ISayHello) + SDV_INTERFACE_ENTRY(IRequestHello) + SDV_INTERFACE_ENTRY(IRegisterHelloCallback) + SDV_INTERFACE_ENTRY(IMegaTest) + SDV_INTERFACE_ENTRY(sdv::IInterfaceAccess) + END_SDV_INTERFACE_MAP() + + /** + * @brief Say hello! + * @return The greeting text. + */ + virtual sdv::string Hello() override + { + return "Hello"; + } + + /** + * @brief Request the hello interface. + * @return pHello The hello interface. + */ + virtual ISayHello* Request() + { + return this; + } + + /** + * @brief Register the callback with the hello interface. + * @param[in] pHello The hello interface. + */ + virtual void Register(/*in*/ ISayHello* pHello) + { + m_pHello = pHello; + } + + /** + * @brief Update the mega struct. This will test marshalling and unmarshalling of the mega struct. Overload of + * IMegaTest::ProcessMegaStruct. + * @param[in] rsStruct Reference to the mega struct. + */ + virtual void ProcessMegaStruct(/*inout*/ SMegaStruct& rsStruct) override + { + rsStruct.bVal = !rsStruct.bVal; + rsStruct.sVal--; + rsStruct.usVal++; + rsStruct.lVal--; + rsStruct.ulVal++; + rsStruct.llVal--; + rsStruct.ullVal++; + rsStruct.i8Val--; + rsStruct.ui8Val++; + rsStruct.i16Val--; + rsStruct.ui16Val++; + rsStruct.i32Val--; + rsStruct.ui32Val++; + rsStruct.i64Val--; + rsStruct.ui64Val++; + rsStruct.cVal++; + rsStruct.c16Val++; + rsStruct.c32Val++; + rsStruct.wcVal++; + rsStruct.fVal -= 1000.000f; + rsStruct.dVal += 1000.000; + rsStruct.ldVal -= 1000.000; + rsStruct.nVal++; + rsStruct.eHelloVal = rsStruct.eHelloVal++; + if (rsStruct.idVal == sdv::GetInterfaceId()) + rsStruct.idVal = sdv::GetInterfaceId(); + else + rsStruct.idVal = 0; + rsStruct.ssVal += rsStruct.ssVal.empty() ? 'x' : rsStruct.ssVal.back() + 1; + rsStruct.ss8Val += rsStruct.ss8Val.empty() ? 'x' : rsStruct.ss8Val.back() + 1; + rsStruct.ss16Val += rsStruct.ss16Val.empty() ? u'x' : rsStruct.ss16Val.back() + 1; + rsStruct.ss32Val += rsStruct.ss32Val.empty() ? U'x' : rsStruct.ss32Val.back() + 1; + rsStruct.wssVal += rsStruct.wssVal.empty() ? L'x' : rsStruct.wssVal.back() + 1; + rsStruct.ssFixVal += rsStruct.ssFixVal.empty() ? 'x' : rsStruct.ssFixVal.back() + 1; + rsStruct.ss8FixVal += rsStruct.ss8FixVal.empty() ? 'x' : rsStruct.ss8FixVal.back() + 1; + rsStruct.ss16FixVal += rsStruct.ss16FixVal.empty() ? u'x' : rsStruct.ss16FixVal.back() + 1; + rsStruct.ss32FixVal += rsStruct.ss32FixVal.empty() ? U'x' : rsStruct.ss32FixVal.back() + 1; + rsStruct.wssFixVal += rsStruct.wssFixVal.empty() ? L'x' : rsStruct.wssFixVal.back() + 1; + rsStruct.ptr8Val.resize(rsStruct.ptr8Val.size() * 2); + for (size_t nIndex = 0; nIndex < rsStruct.ptr8Val.size() / 2; nIndex++) + rsStruct.ptr8Val[nIndex + rsStruct.ptr8Val.size() / 2] = rsStruct.ptr8Val[nIndex]; + rsStruct.ptrssVal.resize(rsStruct.ptrssVal.size() * 2); + for (size_t nIndex = 0; nIndex < rsStruct.ptrssVal.size() / 2; nIndex++) + rsStruct.ptrssVal[nIndex + rsStruct.ptrssVal.size() / 2] = rsStruct.ptrssVal[nIndex]; + for (size_t nIndex = 0; nIndex < rsStruct.ptr8FixVal.size(); nIndex++) + rsStruct.ptr8FixVal[nIndex] = static_cast(rsStruct.ptr8FixVal[nIndex] << 1); + for (size_t nIndex = 0; nIndex < rsStruct.ptrssFixVal.size(); nIndex++) + rsStruct.ptrssFixVal[nIndex] = rsStruct.ptrssFixVal[nIndex] + rsStruct.ptrssFixVal[nIndex]; + rsStruct.seq8Val.resize(rsStruct.seq8Val.size() * 2); + for (size_t nIndex = 0; nIndex < rsStruct.seq8Val.size() / 2; nIndex++) + rsStruct.seq8Val[nIndex + rsStruct.seq8Val.size() / 2] = rsStruct.seq8Val[nIndex]; + rsStruct.seqssVal.resize(rsStruct.seqssVal.size() * 2); + for (size_t nIndex = 0; nIndex < rsStruct.seqssVal.size() / 2; nIndex++) + rsStruct.seqssVal[nIndex + rsStruct.seqssVal.size() / 2] = rsStruct.seqssVal[nIndex]; + for (size_t nIndex = 0; nIndex < rsStruct.seq8FixVal.size(); nIndex++) + rsStruct.seq8FixVal[nIndex] = static_cast(rsStruct.seq8FixVal[nIndex] << 1); + for (size_t nIndex = 0; nIndex < rsStruct.seqssFixVal.size(); nIndex++) + rsStruct.seqssFixVal[nIndex] = rsStruct.seqssFixVal[nIndex] + rsStruct.seqssFixVal[nIndex]; + if (rsStruct.ifcVal.get() && rsStruct.ifcVal.id() == sdv::GetInterfaceId()) + { + m_summer.set(rsStruct.ifcVal.get()); + rsStruct.ifcVal = static_cast(&m_summer); + } + else + rsStruct.ifcVal = nullptr; + if (rsStruct.pMultiplyValue) + { + m_multiplier.set(rsStruct.pMultiplyValue); + rsStruct.pMultiplyValue = static_cast(&m_multiplier); + } + else + rsStruct.pMultiplyValue = nullptr; + rsStruct.sSubVal.i += 1; + rsStruct.sIndVal.i += 10; + // rsStruct.sUnnamedVal.i += 20; // 12.04.2024 EVE Unnamed structures are not supported by IDL compiler. + // Unnamed unions (inline definition) are not supported yet: PBI #398209 + // union switch(int32) { case 0: int32 i; default: int32 j; } uUnnamedVal; + // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 + // struct { int32 iAnonymous1; int32 iAnonymous2; }; + // union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; + + rsStruct.rgbVal[0] = !rsStruct.rgbVal[0]; + rsStruct.rgbVal[1] = !rsStruct.rgbVal[1]; + rsStruct.rgsVal[0]--; + rsStruct.rgsVal[1]++; + rsStruct.rgusVal[0]++; + rsStruct.rgusVal[1] += 2; + rsStruct.rglVal[0]--; + rsStruct.rglVal[1]++; + rsStruct.rgulVal[0]++; + rsStruct.rgulVal[1] += 2; + rsStruct.rgllVal[0]--; + rsStruct.rgllVal[1]++; + rsStruct.rgullVal[0]++; + rsStruct.rgullVal[1] += 2; + rsStruct.rgi8Val[0]--; + rsStruct.rgi8Val[1]++; + rsStruct.rgui8Val[0]++; + rsStruct.rgui8Val[1] += 2; + rsStruct.rgi16Val[0]--; + rsStruct.rgi16Val[1]++; + rsStruct.rgui16Val[0]++; + rsStruct.rgui16Val[1] += 2; + rsStruct.rgi32Val[0]--; + rsStruct.rgi32Val[1]++; + rsStruct.rgui32Val[0]++; + rsStruct.rgui32Val[1] += 2; + rsStruct.rgi64Val[0]--; + rsStruct.rgi64Val[1]++; + rsStruct.rgui64Val[0]++; + rsStruct.rgui64Val[1] += 2; + rsStruct.rgcVal[0]++; + rsStruct.rgcVal[1] += 2; + rsStruct.rgc16Val[0]++; + rsStruct.rgc16Val[1] += 2; + rsStruct.rgc32Val[0]++; + rsStruct.rgc32Val[1] += 2; + rsStruct.rgwcVal[0]++; + rsStruct.rgwcVal[1] += 2; + rsStruct.rgfVal[0] -= 1000.000f; + rsStruct.rgfVal[1] += 2000.000f; + rsStruct.rgdVal[0] += 1000.000; + rsStruct.rgdVal[1] -= 2000.000; + rsStruct.rgldVal[0] -= 1000.000; + rsStruct.rgldVal[1] += 2000.000; + rsStruct.rgnVal[0]++; + rsStruct.rgnVal[1] += 2; + rsStruct.rgeHelloVal[0] = rsStruct.rgeHelloVal[0]++; + rsStruct.rgeHelloVal[1] = rsStruct.rgeHelloVal[1]--; + if (rsStruct.rgidVal[0] == sdv::GetInterfaceId()) + rsStruct.rgidVal[0] = sdv::GetInterfaceId(); + else + rsStruct.rgidVal[0] = 0; + if (rsStruct.rgidVal[1] == sdv::GetInterfaceId()) + rsStruct.rgidVal[1] = sdv::GetInterfaceId(); + else + rsStruct.rgidVal[1] = 0; + rsStruct.rgssVal[0] += rsStruct.rgssVal[0].empty() ? 'x' : rsStruct.rgssVal[0].back() + 1; + rsStruct.rgssVal[1] += rsStruct.rgssVal[1].empty() ? 'x' : rsStruct.rgssVal[1].front() + 1; + rsStruct.rgss8Val[0] += rsStruct.rgss8Val[0].empty() ? 'x' : rsStruct.rgss8Val[0].back() + 1; + rsStruct.rgss8Val[1] += rsStruct.rgss8Val[1].empty() ? 'x' : rsStruct.rgss8Val[1].front() + 1; + rsStruct.rgss16Val[0] += rsStruct.rgss16Val[0].empty() ? u'x' : rsStruct.rgss16Val[0].back() + 1; + rsStruct.rgss16Val[1] += rsStruct.rgss16Val[1].empty() ? u'x' : rsStruct.rgss16Val[1].front() + 1; + rsStruct.rgss32Val[0] += rsStruct.rgss32Val[0].empty() ? U'x' : rsStruct.rgss32Val[0].back() + 1; + rsStruct.rgss32Val[1] += rsStruct.rgss32Val[1].empty() ? U'x' : rsStruct.rgss32Val[1].front() + 1; + rsStruct.rgwssVal[0] += rsStruct.rgwssVal[0].empty() ? L'x' : rsStruct.rgwssVal[0].back() + 1; + rsStruct.rgwssVal[1] += rsStruct.rgwssVal[1].empty() ? L'x' : rsStruct.rgwssVal[1].front() + 1; + rsStruct.rgssFixVal[0] += rsStruct.rgssFixVal[0].empty() ? 'x' : rsStruct.rgssFixVal[0].back() + 1; + rsStruct.rgssFixVal[1] += rsStruct.rgssFixVal[1].empty() ? 'x' : rsStruct.rgssFixVal[1].front() + 1; + rsStruct.rgss8FixVal[0] += rsStruct.rgss8FixVal[0].empty() ? 'x' : rsStruct.rgss8FixVal[0].back() + 1; + rsStruct.rgss8FixVal[1] += rsStruct.rgss8FixVal[1].empty() ? 'x' : rsStruct.rgss8FixVal[1].front() + 1; + rsStruct.rgss16FixVal[0] += rsStruct.rgss16FixVal[0].empty() ? u'x' : rsStruct.rgss16FixVal[0].back() + 1; + rsStruct.rgss16FixVal[1] += rsStruct.rgss16FixVal[1].empty() ? u'x' : rsStruct.rgss16FixVal[1].front() + 1; + rsStruct.rgss32FixVal[0] += rsStruct.rgss32FixVal[0].empty() ? U'x' : rsStruct.rgss32FixVal[0].back() + 1; + rsStruct.rgss32FixVal[1] += rsStruct.rgss32FixVal[1].empty() ? U'x' : rsStruct.rgss32FixVal[1].front() + 1; + rsStruct.rgwssFixVal[0] += rsStruct.rgwssFixVal[0].empty() ? L'x' : rsStruct.rgwssFixVal[0].back() + 1; + rsStruct.rgwssFixVal[1] += rsStruct.rgwssFixVal[1].empty() ? L'x' : rsStruct.rgwssFixVal[1].front() + 1; + rsStruct.rgptr8Val[0].resize(rsStruct.rgptr8Val[0].size() * 2); + rsStruct.rgptr8Val[1].resize(rsStruct.rgptr8Val[1].size() * 3); + for (size_t nIndex = 0; nIndex < rsStruct.rgptr8Val[0].size() / 2; nIndex++) + rsStruct.rgptr8Val[0][nIndex + rsStruct.rgptr8Val[0].size() / 2] = rsStruct.rgptr8Val[0][nIndex]; + for (size_t nIndex = 0; nIndex < rsStruct.rgptr8Val[1].size() / 3; nIndex++) + rsStruct.rgptr8Val[1][nIndex + rsStruct.rgptr8Val[1].size() / 3] = rsStruct.rgptr8Val[1][nIndex]; + rsStruct.rgptrssVal[0].resize(rsStruct.rgptrssVal[0].size() * 2); + rsStruct.rgptrssVal[1].resize(rsStruct.rgptrssVal[1].size() * 3); + for (size_t nIndex = 0; nIndex < rsStruct.rgptrssVal[0].size() / 2; nIndex++) + rsStruct.rgptrssVal[0][nIndex + rsStruct.rgptrssVal[0].size() / 2] = rsStruct.rgptrssVal[0][nIndex]; + for (size_t nIndex = 0; nIndex < rsStruct.rgptrssVal[1].size() / 3; nIndex++) + rsStruct.rgptrssVal[1][nIndex + rsStruct.rgptrssVal[1].size() / 3] = rsStruct.rgptrssVal[1][nIndex]; + for (size_t nIndex = 0; nIndex < rsStruct.rgptr8FixVal[0].size(); nIndex++) + rsStruct.rgptr8FixVal[0][nIndex] = static_cast(rsStruct.rgptr8FixVal[0][nIndex] << 1); + for (size_t nIndex = 0; nIndex < rsStruct.rgptr8FixVal[1].size(); nIndex++) + rsStruct.rgptr8FixVal[1][nIndex] = static_cast(rsStruct.rgptr8FixVal[1][nIndex] << 1); + for (size_t nIndex = 0; nIndex < rsStruct.rgptrssFixVal[0].size(); nIndex++) + rsStruct.rgptrssFixVal[0][nIndex] = rsStruct.rgptrssFixVal[0][nIndex] + rsStruct.rgptrssFixVal[0][nIndex]; + for (size_t nIndex = 0; nIndex < rsStruct.rgptrssFixVal[1].size(); nIndex++) + rsStruct.rgptrssFixVal[1][nIndex] = rsStruct.rgptrssFixVal[1][nIndex] + rsStruct.rgptrssFixVal[1][nIndex]; + rsStruct.rgseq8Val[0].resize(rsStruct.rgseq8Val[0].size() * 2); + rsStruct.rgseq8Val[1].resize(rsStruct.rgseq8Val[1].size() * 3); + for (size_t nIndex = 0; nIndex < rsStruct.rgseq8Val[0].size() / 2; nIndex++) + rsStruct.rgseq8Val[0][nIndex + rsStruct.rgseq8Val[0].size() / 2] = rsStruct.rgseq8Val[0][nIndex]; + for (size_t nIndex = 0; nIndex < rsStruct.rgseq8Val[1].size() / 3; nIndex++) + rsStruct.rgseq8Val[1][nIndex + rsStruct.rgseq8Val[1].size() / 3] = rsStruct.rgseq8Val[1][nIndex]; + rsStruct.rgseqssVal[0].resize(rsStruct.rgseqssVal[0].size() * 2); + rsStruct.rgseqssVal[1].resize(rsStruct.rgseqssVal[1].size() * 3); + for (size_t nIndex = 0; nIndex < rsStruct.rgseqssVal[0].size() / 2; nIndex++) + rsStruct.rgseqssVal[0][nIndex + rsStruct.rgseqssVal[0].size() / 2] = rsStruct.rgseqssVal[0][nIndex]; + for (size_t nIndex = 0; nIndex < rsStruct.rgseqssVal[1].size() / 3; nIndex++) + rsStruct.rgseqssVal[1][nIndex + rsStruct.rgseqssVal[1].size() / 3] = rsStruct.rgseqssVal[1][nIndex]; + for (size_t nIndex = 0; nIndex < rsStruct.rgseq8FixVal[0].size(); nIndex++) + rsStruct.rgseq8FixVal[0][nIndex] = static_cast(rsStruct.rgseq8FixVal[0][nIndex] << 1); + for (size_t nIndex = 0; nIndex < rsStruct.rgseq8FixVal[1].size(); nIndex++) + rsStruct.rgseq8FixVal[1][nIndex] = static_cast(rsStruct.rgseq8FixVal[1][nIndex] << 1); + for (size_t nIndex = 0; nIndex < rsStruct.rgseqssFixVal[0].size(); nIndex++) + rsStruct.rgseqssFixVal[0][nIndex] = rsStruct.rgseqssFixVal[0][nIndex] + rsStruct.rgseqssFixVal[0][nIndex]; + for (size_t nIndex = 0; nIndex < rsStruct.rgseqssFixVal[1].size(); nIndex++) + rsStruct.rgseqssFixVal[1][nIndex] = rsStruct.rgseqssFixVal[1][nIndex] + rsStruct.rgseqssFixVal[1][nIndex]; + // Incompatible serdes generation: BUG #398509 + // if (rsStruct.rgifcVal[0].get() && rsStruct.rgifcVal[0].id() == sdv::GetInterfaceId()) + //{ + // m_summer.set(rsStruct.rgifcVal[0].get()); + // rsStruct.rgifcVal[0] = static_cast(&m_summer); + //} + // else + // rsStruct.rgifcVal[0] = nullptr; + // if (rsStruct.rgifcVal[1].get() && rsStruct.rgifcVal[1].id() == sdv::GetInterfaceId()) + //{ + // m_summer.set(rsStruct.rgifcVal[1].get()); + // rsStruct.rgifcVal[1] = static_cast(&m_summer); + //} + // else + // rsStruct.rgifcVal[1] = nullptr; + // if (rsStruct.rgpMultiplyValue[0]) + //{ + // m_multiplier.set(rsStruct.rgpMultiplyValue[0]); + // rsStruct.rgpMultiplyValue[0] = static_cast(&m_multiplier); + //} + // else + // rsStruct.rgpMultiplyValue[0] = nullptr; + // if (rsStruct.rgpMultiplyValue[1]) + //{ + // m_multiplier.set(rsStruct.rgpMultiplyValue[1]); + // rsStruct.rgpMultiplyValue[1] = static_cast(&m_multiplier); + //} + // else + // rsStruct.rgpMultiplyValue[1] = nullptr; + rsStruct.rgsSubVal[0].i += 1; + rsStruct.rgsSubVal[1].i += 2; + // Incompatible serdes generation: BUG #398509 + // rsStruct.rgsIndVal[0].i += 10; + // rsStruct.rgsIndVal[1].i += 20; + // Unnamed struct with array is not yet supported due to incorret serdes code generation: BUG #398246 + // rsStruct.rgsUnnamedVal[0].i += 20; + // rsStruct.rgsUnnamedVal[1].i += 40; + + rsStruct.tbVal = !rsStruct.tbVal; + rsStruct.tsVal--; + rsStruct.tusVal++; + rsStruct.tlVal--; + rsStruct.tulVal++; + rsStruct.tllVal--; + rsStruct.tullVal++; + rsStruct.ti8Val--; + rsStruct.tui8Val++; + rsStruct.ti16Val--; + rsStruct.tui16Val++; + rsStruct.ti32Val--; + rsStruct.tui32Val++; + rsStruct.ti64Val--; + rsStruct.tui64Val++; + rsStruct.tcVal++; + rsStruct.tc16Val++; + rsStruct.tc32Val++; + rsStruct.twcVal++; + rsStruct.tfVal -= 1000.000f; + rsStruct.tdVal += 1000.000; + rsStruct.tldVal -= 1000.000; + rsStruct.tnVal++; + rsStruct.teHelloVal = rsStruct.teHelloVal++; + if (rsStruct.tidVal == sdv::GetInterfaceId()) + rsStruct.tidVal = sdv::GetInterfaceId(); + else + rsStruct.tidVal = 0; + rsStruct.tssVal += rsStruct.tssVal.empty() ? 'x' : rsStruct.tssVal.back() + 1; + rsStruct.tss8Val += rsStruct.tss8Val.empty() ? 'x' : rsStruct.tss8Val.back() + 1; + rsStruct.tss16Val += rsStruct.tss16Val.empty() ? u'x' : rsStruct.tss16Val.back() + 1; + rsStruct.tss32Val += rsStruct.tss32Val.empty() ? U'x' : rsStruct.tss32Val.back() + 1; + rsStruct.twssVal += rsStruct.twssVal.empty() ? L'x' : rsStruct.twssVal.back() + 1; + rsStruct.tssFixVal += rsStruct.tssFixVal.empty() ? 'x' : rsStruct.tssFixVal.back() + 1; + rsStruct.tss8FixVal += rsStruct.tss8FixVal.empty() ? 'x' : rsStruct.tss8FixVal.back() + 1; + rsStruct.tss16FixVal += rsStruct.tss16FixVal.empty() ? u'x' : rsStruct.tss16FixVal.back() + 1; + rsStruct.tss32FixVal += rsStruct.tss32FixVal.empty() ? U'x' : rsStruct.tss32FixVal.back() + 1; + rsStruct.twssFixVal += rsStruct.twssFixVal.empty() ? L'x' : rsStruct.twssFixVal.back() + 1; + // Using typedefs of template based types produced an error: BUG #398266 + // rsStruct.tptr8Val.resize(rsStruct.tptr8Val.size() * 2); + // for (size_t nIndex = 0; nIndex < rsStruct.tptr8Val.size() / 2; nIndex++) + // rsStruct.tptr8Val[nIndex + rsStruct.tptr8Val.size() / 2] = rsStruct.tptr8Val[nIndex]; + // rsStruct.ptrssVal.resize(rsStruct.tptrssVal.size() * 2); + // for (size_t nIndex = 0; nIndex < rsStruct.tptrssVal.size() / 2; nIndex++) + // rsStruct.tptrssVal[nIndex + rsStruct.tptrssVal.size() / 2] = rsStruct.tptrssVal[nIndex]; + // for (size_t nIndex = 0; nIndex < rsStruct.tptr8FixVal.size(); nIndex++) + // rsStruct.tptr8FixVal[nIndex] = static_cast(rsStruct.tptr8FixVal[nIndex] << 1); + // for (size_t nIndex = 0; nIndex < rsStruct.tptrssFixVal.size(); nIndex++) + // rsStruct.ptrssFixVal[nIndex] = rsStruct.tptrssFixVal[nIndex] + rsStruct.tptrssFixVal[nIndex]; + // rsStruct.tseq8Val.resize(rsStruct.tseq8Val.size() * 2); + // for (size_t nIndex = 0; nIndex < rsStruct.tseq8Val.size() / 2; nIndex++) + // rsStruct.tseq8Val[nIndex + rsStruct.tseq8Val.size() / 2] = rsStruct.tseq8Val[nIndex]; + // rsStruct.tseqssVal.resize(rsStruct.tseqssVal.size() * 2); + // for (size_t nIndex = 0; nIndex < rsStruct.tseqssVal.size() / 2; nIndex++) + // rsStruct.seqssVal[nIndex + rsStruct.tseqssVal.size() / 2] = rsStruct.tseqssVal[nIndex]; + // for (size_t nIndex = 0; nIndex < rsStruct.tseq8FixVal.size(); nIndex++) + // rsStruct.tseq8FixVal[nIndex] = static_cast(rsStruct.tseq8FixVal[nIndex] << 1); + // for (size_t nIndex = 0; nIndex < rsStruct.tseqssFixVal.size(); nIndex++) + // rsStruct.tseqssFixVal[nIndex] = rsStruct.tseqssFixVal[nIndex] + rsStruct.tseqssFixVal[nIndex]; + if (rsStruct.tifcVal.get() && rsStruct.tifcVal.id() == sdv::GetInterfaceId()) + { + m_summerTypedef.set(rsStruct.tifcVal.get()); + rsStruct.tifcVal = static_cast(&m_summerTypedef); + } + else + rsStruct.tifcVal = nullptr; + // Typedef of interfaces current not possible. BUG #399464 + // if (rsStruct.tpMultiplyValue) + //{ + // m_multiplierTypedef.set(rsStruct.tpMultiplyValue); + // rsStruct.tpMultiplyValue = static_cast(&m_multiplierTypedef); + //} + // else + // rsStruct.tpMultiplyValue = nullptr; + rsStruct.tsSubVal.i += 1; + rsStruct.tsIndVal.i += 10; + // Incompatible serdes generation: BUG #398509 + // rsStruct.tsUnnamedVal.i += 20; + // Unnamed unions (inline definition) are not supported yet: PBI #398209 + // union switch(int32) { case 0: int32 i; default: int32 j; } tuUnnamedVal; + // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 + // struct { int32 iAnonymous1; int32 iAnonymous2; }; + // union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; + } + + /** + * @brief Update the mega union. This will test marshalling and unmarshalling of the mega union. Overload of + * IMegaTest::ProcessMegaTypeBasedUnion. + */ + virtual void ProcessMegaTypeBasedUnion(/*inout*/ UMegaTypeBasedUnion& ruUnion) override + { + switch (ruUnion.get_switch()) + { + case 1: + ruUnion.bVal = !ruUnion.bVal; + break; + case 2: + ruUnion.sVal--; + break; + case 3: + ruUnion.usVal++; + break; + case 4: + ruUnion.lVal--; + break; + case 5: + ruUnion.ulVal++; + break; + case 6: + ruUnion.llVal--; + break; + case 7: + ruUnion.ullVal++; + break; + case 8: + ruUnion.i8Val--; + break; + case 9: + ruUnion.ui8Val++; + break; + case 10: + ruUnion.i16Val--; + break; + case 11: + ruUnion.ui16Val++; + break; + case 12: + ruUnion.i32Val--; + break; + case 13: + ruUnion.ui32Val++; + break; + case 14: + ruUnion.i64Val--; + break; + case 15: + ruUnion.ui64Val++; + break; + case 16: + ruUnion.cVal++; + break; + case 17: + ruUnion.c16Val++; + break; + case 18: + ruUnion.c32Val++; + break; + case 19: + ruUnion.wcVal++; + break; + case 20: + ruUnion.fVal -= 1000.000f; + break; + case 21: + ruUnion.dVal += 1000.000; + break; + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + // case 22: ruUnion.ldVal -= 1000.000; break; + case 23: + ruUnion.nVal++; + break; + case 24: + ruUnion.eHelloVal = ruUnion.eHelloVal++; + break; + case 25: + if (ruUnion.idVal == sdv::GetInterfaceId()) + ruUnion.idVal = sdv::GetInterfaceId(); + else + ruUnion.idVal = 0; + break; + case 26: + ruUnion.ssVal += ruUnion.ssVal.empty() ? 'x' : ruUnion.ssVal.back() + 1; + break; + case 27: + ruUnion.ss8Val += ruUnion.ss8Val.empty() ? 'x' : ruUnion.ss8Val.back() + 1; + break; + case 28: + ruUnion.ss16Val += ruUnion.ss16Val.empty() ? u'x' : ruUnion.ss16Val.back() + 1; + break; + case 29: + ruUnion.ss32Val += ruUnion.ss32Val.empty() ? U'x' : ruUnion.ss32Val.back() + 1; + break; + case 30: + ruUnion.wssVal += ruUnion.wssVal.empty() ? L'x' : ruUnion.wssVal.back() + 1; + break; + case 31: + ruUnion.ssFixVal += ruUnion.ssFixVal.empty() ? 'x' : ruUnion.ssFixVal.back() + 1; + break; + case 32: + ruUnion.ss8FixVal += ruUnion.ss8FixVal.empty() ? 'x' : ruUnion.ss8FixVal.back() + 1; + break; + case 33: + ruUnion.ss16FixVal += ruUnion.ss16FixVal.empty() ? u'x' : ruUnion.ss16FixVal.back() + 1; + break; + case 34: + ruUnion.ss32FixVal += ruUnion.ss32FixVal.empty() ? U'x' : ruUnion.ss32FixVal.back() + 1; + break; + case 35: + ruUnion.wssFixVal += ruUnion.wssFixVal.empty() ? L'x' : ruUnion.wssFixVal.back() + 1; + break; + case 36: + ruUnion.ptr8Val.resize(ruUnion.ptr8Val.size() * 2); + for (size_t nIndex = 0; nIndex < ruUnion.ptr8Val.size() / 2; nIndex++) + ruUnion.ptr8Val[nIndex + ruUnion.ptr8Val.size() / 2] = ruUnion.ptr8Val[nIndex]; + break; + case 37: + ruUnion.ptrssVal.resize(ruUnion.ptrssVal.size() * 2); + for (size_t nIndex = 0; nIndex < ruUnion.ptrssVal.size() / 2; nIndex++) + ruUnion.ptrssVal[nIndex + ruUnion.ptrssVal.size() / 2] = ruUnion.ptrssVal[nIndex]; + break; + case 38: + for (size_t nIndex = 0; nIndex < ruUnion.ptr8FixVal.size(); nIndex++) + ruUnion.ptr8FixVal[nIndex] = static_cast(ruUnion.ptr8FixVal[nIndex] << 1); + break; + case 39: + for (size_t nIndex = 0; nIndex < ruUnion.ptrssFixVal.size(); nIndex++) + ruUnion.ptrssFixVal[nIndex] = ruUnion.ptrssFixVal[nIndex] + ruUnion.ptrssFixVal[nIndex]; + break; + case 40: + ruUnion.seq8Val.resize(ruUnion.seq8Val.size() * 2); + for (size_t nIndex = 0; nIndex < ruUnion.seq8Val.size() / 2; nIndex++) + ruUnion.seq8Val[nIndex + ruUnion.seq8Val.size() / 2] = ruUnion.seq8Val[nIndex]; + break; + case 41: + ruUnion.seqssVal.resize(ruUnion.seqssVal.size() * 2); + for (size_t nIndex = 0; nIndex < ruUnion.seqssVal.size() / 2; nIndex++) + ruUnion.seqssVal[nIndex + ruUnion.seqssVal.size() / 2] = ruUnion.seqssVal[nIndex]; + break; + case 42: + for (size_t nIndex = 0; nIndex < ruUnion.seq8FixVal.size(); nIndex++) + ruUnion.seq8FixVal[nIndex] = static_cast(ruUnion.seq8FixVal[nIndex] << 1); + break; + case 43: + for (size_t nIndex = 0; nIndex < ruUnion.seqssFixVal.size(); nIndex++) + ruUnion.seqssFixVal[nIndex] = ruUnion.seqssFixVal[nIndex] + ruUnion.seqssFixVal[nIndex]; + break; + case 44: + if (ruUnion.ifcVal.get() && ruUnion.ifcVal.id() == sdv::GetInterfaceId()) + { + m_summer.set(ruUnion.ifcVal.get()); + ruUnion.ifcVal = static_cast(&m_summer); + } + else + ruUnion.ifcVal = nullptr; + break; + case 45: + if (ruUnion.pMultiplyValue) + { + m_multiplier.set(ruUnion.pMultiplyValue); + ruUnion.pMultiplyValue = static_cast(&m_multiplier); + } + else + ruUnion.pMultiplyValue = nullptr; + break; + case 46: + ruUnion.sSubVal.i += 1; + break; + // Incompatible serdes generation: BUG #398509 + // case 47: ruUnion.sIndVal.i += 10; break; + // Unnamed struct inside unions are not supported yet: PBI #398230 + // case 48: ruUnion.sUnnamedVal.i += 20; break; + // Unnamed unions are not supported yet: PBI #398209 + // case 49: union switch(int32) { case 0: int32 i; default: int32 j; } uUnnamedVal; + // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 + // case 50: struct { int32 iAnonymous1; int32 iAnonymous2; }; + // case 51: union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; + + case 201: + ruUnion.tbVal = !ruUnion.tbVal; + break; + case 202: + ruUnion.tsVal--; + break; + case 203: + ruUnion.tusVal++; + break; + case 204: + ruUnion.tlVal--; + break; + case 205: + ruUnion.tulVal++; + break; + case 206: + ruUnion.tllVal--; + break; + case 207: + ruUnion.tullVal++; + break; + case 208: + ruUnion.ti8Val--; + break; + case 209: + ruUnion.tui8Val++; + break; + case 210: + ruUnion.ti16Val--; + break; + case 211: + ruUnion.tui16Val++; + break; + case 212: + ruUnion.ti32Val--; + break; + case 213: + ruUnion.tui32Val++; + break; + case 214: + ruUnion.ti64Val--; + break; + case 215: + ruUnion.tui64Val++; + break; + case 216: + ruUnion.tcVal++; + break; + case 217: + ruUnion.tc16Val++; + break; + case 218: + ruUnion.tc32Val++; + break; + case 219: + ruUnion.twcVal++; + break; + case 220: + ruUnion.tfVal -= 1000.000f; + break; + case 221: + ruUnion.tdVal += 1000.000; + break; + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + // case 22: ruUnion.tldVal -= 1000.000; break; + case 223: + ruUnion.tnVal++; + break; + case 224: + ruUnion.teHelloVal = ruUnion.teHelloVal++; + break; + case 225: + if (ruUnion.tidVal == sdv::GetInterfaceId()) + ruUnion.tidVal = sdv::GetInterfaceId(); + else + ruUnion.tidVal = 0; + break; + case 226: + ruUnion.tssVal += ruUnion.tssVal.empty() ? 'x' : ruUnion.tssVal.back() + 1; + break; + case 227: + ruUnion.tss8Val += ruUnion.tss8Val.empty() ? 'x' : ruUnion.tss8Val.back() + 1; + break; + case 228: + ruUnion.tss16Val += ruUnion.tss16Val.empty() ? u'x' : ruUnion.tss16Val.back() + 1; + break; + case 229: + ruUnion.tss32Val += ruUnion.tss32Val.empty() ? U'x' : ruUnion.tss32Val.back() + 1; + break; + case 230: + ruUnion.twssVal += ruUnion.twssVal.empty() ? L'x' : ruUnion.twssVal.back() + 1; + break; + case 231: + ruUnion.tssFixVal += ruUnion.tssFixVal.empty() ? 'x' : ruUnion.tssFixVal.back() + 1; + break; + case 232: + ruUnion.tss8FixVal += ruUnion.tss8FixVal.empty() ? 'x' : ruUnion.tss8FixVal.back() + 1; + break; + case 233: + ruUnion.tss16FixVal += ruUnion.tss16FixVal.empty() ? u'x' : ruUnion.tss16FixVal.back() + 1; + break; + case 234: + ruUnion.tss32FixVal += ruUnion.tss32FixVal.empty() ? U'x' : ruUnion.tss32FixVal.back() + 1; + break; + case 235: + ruUnion.twssFixVal += ruUnion.twssFixVal.empty() ? L'x' : ruUnion.twssFixVal.back() + 1; + break; + // Using typedefs of template based types produced an error: BUG #398266 + // case 236: + // ruUnion.tptr8Val.resize(ruUnion.tptr8Val.size() * 2); + // for (size_t nIndex = 0; nIndex < ruUnion.tptr8Val.size() / 2; nIndex++) + // ruUnion.tptr8Val[nIndex + ruUnion.tptr8Val.size() / 2] = ruUnion.tptr8Val[nIndex]; + // break; + // case 237: + // ruUnion.tptrssVal.resize(ruUnion.tptrssVal.size() * 2); + // for (size_t nIndex = 0; nIndex < ruUnion.tptrssVal.size() / 2; nIndex++) + // ruUnion.tptrssVal[nIndex + ruUnion.tptrssVal.size() / 2] = ruUnion.tptrssVal[nIndex]; + // break; + // case 238: + // for (size_t nIndex = 0; nIndex < ruUnion.tptr8FixVal.size(); nIndex++) + // ruUnion.tptr8FixVal[nIndex] = static_cast(ruUnion.tptr8FixVal[nIndex] << 1); + // break; + // case 239: + // for (size_t nIndex = 0; nIndex < ruUnion.tptrssFixVal.size(); nIndex++) + // ruUnion.tptrssFixVal[nIndex] = ruUnion.tptrssFixVal[nIndex] + ruUnion.tptrssFixVal[nIndex]; + // break; + // case 240: + // ruUnion.tseq8Val.resize(ruUnion.tseq8Val.size() * 2); + // for (size_t nIndex = 0; nIndex < ruUnion.tseq8Val.size() / 2; nIndex++) + // ruUnion.tseq8Val[nIndex + ruUnion.tseq8Val.size() / 2] = ruUnion.tseq8Val[nIndex]; + // break; + // case 241: + // ruUnion.tseqssVal.resize(ruUnion.tseqssVal.size() * 2); + // for (size_t nIndex = 0; nIndex < ruUnion.tseqssVal.size() / 2; nIndex++) + // ruUnion.tseqssVal[nIndex + ruUnion.tseqssVal.size() / 2] = ruUnion.tseqssVal[nIndex]; + // break; + // case 242: + // for (size_t nIndex = 0; nIndex < ruUnion.tseq8FixVal.size(); nIndex++) + // ruUnion.tseq8FixVal[nIndex] = static_cast(ruUnion.tseq8FixVal[nIndex] << 1); + // break; + // case 243: + // for (size_t nIndex = 0; nIndex < ruUnion.tseqssFixVal.size(); nIndex++) + // ruUnion.tseqssFixVal[nIndex] = ruUnion.tseqssFixVal[nIndex] + ruUnion.tseqssFixVal[nIndex]; + // break; + case 244: + if (ruUnion.tifcVal.get() && ruUnion.tifcVal.id() == sdv::GetInterfaceId()) + { + m_summerTypedef.set(ruUnion.tifcVal.get()); + ruUnion.tifcVal = static_cast(&m_summerTypedef); + } + else + ruUnion.tifcVal = nullptr; + break; + // Typedef of interfaces current not possible. BUG #399464 + // case 245: + // if (ruUnion.tpMultiplyValue) + // { + // m_multiplierTypedef.set(ruUnion.tpMultiplyValue); + // ruUnion.tpMultiplyValue = static_cast(&m_multiplierTypedef); + // } + // else + // ruUnion.tpMultiplyValue = nullptr; + // break; + case 246: + ruUnion.tsSubVal.i += 1; + break; + // Incompatible serdes generation: BUG #398509 + case 247: + ruUnion.tsIndVal.i += 10; + break; + // Unnamed struct inside unions are not supported yet: PBI #398230 + // case 248: ruUnion.tsUnnamedVal.i += 20; break; + // Unnamed unions are not supported yet: PBI #398209 + // case 249: union switch(int32) { case 0: int32 i; default: int32 j; } uUnnamedVal; + // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 + // case 250: struct { int32 iAnonymous1; int32 iAnonymous2; }; + // case 251: union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; + default: + break; + } + } + + /** + * @brief Update the mega union. This will test marshalling and unmarshalling of the mega union. Overload of + * IMegaTest::ProcessMegaTypeBasedUnion. + */ + virtual void ProcessMegaVarBasedUnion(/*inout*/ SMegaVarBasedUnion& rsUnion) override + { + switch (rsUnion.get_switch()) + { + case 1: + rsUnion.uVal.bVal = !rsUnion.uVal.bVal; + break; + case 2: + rsUnion.uVal.sVal--; + break; + case 3: + rsUnion.uVal.usVal++; + break; + case 4: + rsUnion.uVal.lVal--; + break; + case 5: + rsUnion.uVal.ulVal++; + break; + case 6: + rsUnion.uVal.llVal--; + break; + case 7: + rsUnion.uVal.ullVal++; + break; + case 8: + rsUnion.uVal.i8Val--; + break; + case 9: + rsUnion.uVal.ui8Val++; + break; + case 10: + rsUnion.uVal.i16Val--; + break; + case 11: + rsUnion.uVal.ui16Val++; + break; + case 12: + rsUnion.uVal.i32Val--; + break; + case 13: + rsUnion.uVal.ui32Val++; + break; + case 14: + rsUnion.uVal.i64Val--; + break; + case 15: + rsUnion.uVal.ui64Val++; + break; + case 16: + rsUnion.uVal.cVal++; + break; + case 17: + rsUnion.uVal.c16Val++; + break; + case 18: + rsUnion.uVal.c32Val++; + break; + case 19: + rsUnion.uVal.wcVal++; + break; + case 20: + rsUnion.uVal.fVal -= 1000.000f; + break; + case 21: + rsUnion.uVal.dVal += 1000.000; + break; + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + // case 22: rsUnion.uVal.ldVal -= 1000.000; break; + case 23: + rsUnion.uVal.nVal++; + break; + case 24: + rsUnion.uVal.eHelloVal = rsUnion.uVal.eHelloVal++; + break; + case 25: + if (rsUnion.uVal.idVal == sdv::GetInterfaceId()) + rsUnion.uVal.idVal = sdv::GetInterfaceId(); + else + rsUnion.uVal.idVal = 0; + break; + case 26: + rsUnion.uVal.ssVal += rsUnion.uVal.ssVal.empty() ? 'x' : rsUnion.uVal.ssVal.back() + 1; + break; + case 27: + rsUnion.uVal.ss8Val += rsUnion.uVal.ss8Val.empty() ? 'x' : rsUnion.uVal.ss8Val.back() + 1; + break; + case 28: + rsUnion.uVal.ss16Val += rsUnion.uVal.ss16Val.empty() ? u'x' : rsUnion.uVal.ss16Val.back() + 1; + break; + case 29: + rsUnion.uVal.ss32Val += rsUnion.uVal.ss32Val.empty() ? U'x' : rsUnion.uVal.ss32Val.back() + 1; + break; + case 30: + rsUnion.uVal.wssVal += rsUnion.uVal.wssVal.empty() ? L'x' : rsUnion.uVal.wssVal.back() + 1; + break; + case 31: + rsUnion.uVal.ssFixVal += rsUnion.uVal.ssFixVal.empty() ? 'x' : rsUnion.uVal.ssFixVal.back() + 1; + break; + case 32: + rsUnion.uVal.ss8FixVal += rsUnion.uVal.ss8FixVal.empty() ? 'x' : rsUnion.uVal.ss8FixVal.back() + 1; + break; + case 33: + rsUnion.uVal.ss16FixVal += rsUnion.uVal.ss16FixVal.empty() ? u'x' : rsUnion.uVal.ss16FixVal.back() + 1; + break; + case 34: + rsUnion.uVal.ss32FixVal += rsUnion.uVal.ss32FixVal.empty() ? U'x' : rsUnion.uVal.ss32FixVal.back() + 1; + break; + case 35: + rsUnion.uVal.wssFixVal += rsUnion.uVal.wssFixVal.empty() ? L'x' : rsUnion.uVal.wssFixVal.back() + 1; + break; + case 36: + rsUnion.uVal.ptr8Val.resize(rsUnion.uVal.ptr8Val.size() * 2); + for (size_t nIndex = 0; nIndex < rsUnion.uVal.ptr8Val.size() / 2; nIndex++) + rsUnion.uVal.ptr8Val[nIndex + rsUnion.uVal.ptr8Val.size() / 2] = rsUnion.uVal.ptr8Val[nIndex]; + break; + case 37: + rsUnion.uVal.ptrssVal.resize(rsUnion.uVal.ptrssVal.size() * 2); + for (size_t nIndex = 0; nIndex < rsUnion.uVal.ptrssVal.size() / 2; nIndex++) + rsUnion.uVal.ptrssVal[nIndex + rsUnion.uVal.ptrssVal.size() / 2] = rsUnion.uVal.ptrssVal[nIndex]; + break; + case 38: + for (size_t nIndex = 0; nIndex < rsUnion.uVal.ptr8FixVal.size(); nIndex++) + rsUnion.uVal.ptr8FixVal[nIndex] = static_cast(rsUnion.uVal.ptr8FixVal[nIndex] << 1); + break; + case 39: + for (size_t nIndex = 0; nIndex < rsUnion.uVal.ptrssFixVal.size(); nIndex++) + rsUnion.uVal.ptrssFixVal[nIndex] = rsUnion.uVal.ptrssFixVal[nIndex] + rsUnion.uVal.ptrssFixVal[nIndex]; + break; + case 40: + rsUnion.uVal.seq8Val.resize(rsUnion.uVal.seq8Val.size() * 2); + for (size_t nIndex = 0; nIndex < rsUnion.uVal.seq8Val.size() / 2; nIndex++) + rsUnion.uVal.seq8Val[nIndex + rsUnion.uVal.seq8Val.size() / 2] = rsUnion.uVal.seq8Val[nIndex]; + break; + case 41: + rsUnion.uVal.seqssVal.resize(rsUnion.uVal.seqssVal.size() * 2); + for (size_t nIndex = 0; nIndex < rsUnion.uVal.seqssVal.size() / 2; nIndex++) + rsUnion.uVal.seqssVal[nIndex + rsUnion.uVal.seqssVal.size() / 2] = rsUnion.uVal.seqssVal[nIndex]; + break; + case 42: + for (size_t nIndex = 0; nIndex < rsUnion.uVal.seq8FixVal.size(); nIndex++) + rsUnion.uVal.seq8FixVal[nIndex] = static_cast(rsUnion.uVal.seq8FixVal[nIndex] << 1); + break; + case 43: + for (size_t nIndex = 0; nIndex < rsUnion.uVal.seqssFixVal.size(); nIndex++) + rsUnion.uVal.seqssFixVal[nIndex] = rsUnion.uVal.seqssFixVal[nIndex] + rsUnion.uVal.seqssFixVal[nIndex]; + break; + case 44: + if (rsUnion.uVal.ifcVal.get() && rsUnion.uVal.ifcVal.id() == sdv::GetInterfaceId()) + { + m_summer.set(rsUnion.uVal.ifcVal.get()); + rsUnion.uVal.ifcVal = static_cast(&m_summer); + } + else + rsUnion.uVal.ifcVal = nullptr; + break; + case 45: + if (rsUnion.uVal.pMultiplyValue) + { + m_multiplier.set(rsUnion.uVal.pMultiplyValue); + rsUnion.uVal.pMultiplyValue = static_cast(&m_multiplier); + } + else + rsUnion.uVal.pMultiplyValue = nullptr; + break; + case 46: + rsUnion.uVal.sSubVal.i += 1; + break; + // Incompatible serdes generation: BUG #398509 + // case 47: rsUnion.uVal.sIndVal.i += 10; break; + // Unnamed struct inside unions are not supported yet: PBI #398230 + // case 48: rsUnion.uVal.sUnnamedVal.i += 20; break; + // Unnamed unions are not supported yet: PBI #398209 + // case 49: union switch(int32) { case 0: int32 i; default: int32 j; } uUnnamedVal; + // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 + // case 50: struct { int32 iAnonymous1; int32 iAnonymous2; }; + // case 51: union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; + + case 201: + rsUnion.uVal.tbVal = !rsUnion.uVal.tbVal; + break; + case 202: + rsUnion.uVal.tsVal--; + break; + case 203: + rsUnion.uVal.tusVal++; + break; + case 204: + rsUnion.uVal.tlVal--; + break; + case 205: + rsUnion.uVal.tulVal++; + break; + case 206: + rsUnion.uVal.tllVal--; + break; + case 207: + rsUnion.uVal.tullVal++; + break; + case 208: + rsUnion.uVal.ti8Val--; + break; + case 209: + rsUnion.uVal.tui8Val++; + break; + case 210: + rsUnion.uVal.ti16Val--; + break; + case 211: + rsUnion.uVal.tui16Val++; + break; + case 212: + rsUnion.uVal.ti32Val--; + break; + case 213: + rsUnion.uVal.tui32Val++; + break; + case 214: + rsUnion.uVal.ti64Val--; + break; + case 215: + rsUnion.uVal.tui64Val++; + break; + case 216: + rsUnion.uVal.tcVal++; + break; + case 217: + rsUnion.uVal.tc16Val++; + break; + case 218: + rsUnion.uVal.tc32Val++; + break; + case 219: + rsUnion.uVal.twcVal++; + break; + case 220: + rsUnion.uVal.tfVal -= 1000.000f; + break; + case 221: + rsUnion.uVal.tdVal += 1000.000; + break; + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + // case 22: rsUnion.uVal.tldVal -= 1000.000; break; + case 223: + rsUnion.uVal.tnVal++; + break; + case 224: + rsUnion.uVal.teHelloVal = rsUnion.uVal.teHelloVal++; + break; + case 225: + if (rsUnion.uVal.tidVal == sdv::GetInterfaceId()) + rsUnion.uVal.tidVal = sdv::GetInterfaceId(); + else + rsUnion.uVal.tidVal = 0; + break; + case 226: + rsUnion.uVal.tssVal += rsUnion.uVal.tssVal.empty() ? 'x' : rsUnion.uVal.tssVal.back() + 1; + break; + case 227: + rsUnion.uVal.tss8Val += rsUnion.uVal.tss8Val.empty() ? 'x' : rsUnion.uVal.tss8Val.back() + 1; + break; + case 228: + rsUnion.uVal.tss16Val += rsUnion.uVal.tss16Val.empty() ? u'x' : rsUnion.uVal.tss16Val.back() + 1; + break; + case 229: + rsUnion.uVal.tss32Val += rsUnion.uVal.tss32Val.empty() ? U'x' : rsUnion.uVal.tss32Val.back() + 1; + break; + case 230: + rsUnion.uVal.twssVal += rsUnion.uVal.twssVal.empty() ? L'x' : rsUnion.uVal.twssVal.back() + 1; + break; + case 231: + rsUnion.uVal.tssFixVal += rsUnion.uVal.tssFixVal.empty() ? 'x' : rsUnion.uVal.tssFixVal.back() + 1; + break; + case 232: + rsUnion.uVal.tss8FixVal += rsUnion.uVal.tss8FixVal.empty() ? 'x' : rsUnion.uVal.tss8FixVal.back() + 1; + break; + case 233: + rsUnion.uVal.tss16FixVal += rsUnion.uVal.tss16FixVal.empty() ? u'x' : rsUnion.uVal.tss16FixVal.back() + 1; + break; + case 234: + rsUnion.uVal.tss32FixVal += rsUnion.uVal.tss32FixVal.empty() ? U'x' : rsUnion.uVal.tss32FixVal.back() + 1; + break; + case 235: + rsUnion.uVal.twssFixVal += rsUnion.uVal.twssFixVal.empty() ? L'x' : rsUnion.uVal.twssFixVal.back() + 1; + break; + // Using typedefs of template based types produced an error: BUG #398266 + // case 236: + // rsUnion.uVal.tptr8Val.resize(rsUnion.uVal.tptr8Val.size() * 2); + // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tptr8Val.size() / 2; nIndex++) + // rsUnion.uVal.tptr8Val[nIndex + rsUnion.uVal.tptr8Val.size() / 2] = rsUnion.uVal.tptr8Val[nIndex]; + // break; + // case 237: + // rsUnion.uVal.tptrssVal.resize(rsUnion.uVal.tptrssVal.size() * 2); + // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tptrssVal.size() / 2; nIndex++) + // rsUnion.uVal.tptrssVal[nIndex + rsUnion.uVal.tptrssVal.size() / 2] = rsUnion.uVal.tptrssVal[nIndex]; + // break; + // case 238: + // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tptr8FixVal.size(); nIndex++) + // rsUnion.uVal.tptr8FixVal[nIndex] = static_cast(rsUnion.uVal.tptr8FixVal[nIndex] << 1); + // break; + // case 239: + // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tptrssFixVal.size(); nIndex++) + // rsUnion.uVal.tptrssFixVal[nIndex] = rsUnion.uVal.tptrssFixVal[nIndex] + rsUnion.uVal.tptrssFixVal[nIndex]; + // break; + // case 240: + // rsUnion.uVal.tseq8Val.resize(rsUnion.uVal.tseq8Val.size() * 2); + // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tseq8Val.size() / 2; nIndex++) + // rsUnion.uVal.tseq8Val[nIndex + rsUnion.uVal.tseq8Val.size() / 2] = rsUnion.uVal.tseq8Val[nIndex]; + // break; + // case 241: + // rsUnion.uVal.tseqssVal.resize(rsUnion.uVal.tseqssVal.size() * 2); + // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tseqssVal.size() / 2; nIndex++) + // rsUnion.uVal.tseqssVal[nIndex + rsUnion.uVal.tseqssVal.size() / 2] = rsUnion.uVal.tseqssVal[nIndex]; + // break; + // case 242: + // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tseq8FixVal.size(); nIndex++) + // rsUnion.uVal.tseq8FixVal[nIndex] = static_cast(rsUnion.uVal.tseq8FixVal[nIndex] << 1); + // break; + // case 243: + // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tseqssFixVal.size(); nIndex++) + // rsUnion.uVal.tseqssFixVal[nIndex] = rsUnion.uVal.tseqssFixVal[nIndex] + rsUnion.uVal.tseqssFixVal[nIndex]; + // break; + case 244: + if (rsUnion.uVal.tifcVal.get() && rsUnion.uVal.tifcVal.id() == sdv::GetInterfaceId()) + { + m_summerTypedef.set(rsUnion.uVal.tifcVal.get()); + rsUnion.uVal.tifcVal = static_cast(&m_summerTypedef); + } + else + rsUnion.uVal.tifcVal = nullptr; + break; + // Typedef of interfaces current not possible. BUG #399464 + // case 245: + // if (rsUnion.uVal.tpMultiplyValue) + // { + // m_multiplierTypedef.set(rsUnion.uVal.tpMultiplyValue); + // rsUnion.uVal.tpMultiplyValue = static_cast(&m_multiplierTypedef); + // } + // else + // rsUnion.uVal.tpMultiplyValue = nullptr; + // break; + case 246: + rsUnion.uVal.tsSubVal.i += 1; + break; + // Incompatible serdes generation: BUG #398509 + case 247: + rsUnion.uVal.tsIndVal.i += 10; + break; + // Unnamed struct inside unions are not supported yet: PBI #398230 + // case 248: rsUnion.uVal.tsUnnamedVal.i += 20; break; + // Unnamed unions are not supported yet: PBI #398209 + // case 249: union switch(int32) { case 0: int32 i; default: int32 j; } uUnnamedVal; + // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 + // case 250: struct { int32 iAnonymous1; int32 iAnonymous2; }; + // case 251: union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; + default: + break; + } + } + + ISayHello* m_pHello = nullptr; + + struct SInternalMultiply : IMultiplyValue + { + void set(IMultiplyValue* p) + { + pMultiplyValue = p; + } + size_t Multiply(size_t n) + { + if (!pMultiplyValue) + return 0; + return pMultiplyValue->Multiply(n) * pMultiplyValue->Multiply(n); + } + IMultiplyValue* pMultiplyValue = nullptr; + }; + SInternalMultiply m_multiplier; + SInternalMultiply m_multiplierTypedef; + + struct SInternalAdd : IAddValue + { + void set(IMultiplyValue* p) + { + pMultiplyValue = p; + } + size_t Add(size_t n) + { + if (!pMultiplyValue) + return 0; + return pMultiplyValue->Multiply(n) + pMultiplyValue->Multiply(n); + } + IMultiplyValue* pMultiplyValue = nullptr; + }; + SInternalAdd m_summer; + SInternalAdd m_summerTypedef; +}; + +enum class ELocalIpcBackend +{ + sharedmem, + sockets, + tunnel +}; + +inline void LoadIPCModules(CCommunicationControl& rControl, ELocalIpcBackend eLocalBackend) +{ + sdv::u8string ssModule, ssObject; + switch (eLocalBackend) + { + case ELocalIpcBackend::sockets: +#if defined(_WIN32) + ssModule = "uds_win_sockets.sdv"; + ssObject = "WinSocketsChannelControl"; +#elif defined(__unix__) + ssModule = "uds_unix_sockets.sdv"; + ssObject = "UnixSocketsChannelControl"; +#else + #error Unsupported platform for IPC communication unit tests +#endif + break; + case ELocalIpcBackend::tunnel: +#if defined(_WIN32) + ssModule = "uds_win_tunnel.sdv"; + ssObject = "WinTunnelChannelControl"; +#elif defined(__unix__) + ssModule = "uds_unix_tunnel.sdv"; + ssObject = "UnixTunnelChannelControl"; +#else + #error Unsupported platform for IPC communication unit tests +#endif + break; + case ELocalIpcBackend::sharedmem: + default: + ssModule = "ipc_shared_mem.sdv"; + ssObject = "DefaultSharedMemory"; + break; + } + + // Load the IPC modules + sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); + ASSERT_NE(pModuleControl, nullptr); + EXPECT_NE(pModuleControl->Load(ssModule), 0u); + EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0u); + EXPECT_NE(pModuleControl->Load("UnitTest_IPC_Communication_ps.sdv"), 0u); + + // Register communication control + sdv::core::IRegisterForeignObject* pRegisterObject = sdv::core::GetObject("RepositoryService"); + + ASSERT_NE(pRegisterObject, nullptr); + + EXPECT_NE(pRegisterObject->RegisterObject(&rControl, "CommunicationControl"), 0u); + + // Create channel control + sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); + + ASSERT_NE(pRepositoryControl, nullptr); + + if (eLocalBackend == ELocalIpcBackend::tunnel) + { + // CRITICAL: channel name MUST match CreateEndpoint("DEFAULT_CHANNEL") + EXPECT_NE(pRepositoryControl->CreateObject(ssObject, "DEFAULT_CHANNEL", ""), 0u); + } + else + { + // sockets + sharedmem don't require explicit channel naming + EXPECT_NE(pRepositoryControl->CreateObject(ssObject, "", ""), 0u); + } +} diff --git a/tests/unit_tests/ipc_com/ipc_com.cpp b/tests/unit_tests/ipc_com/ipc_com_sharedmem.cpp similarity index 73% rename from tests/unit_tests/ipc_com/ipc_com.cpp rename to tests/unit_tests/ipc_com/ipc_com_sharedmem.cpp index 8c2e643..1f5aaa5 100644 --- a/tests/unit_tests/ipc_com/ipc_com.cpp +++ b/tests/unit_tests/ipc_com/ipc_com_sharedmem.cpp @@ -11,934 +11,18 @@ * Erik Verhoeven - initial API and implementation ********************************************************************************/ -#include "../../include/gtest_custom.h" -#include "../../../sdv_services/ipc_com/com_ctrl.h" -#include "../../../sdv_services/ipc_com/com_channel.h" -#include "../../../sdv_services/ipc_com/marshall_object.h" -#include -#include -#include -#include "generated/test_ifc.h" +#include "include.h" -EHello& operator++(EHello& reHello, int) -{ - switch (reHello) - { - case EHello::hi: reHello = EHello::huhu; break; - case EHello::huhu: reHello = EHello::hello; break; - case EHello::hello: reHello = EHello::hallo; break; - case EHello::hallo: reHello = EHello::servus; break; - case EHello::servus: reHello = EHello::gruessgott; break; - case EHello::gruessgott: reHello = EHello::hi; break; - default: break; - } - return reHello; -} - -EHello& operator--(EHello& reHello, int) -{ - switch (reHello) - { - case EHello::hi: reHello = EHello::gruessgott; break; - case EHello::huhu: reHello = EHello::hi; break; - case EHello::hello: reHello = EHello::huhu; break; - case EHello::hallo: reHello = EHello::hello; break; - case EHello::servus: reHello = EHello::hallo; break; - case EHello::gruessgott: reHello = EHello::servus; break; - default: break; - } - return reHello; -} - -/** - * - Instantiate service - * - Assign server endpoint - * - Assign server endpoint + assign client endpoint - * - Create channel endpoint - * - Create channel endpoint + connect channel endpoint - * - Create proxy/stub connection - * - Reconnect - * - Multiple connections - * - Get other interface - * - Multiple access interface - * - Random multiple access interface - * - Random multiple access interface with reconnect - */ - -class CInterfaceTest : public ISayHello, public IRequestHello, public IRegisterHelloCallback, public IMegaTest, public sdv::IInterfaceAccess -{ -public: - BEGIN_SDV_INTERFACE_MAP() - SDV_INTERFACE_ENTRY(ISayHello) - SDV_INTERFACE_ENTRY(IRequestHello) - SDV_INTERFACE_ENTRY(IRegisterHelloCallback) - SDV_INTERFACE_ENTRY(IMegaTest) - SDV_INTERFACE_ENTRY(sdv::IInterfaceAccess) - END_SDV_INTERFACE_MAP() - - /** - * @brief Say hello! - * @return The greeting text. - */ - virtual sdv::string Hello() override - { - return "Hello"; - } - - /** - * @brief Request the hello interface. - * @return pHello The hello interface. - */ - virtual ISayHello* Request() - { - return this; - } - - /** - * @brief Register the callback with the hello interface. - * @param[in] pHello The hello interface. - */ - virtual void Register(/*in*/ ISayHello* pHello) - { - m_pHello = pHello; - } - - /** - * @brief Update the mega struct. This will test marshalling and unmarshalling of the mega struct. Overload of - * IMegaTest::ProcessMegaStruct. - * @param[in] rsStruct Reference to the mega struct. - */ - virtual void ProcessMegaStruct(/*inout*/ SMegaStruct& rsStruct) override - { - rsStruct.bVal = !rsStruct.bVal; - rsStruct.sVal--; - rsStruct.usVal++; - rsStruct.lVal--; - rsStruct.ulVal++; - rsStruct.llVal--; - rsStruct.ullVal++; - rsStruct.i8Val--; - rsStruct.ui8Val++; - rsStruct.i16Val--; - rsStruct.ui16Val++; - rsStruct.i32Val--; - rsStruct.ui32Val++; - rsStruct.i64Val--; - rsStruct.ui64Val++; - rsStruct.cVal++; - rsStruct.c16Val++; - rsStruct.c32Val++; - rsStruct.wcVal++; - rsStruct.fVal -= 1000.000f; - rsStruct.dVal += 1000.000; - rsStruct.ldVal -= 1000.000; - rsStruct.nVal++; - rsStruct.eHelloVal = rsStruct.eHelloVal++; - if (rsStruct.idVal == sdv::GetInterfaceId()) - rsStruct.idVal = sdv::GetInterfaceId(); - else - rsStruct.idVal = 0; - rsStruct.ssVal += rsStruct.ssVal.empty() ? 'x' : rsStruct.ssVal.back() + 1; - rsStruct.ss8Val += rsStruct.ss8Val.empty() ? 'x' : rsStruct.ss8Val.back() + 1; - rsStruct.ss16Val += rsStruct.ss16Val.empty() ? u'x' : rsStruct.ss16Val.back() + 1; - rsStruct.ss32Val += rsStruct.ss32Val.empty() ? U'x' : rsStruct.ss32Val.back() + 1; - rsStruct.wssVal += rsStruct.wssVal.empty() ? L'x' : rsStruct.wssVal.back() + 1; - rsStruct.ssFixVal += rsStruct.ssFixVal.empty() ? 'x' : rsStruct.ssFixVal.back() + 1; - rsStruct.ss8FixVal += rsStruct.ss8FixVal.empty() ? 'x' : rsStruct.ss8FixVal.back() + 1; - rsStruct.ss16FixVal += rsStruct.ss16FixVal.empty() ? u'x' : rsStruct.ss16FixVal.back() + 1; - rsStruct.ss32FixVal += rsStruct.ss32FixVal.empty() ? U'x' : rsStruct.ss32FixVal.back() + 1; - rsStruct.wssFixVal += rsStruct.wssFixVal.empty() ? L'x' : rsStruct.wssFixVal.back() + 1; - rsStruct.ptr8Val.resize(rsStruct.ptr8Val.size() * 2); - for (size_t nIndex = 0; nIndex < rsStruct.ptr8Val.size() / 2; nIndex++) - rsStruct.ptr8Val[nIndex + rsStruct.ptr8Val.size() / 2] = rsStruct.ptr8Val[nIndex]; - rsStruct.ptrssVal.resize(rsStruct.ptrssVal.size() * 2); - for (size_t nIndex = 0; nIndex < rsStruct.ptrssVal.size() / 2; nIndex++) - rsStruct.ptrssVal[nIndex + rsStruct.ptrssVal.size() / 2] = rsStruct.ptrssVal[nIndex]; - for (size_t nIndex = 0; nIndex < rsStruct.ptr8FixVal.size(); nIndex++) - rsStruct.ptr8FixVal[nIndex] = static_cast(rsStruct.ptr8FixVal[nIndex] << 1); - for (size_t nIndex = 0; nIndex < rsStruct.ptrssFixVal.size(); nIndex++) - rsStruct.ptrssFixVal[nIndex] = rsStruct.ptrssFixVal[nIndex] + rsStruct.ptrssFixVal[nIndex]; - rsStruct.seq8Val.resize(rsStruct.seq8Val.size() * 2); - for (size_t nIndex = 0; nIndex < rsStruct.seq8Val.size() / 2; nIndex++) - rsStruct.seq8Val[nIndex + rsStruct.seq8Val.size() / 2] = rsStruct.seq8Val[nIndex]; - rsStruct.seqssVal.resize(rsStruct.seqssVal.size() * 2); - for (size_t nIndex = 0; nIndex < rsStruct.seqssVal.size() / 2; nIndex++) - rsStruct.seqssVal[nIndex + rsStruct.seqssVal.size() / 2] = rsStruct.seqssVal[nIndex]; - for (size_t nIndex = 0; nIndex < rsStruct.seq8FixVal.size(); nIndex++) - rsStruct.seq8FixVal[nIndex] = static_cast(rsStruct.seq8FixVal[nIndex] << 1); - for (size_t nIndex = 0; nIndex < rsStruct.seqssFixVal.size(); nIndex++) - rsStruct.seqssFixVal[nIndex] = rsStruct.seqssFixVal[nIndex] + rsStruct.seqssFixVal[nIndex]; - if (rsStruct.ifcVal.get() && rsStruct.ifcVal.id() == sdv::GetInterfaceId()) - { - m_summer.set(rsStruct.ifcVal.get()); - rsStruct.ifcVal = static_cast(&m_summer); - } - else - rsStruct.ifcVal = nullptr; - if (rsStruct.pMultiplyValue) - { - m_multiplier.set(rsStruct.pMultiplyValue); - rsStruct.pMultiplyValue = static_cast(&m_multiplier); - } - else - rsStruct.pMultiplyValue = nullptr; - rsStruct.sSubVal.i += 1; - rsStruct.sIndVal.i += 10; - //rsStruct.sUnnamedVal.i += 20; // 12.04.2024 EVE Unnamed structures are not supported by IDL compiler. - // Unnamed unions (inline definition) are not supported yet: PBI #398209 - //union switch(int32) { case 0: int32 i; default: int32 j; } uUnnamedVal; - // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 - //struct { int32 iAnonymous1; int32 iAnonymous2; }; - //union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; - - rsStruct.rgbVal[0] = !rsStruct.rgbVal[0]; - rsStruct.rgbVal[1] = !rsStruct.rgbVal[1]; - rsStruct.rgsVal[0]--; - rsStruct.rgsVal[1]++; - rsStruct.rgusVal[0]++; - rsStruct.rgusVal[1] += 2; - rsStruct.rglVal[0]--; - rsStruct.rglVal[1]++; - rsStruct.rgulVal[0]++; - rsStruct.rgulVal[1] += 2; - rsStruct.rgllVal[0]--; - rsStruct.rgllVal[1]++; - rsStruct.rgullVal[0]++; - rsStruct.rgullVal[1] += 2; - rsStruct.rgi8Val[0]--; - rsStruct.rgi8Val[1]++; - rsStruct.rgui8Val[0]++; - rsStruct.rgui8Val[1] += 2; - rsStruct.rgi16Val[0]--; - rsStruct.rgi16Val[1]++; - rsStruct.rgui16Val[0]++; - rsStruct.rgui16Val[1] += 2; - rsStruct.rgi32Val[0]--; - rsStruct.rgi32Val[1]++; - rsStruct.rgui32Val[0]++; - rsStruct.rgui32Val[1] += 2; - rsStruct.rgi64Val[0]--; - rsStruct.rgi64Val[1]++; - rsStruct.rgui64Val[0]++; - rsStruct.rgui64Val[1] += 2; - rsStruct.rgcVal[0]++; - rsStruct.rgcVal[1] += 2; - rsStruct.rgc16Val[0]++; - rsStruct.rgc16Val[1] += 2; - rsStruct.rgc32Val[0]++; - rsStruct.rgc32Val[1] += 2; - rsStruct.rgwcVal[0]++; - rsStruct.rgwcVal[1] += 2; - rsStruct.rgfVal[0] -= 1000.000f; - rsStruct.rgfVal[1] += 2000.000f; - rsStruct.rgdVal[0] += 1000.000; - rsStruct.rgdVal[1] -= 2000.000; - rsStruct.rgldVal[0] -= 1000.000; - rsStruct.rgldVal[1] += 2000.000; - rsStruct.rgnVal[0]++; - rsStruct.rgnVal[1] += 2; - rsStruct.rgeHelloVal[0] = rsStruct.rgeHelloVal[0]++; - rsStruct.rgeHelloVal[1] = rsStruct.rgeHelloVal[1]--; - if (rsStruct.rgidVal[0] == sdv::GetInterfaceId()) - rsStruct.rgidVal[0] = sdv::GetInterfaceId(); - else - rsStruct.rgidVal[0] = 0; - if (rsStruct.rgidVal[1] == sdv::GetInterfaceId()) - rsStruct.rgidVal[1] = sdv::GetInterfaceId(); - else - rsStruct.rgidVal[1] = 0; - rsStruct.rgssVal[0] += rsStruct.rgssVal[0].empty() ? 'x' : rsStruct.rgssVal[0].back() + 1; - rsStruct.rgssVal[1] += rsStruct.rgssVal[1].empty() ? 'x' : rsStruct.rgssVal[1].front() + 1; - rsStruct.rgss8Val[0] += rsStruct.rgss8Val[0].empty() ? 'x' : rsStruct.rgss8Val[0].back() + 1; - rsStruct.rgss8Val[1] += rsStruct.rgss8Val[1].empty() ? 'x' : rsStruct.rgss8Val[1].front() + 1; - rsStruct.rgss16Val[0] += rsStruct.rgss16Val[0].empty() ? u'x' : rsStruct.rgss16Val[0].back() + 1; - rsStruct.rgss16Val[1] += rsStruct.rgss16Val[1].empty() ? u'x' : rsStruct.rgss16Val[1].front() + 1; - rsStruct.rgss32Val[0] += rsStruct.rgss32Val[0].empty() ? U'x' : rsStruct.rgss32Val[0].back() + 1; - rsStruct.rgss32Val[1] += rsStruct.rgss32Val[1].empty() ? U'x' : rsStruct.rgss32Val[1].front() + 1; - rsStruct.rgwssVal[0] += rsStruct.rgwssVal[0].empty() ? L'x' : rsStruct.rgwssVal[0].back() + 1; - rsStruct.rgwssVal[1] += rsStruct.rgwssVal[1].empty() ? L'x' : rsStruct.rgwssVal[1].front() + 1; - rsStruct.rgssFixVal[0] += rsStruct.rgssFixVal[0].empty() ? 'x' : rsStruct.rgssFixVal[0].back() + 1; - rsStruct.rgssFixVal[1] += rsStruct.rgssFixVal[1].empty() ? 'x' : rsStruct.rgssFixVal[1].front() + 1; - rsStruct.rgss8FixVal[0] += rsStruct.rgss8FixVal[0].empty() ? 'x' : rsStruct.rgss8FixVal[0].back() + 1; - rsStruct.rgss8FixVal[1] += rsStruct.rgss8FixVal[1].empty() ? 'x' : rsStruct.rgss8FixVal[1].front() + 1; - rsStruct.rgss16FixVal[0] += rsStruct.rgss16FixVal[0].empty() ? u'x' : rsStruct.rgss16FixVal[0].back() + 1; - rsStruct.rgss16FixVal[1] += rsStruct.rgss16FixVal[1].empty() ? u'x' : rsStruct.rgss16FixVal[1].front() + 1; - rsStruct.rgss32FixVal[0] += rsStruct.rgss32FixVal[0].empty() ? U'x' : rsStruct.rgss32FixVal[0].back() + 1; - rsStruct.rgss32FixVal[1] += rsStruct.rgss32FixVal[1].empty() ? U'x' : rsStruct.rgss32FixVal[1].front() + 1; - rsStruct.rgwssFixVal[0] += rsStruct.rgwssFixVal[0].empty() ? L'x' : rsStruct.rgwssFixVal[0].back() + 1; - rsStruct.rgwssFixVal[1] += rsStruct.rgwssFixVal[1].empty() ? L'x' : rsStruct.rgwssFixVal[1].front() + 1; - rsStruct.rgptr8Val[0].resize(rsStruct.rgptr8Val[0].size() * 2); - rsStruct.rgptr8Val[1].resize(rsStruct.rgptr8Val[1].size() * 3); - for (size_t nIndex = 0; nIndex < rsStruct.rgptr8Val[0].size() / 2; nIndex++) - rsStruct.rgptr8Val[0][nIndex + rsStruct.rgptr8Val[0].size() / 2] = rsStruct.rgptr8Val[0][nIndex]; - for (size_t nIndex = 0; nIndex < rsStruct.rgptr8Val[1].size() / 3; nIndex++) - rsStruct.rgptr8Val[1][nIndex + rsStruct.rgptr8Val[1].size() / 3] = rsStruct.rgptr8Val[1][nIndex]; - rsStruct.rgptrssVal[0].resize(rsStruct.rgptrssVal[0].size() * 2); - rsStruct.rgptrssVal[1].resize(rsStruct.rgptrssVal[1].size() * 3); - for (size_t nIndex = 0; nIndex < rsStruct.rgptrssVal[0].size() / 2; nIndex++) - rsStruct.rgptrssVal[0][nIndex + rsStruct.rgptrssVal[0].size() / 2] = rsStruct.rgptrssVal[0][nIndex]; - for (size_t nIndex = 0; nIndex < rsStruct.rgptrssVal[1].size() / 3; nIndex++) - rsStruct.rgptrssVal[1][nIndex + rsStruct.rgptrssVal[1].size() / 3] = rsStruct.rgptrssVal[1][nIndex]; - for (size_t nIndex = 0; nIndex < rsStruct.rgptr8FixVal[0].size(); nIndex++) - rsStruct.rgptr8FixVal[0][nIndex] = static_cast(rsStruct.rgptr8FixVal[0][nIndex] << 1); - for (size_t nIndex = 0; nIndex < rsStruct.rgptr8FixVal[1].size(); nIndex++) - rsStruct.rgptr8FixVal[1][nIndex] = static_cast(rsStruct.rgptr8FixVal[1][nIndex] << 1); - for (size_t nIndex = 0; nIndex < rsStruct.rgptrssFixVal[0].size(); nIndex++) - rsStruct.rgptrssFixVal[0][nIndex] = rsStruct.rgptrssFixVal[0][nIndex] + rsStruct.rgptrssFixVal[0][nIndex]; - for (size_t nIndex = 0; nIndex < rsStruct.rgptrssFixVal[1].size(); nIndex++) - rsStruct.rgptrssFixVal[1][nIndex] = rsStruct.rgptrssFixVal[1][nIndex] + rsStruct.rgptrssFixVal[1][nIndex]; - rsStruct.rgseq8Val[0].resize(rsStruct.rgseq8Val[0].size() * 2); - rsStruct.rgseq8Val[1].resize(rsStruct.rgseq8Val[1].size() * 3); - for (size_t nIndex = 0; nIndex < rsStruct.rgseq8Val[0].size() / 2; nIndex++) - rsStruct.rgseq8Val[0][nIndex + rsStruct.rgseq8Val[0].size() / 2] = rsStruct.rgseq8Val[0][nIndex]; - for (size_t nIndex = 0; nIndex < rsStruct.rgseq8Val[1].size() / 3; nIndex++) - rsStruct.rgseq8Val[1][nIndex + rsStruct.rgseq8Val[1].size() / 3] = rsStruct.rgseq8Val[1][nIndex]; - rsStruct.rgseqssVal[0].resize(rsStruct.rgseqssVal[0].size() * 2); - rsStruct.rgseqssVal[1].resize(rsStruct.rgseqssVal[1].size() * 3); - for (size_t nIndex = 0; nIndex < rsStruct.rgseqssVal[0].size() / 2; nIndex++) - rsStruct.rgseqssVal[0][nIndex + rsStruct.rgseqssVal[0].size() / 2] = rsStruct.rgseqssVal[0][nIndex]; - for (size_t nIndex = 0; nIndex < rsStruct.rgseqssVal[1].size() / 3; nIndex++) - rsStruct.rgseqssVal[1][nIndex + rsStruct.rgseqssVal[1].size() / 3] = rsStruct.rgseqssVal[1][nIndex]; - for (size_t nIndex = 0; nIndex < rsStruct.rgseq8FixVal[0].size(); nIndex++) - rsStruct.rgseq8FixVal[0][nIndex] = static_cast(rsStruct.rgseq8FixVal[0][nIndex] << 1); - for (size_t nIndex = 0; nIndex < rsStruct.rgseq8FixVal[1].size(); nIndex++) - rsStruct.rgseq8FixVal[1][nIndex] = static_cast(rsStruct.rgseq8FixVal[1][nIndex] << 1); - for (size_t nIndex = 0; nIndex < rsStruct.rgseqssFixVal[0].size(); nIndex++) - rsStruct.rgseqssFixVal[0][nIndex] = rsStruct.rgseqssFixVal[0][nIndex] + rsStruct.rgseqssFixVal[0][nIndex]; - for (size_t nIndex = 0; nIndex < rsStruct.rgseqssFixVal[1].size(); nIndex++) - rsStruct.rgseqssFixVal[1][nIndex] = rsStruct.rgseqssFixVal[1][nIndex] + rsStruct.rgseqssFixVal[1][nIndex]; - // Incompatible serdes generation: BUG #398509 - //if (rsStruct.rgifcVal[0].get() && rsStruct.rgifcVal[0].id() == sdv::GetInterfaceId()) - //{ - // m_summer.set(rsStruct.rgifcVal[0].get()); - // rsStruct.rgifcVal[0] = static_cast(&m_summer); - //} - //else - // rsStruct.rgifcVal[0] = nullptr; - //if (rsStruct.rgifcVal[1].get() && rsStruct.rgifcVal[1].id() == sdv::GetInterfaceId()) - //{ - // m_summer.set(rsStruct.rgifcVal[1].get()); - // rsStruct.rgifcVal[1] = static_cast(&m_summer); - //} - //else - // rsStruct.rgifcVal[1] = nullptr; - //if (rsStruct.rgpMultiplyValue[0]) - //{ - // m_multiplier.set(rsStruct.rgpMultiplyValue[0]); - // rsStruct.rgpMultiplyValue[0] = static_cast(&m_multiplier); - //} - //else - // rsStruct.rgpMultiplyValue[0] = nullptr; - //if (rsStruct.rgpMultiplyValue[1]) - //{ - // m_multiplier.set(rsStruct.rgpMultiplyValue[1]); - // rsStruct.rgpMultiplyValue[1] = static_cast(&m_multiplier); - //} - //else - // rsStruct.rgpMultiplyValue[1] = nullptr; - rsStruct.rgsSubVal[0].i += 1; - rsStruct.rgsSubVal[1].i += 2; - // Incompatible serdes generation: BUG #398509 - //rsStruct.rgsIndVal[0].i += 10; - //rsStruct.rgsIndVal[1].i += 20; - // Unnamed struct with array is not yet supported due to incorret serdes code generation: BUG #398246 - //rsStruct.rgsUnnamedVal[0].i += 20; - //rsStruct.rgsUnnamedVal[1].i += 40; - - rsStruct.tbVal = !rsStruct.tbVal; - rsStruct.tsVal--; - rsStruct.tusVal++; - rsStruct.tlVal--; - rsStruct.tulVal++; - rsStruct.tllVal--; - rsStruct.tullVal++; - rsStruct.ti8Val--; - rsStruct.tui8Val++; - rsStruct.ti16Val--; - rsStruct.tui16Val++; - rsStruct.ti32Val--; - rsStruct.tui32Val++; - rsStruct.ti64Val--; - rsStruct.tui64Val++; - rsStruct.tcVal++; - rsStruct.tc16Val++; - rsStruct.tc32Val++; - rsStruct.twcVal++; - rsStruct.tfVal -= 1000.000f; - rsStruct.tdVal += 1000.000; - rsStruct.tldVal -= 1000.000; - rsStruct.tnVal++; - rsStruct.teHelloVal = rsStruct.teHelloVal++; - if (rsStruct.tidVal == sdv::GetInterfaceId()) - rsStruct.tidVal = sdv::GetInterfaceId(); - else - rsStruct.tidVal = 0; - rsStruct.tssVal += rsStruct.tssVal.empty() ? 'x' : rsStruct.tssVal.back() + 1; - rsStruct.tss8Val += rsStruct.tss8Val.empty() ? 'x' : rsStruct.tss8Val.back() + 1; - rsStruct.tss16Val += rsStruct.tss16Val.empty() ? u'x' : rsStruct.tss16Val.back() + 1; - rsStruct.tss32Val += rsStruct.tss32Val.empty() ? U'x' : rsStruct.tss32Val.back() + 1; - rsStruct.twssVal += rsStruct.twssVal.empty() ? L'x' : rsStruct.twssVal.back() + 1; - rsStruct.tssFixVal += rsStruct.tssFixVal.empty() ? 'x' : rsStruct.tssFixVal.back() + 1; - rsStruct.tss8FixVal += rsStruct.tss8FixVal.empty() ? 'x' : rsStruct.tss8FixVal.back() + 1; - rsStruct.tss16FixVal += rsStruct.tss16FixVal.empty() ? u'x' : rsStruct.tss16FixVal.back() + 1; - rsStruct.tss32FixVal += rsStruct.tss32FixVal.empty() ? U'x' : rsStruct.tss32FixVal.back() + 1; - rsStruct.twssFixVal += rsStruct.twssFixVal.empty() ? L'x' : rsStruct.twssFixVal.back() + 1; - // Using typedefs of template based types produced an error: BUG #398266 - //rsStruct.tptr8Val.resize(rsStruct.tptr8Val.size() * 2); - //for (size_t nIndex = 0; nIndex < rsStruct.tptr8Val.size() / 2; nIndex++) - // rsStruct.tptr8Val[nIndex + rsStruct.tptr8Val.size() / 2] = rsStruct.tptr8Val[nIndex]; - //rsStruct.ptrssVal.resize(rsStruct.tptrssVal.size() * 2); - //for (size_t nIndex = 0; nIndex < rsStruct.tptrssVal.size() / 2; nIndex++) - // rsStruct.tptrssVal[nIndex + rsStruct.tptrssVal.size() / 2] = rsStruct.tptrssVal[nIndex]; - //for (size_t nIndex = 0; nIndex < rsStruct.tptr8FixVal.size(); nIndex++) - // rsStruct.tptr8FixVal[nIndex] = static_cast(rsStruct.tptr8FixVal[nIndex] << 1); - //for (size_t nIndex = 0; nIndex < rsStruct.tptrssFixVal.size(); nIndex++) - // rsStruct.ptrssFixVal[nIndex] = rsStruct.tptrssFixVal[nIndex] + rsStruct.tptrssFixVal[nIndex]; - //rsStruct.tseq8Val.resize(rsStruct.tseq8Val.size() * 2); - //for (size_t nIndex = 0; nIndex < rsStruct.tseq8Val.size() / 2; nIndex++) - // rsStruct.tseq8Val[nIndex + rsStruct.tseq8Val.size() / 2] = rsStruct.tseq8Val[nIndex]; - //rsStruct.tseqssVal.resize(rsStruct.tseqssVal.size() * 2); - //for (size_t nIndex = 0; nIndex < rsStruct.tseqssVal.size() / 2; nIndex++) - // rsStruct.seqssVal[nIndex + rsStruct.tseqssVal.size() / 2] = rsStruct.tseqssVal[nIndex]; - //for (size_t nIndex = 0; nIndex < rsStruct.tseq8FixVal.size(); nIndex++) - // rsStruct.tseq8FixVal[nIndex] = static_cast(rsStruct.tseq8FixVal[nIndex] << 1); - //for (size_t nIndex = 0; nIndex < rsStruct.tseqssFixVal.size(); nIndex++) - // rsStruct.tseqssFixVal[nIndex] = rsStruct.tseqssFixVal[nIndex] + rsStruct.tseqssFixVal[nIndex]; - if (rsStruct.tifcVal.get() && rsStruct.tifcVal.id() == sdv::GetInterfaceId()) - { - m_summerTypedef.set(rsStruct.tifcVal.get()); - rsStruct.tifcVal = static_cast(&m_summerTypedef); - } - else - rsStruct.tifcVal = nullptr; - // Typedef of interfaces current not possible. BUG #399464 - //if (rsStruct.tpMultiplyValue) - //{ - // m_multiplierTypedef.set(rsStruct.tpMultiplyValue); - // rsStruct.tpMultiplyValue = static_cast(&m_multiplierTypedef); - //} - //else - // rsStruct.tpMultiplyValue = nullptr; - rsStruct.tsSubVal.i += 1; - rsStruct.tsIndVal.i += 10; - // Incompatible serdes generation: BUG #398509 - //rsStruct.tsUnnamedVal.i += 20; - // Unnamed unions (inline definition) are not supported yet: PBI #398209 - //union switch(int32) { case 0: int32 i; default: int32 j; } tuUnnamedVal; - // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 - //struct { int32 iAnonymous1; int32 iAnonymous2; }; - //union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; - } - - /** - * @brief Update the mega union. This will test marshalling and unmarshalling of the mega union. Overload of - * IMegaTest::ProcessMegaTypeBasedUnion. - */ - virtual void ProcessMegaTypeBasedUnion(/*inout*/ UMegaTypeBasedUnion& ruUnion) override - { - switch (ruUnion.get_switch()) - { - case 1: ruUnion.bVal = !ruUnion.bVal; break; - case 2: ruUnion.sVal--; break; - case 3: ruUnion.usVal++; break; - case 4: ruUnion.lVal--; break; - case 5: ruUnion.ulVal++; break; - case 6: ruUnion.llVal--; break; - case 7: ruUnion.ullVal++; break; - case 8: ruUnion.i8Val--; break; - case 9: ruUnion.ui8Val++; break; - case 10: ruUnion.i16Val--; break; - case 11: ruUnion.ui16Val++; break; - case 12: ruUnion.i32Val--; break; - case 13: ruUnion.ui32Val++; break; - case 14: ruUnion.i64Val--; break; - case 15: ruUnion.ui64Val++; break; - case 16: ruUnion.cVal++; break; - case 17: ruUnion.c16Val++; break; - case 18: ruUnion.c32Val++; break; - case 19: ruUnion.wcVal++; break; - case 20: ruUnion.fVal -= 1000.000f; break; - case 21: ruUnion.dVal += 1000.000; break; - // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 - //case 22: ruUnion.ldVal -= 1000.000; break; - case 23: ruUnion.nVal++; break; - case 24: ruUnion.eHelloVal = ruUnion.eHelloVal++; break; - case 25: - if (ruUnion.idVal == sdv::GetInterfaceId()) - ruUnion.idVal = sdv::GetInterfaceId(); - else - ruUnion.idVal = 0; - break; - case 26: ruUnion.ssVal += ruUnion.ssVal.empty() ? 'x' : ruUnion.ssVal.back() + 1; break; - case 27: ruUnion.ss8Val += ruUnion.ss8Val.empty() ? 'x' : ruUnion.ss8Val.back() + 1; break; - case 28: ruUnion.ss16Val += ruUnion.ss16Val.empty() ? u'x' : ruUnion.ss16Val.back() + 1; break; - case 29: ruUnion.ss32Val += ruUnion.ss32Val.empty() ? U'x' : ruUnion.ss32Val.back() + 1; break; - case 30: ruUnion.wssVal += ruUnion.wssVal.empty() ? L'x' : ruUnion.wssVal.back() + 1; break; - case 31: ruUnion.ssFixVal += ruUnion.ssFixVal.empty() ? 'x' : ruUnion.ssFixVal.back() + 1; break; - case 32: ruUnion.ss8FixVal += ruUnion.ss8FixVal.empty() ? 'x' : ruUnion.ss8FixVal.back() + 1; break; - case 33: ruUnion.ss16FixVal += ruUnion.ss16FixVal.empty() ? u'x' : ruUnion.ss16FixVal.back() + 1; break; - case 34: ruUnion.ss32FixVal += ruUnion.ss32FixVal.empty() ? U'x' : ruUnion.ss32FixVal.back() + 1; break; - case 35: ruUnion.wssFixVal += ruUnion.wssFixVal.empty() ? L'x' : ruUnion.wssFixVal.back() + 1; break; - case 36: - ruUnion.ptr8Val.resize(ruUnion.ptr8Val.size() * 2); - for (size_t nIndex = 0; nIndex < ruUnion.ptr8Val.size() / 2; nIndex++) - ruUnion.ptr8Val[nIndex + ruUnion.ptr8Val.size() / 2] = ruUnion.ptr8Val[nIndex]; - break; - case 37: - ruUnion.ptrssVal.resize(ruUnion.ptrssVal.size() * 2); - for (size_t nIndex = 0; nIndex < ruUnion.ptrssVal.size() / 2; nIndex++) - ruUnion.ptrssVal[nIndex + ruUnion.ptrssVal.size() / 2] = ruUnion.ptrssVal[nIndex]; - break; - case 38: - for (size_t nIndex = 0; nIndex < ruUnion.ptr8FixVal.size(); nIndex++) - ruUnion.ptr8FixVal[nIndex] = static_cast(ruUnion.ptr8FixVal[nIndex] << 1); - break; - case 39: - for (size_t nIndex = 0; nIndex < ruUnion.ptrssFixVal.size(); nIndex++) - ruUnion.ptrssFixVal[nIndex] = ruUnion.ptrssFixVal[nIndex] + ruUnion.ptrssFixVal[nIndex]; - break; - case 40: - ruUnion.seq8Val.resize(ruUnion.seq8Val.size() * 2); - for (size_t nIndex = 0; nIndex < ruUnion.seq8Val.size() / 2; nIndex++) - ruUnion.seq8Val[nIndex + ruUnion.seq8Val.size() / 2] = ruUnion.seq8Val[nIndex]; - break; - case 41: - ruUnion.seqssVal.resize(ruUnion.seqssVal.size() * 2); - for (size_t nIndex = 0; nIndex < ruUnion.seqssVal.size() / 2; nIndex++) - ruUnion.seqssVal[nIndex + ruUnion.seqssVal.size() / 2] = ruUnion.seqssVal[nIndex]; - break; - case 42: - for (size_t nIndex = 0; nIndex < ruUnion.seq8FixVal.size(); nIndex++) - ruUnion.seq8FixVal[nIndex] = static_cast(ruUnion.seq8FixVal[nIndex] << 1); - break; - case 43: - for (size_t nIndex = 0; nIndex < ruUnion.seqssFixVal.size(); nIndex++) - ruUnion.seqssFixVal[nIndex] = ruUnion.seqssFixVal[nIndex] + ruUnion.seqssFixVal[nIndex]; - break; - case 44: - if (ruUnion.ifcVal.get() && ruUnion.ifcVal.id() == sdv::GetInterfaceId()) - { - m_summer.set(ruUnion.ifcVal.get()); - ruUnion.ifcVal = static_cast(&m_summer); - } - else - ruUnion.ifcVal = nullptr; - break; - case 45: - if (ruUnion.pMultiplyValue) - { - m_multiplier.set(ruUnion.pMultiplyValue); - ruUnion.pMultiplyValue = static_cast(&m_multiplier); - } - else - ruUnion.pMultiplyValue = nullptr; - break; - case 46: ruUnion.sSubVal.i += 1; break; - // Incompatible serdes generation: BUG #398509 - //case 47: ruUnion.sIndVal.i += 10; break; - // Unnamed struct inside unions are not supported yet: PBI #398230 - //case 48: ruUnion.sUnnamedVal.i += 20; break; - // Unnamed unions are not supported yet: PBI #398209 - //case 49: union switch(int32) { case 0: int32 i; default: int32 j; } uUnnamedVal; - // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 - //case 50: struct { int32 iAnonymous1; int32 iAnonymous2; }; - //case 51: union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; - - case 201: ruUnion.tbVal = !ruUnion.tbVal; break; - case 202: ruUnion.tsVal--; break; - case 203: ruUnion.tusVal++; break; - case 204: ruUnion.tlVal--; break; - case 205: ruUnion.tulVal++; break; - case 206: ruUnion.tllVal--; break; - case 207: ruUnion.tullVal++; break; - case 208: ruUnion.ti8Val--; break; - case 209: ruUnion.tui8Val++; break; - case 210: ruUnion.ti16Val--; break; - case 211: ruUnion.tui16Val++; break; - case 212: ruUnion.ti32Val--; break; - case 213: ruUnion.tui32Val++; break; - case 214: ruUnion.ti64Val--; break; - case 215: ruUnion.tui64Val++; break; - case 216: ruUnion.tcVal++; break; - case 217: ruUnion.tc16Val++; break; - case 218: ruUnion.tc32Val++; break; - case 219: ruUnion.twcVal++; break; - case 220: ruUnion.tfVal -= 1000.000f; break; - case 221: ruUnion.tdVal += 1000.000; break; - // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 - //case 22: ruUnion.tldVal -= 1000.000; break; - case 223: ruUnion.tnVal++; break; - case 224: ruUnion.teHelloVal = ruUnion.teHelloVal++; break; - case 225: - if (ruUnion.tidVal == sdv::GetInterfaceId()) - ruUnion.tidVal = sdv::GetInterfaceId(); - else - ruUnion.tidVal = 0; - break; - case 226: ruUnion.tssVal += ruUnion.tssVal.empty() ? 'x' : ruUnion.tssVal.back() + 1; break; - case 227: ruUnion.tss8Val += ruUnion.tss8Val.empty() ? 'x' : ruUnion.tss8Val.back() + 1; break; - case 228: ruUnion.tss16Val += ruUnion.tss16Val.empty() ? u'x' : ruUnion.tss16Val.back() + 1; break; - case 229: ruUnion.tss32Val += ruUnion.tss32Val.empty() ? U'x' : ruUnion.tss32Val.back() + 1; break; - case 230: ruUnion.twssVal += ruUnion.twssVal.empty() ? L'x' : ruUnion.twssVal.back() + 1; break; - case 231: ruUnion.tssFixVal += ruUnion.tssFixVal.empty() ? 'x' : ruUnion.tssFixVal.back() + 1; break; - case 232: ruUnion.tss8FixVal += ruUnion.tss8FixVal.empty() ? 'x' : ruUnion.tss8FixVal.back() + 1; break; - case 233: ruUnion.tss16FixVal += ruUnion.tss16FixVal.empty() ? u'x' : ruUnion.tss16FixVal.back() + 1; break; - case 234: ruUnion.tss32FixVal += ruUnion.tss32FixVal.empty() ? U'x' : ruUnion.tss32FixVal.back() + 1; break; - case 235: ruUnion.twssFixVal += ruUnion.twssFixVal.empty() ? L'x' : ruUnion.twssFixVal.back() + 1; break; - // Using typedefs of template based types produced an error: BUG #398266 - //case 236: - // ruUnion.tptr8Val.resize(ruUnion.tptr8Val.size() * 2); - // for (size_t nIndex = 0; nIndex < ruUnion.tptr8Val.size() / 2; nIndex++) - // ruUnion.tptr8Val[nIndex + ruUnion.tptr8Val.size() / 2] = ruUnion.tptr8Val[nIndex]; - // break; - //case 237: - // ruUnion.tptrssVal.resize(ruUnion.tptrssVal.size() * 2); - // for (size_t nIndex = 0; nIndex < ruUnion.tptrssVal.size() / 2; nIndex++) - // ruUnion.tptrssVal[nIndex + ruUnion.tptrssVal.size() / 2] = ruUnion.tptrssVal[nIndex]; - // break; - //case 238: - // for (size_t nIndex = 0; nIndex < ruUnion.tptr8FixVal.size(); nIndex++) - // ruUnion.tptr8FixVal[nIndex] = static_cast(ruUnion.tptr8FixVal[nIndex] << 1); - // break; - //case 239: - // for (size_t nIndex = 0; nIndex < ruUnion.tptrssFixVal.size(); nIndex++) - // ruUnion.tptrssFixVal[nIndex] = ruUnion.tptrssFixVal[nIndex] + ruUnion.tptrssFixVal[nIndex]; - // break; - //case 240: - // ruUnion.tseq8Val.resize(ruUnion.tseq8Val.size() * 2); - // for (size_t nIndex = 0; nIndex < ruUnion.tseq8Val.size() / 2; nIndex++) - // ruUnion.tseq8Val[nIndex + ruUnion.tseq8Val.size() / 2] = ruUnion.tseq8Val[nIndex]; - // break; - //case 241: - // ruUnion.tseqssVal.resize(ruUnion.tseqssVal.size() * 2); - // for (size_t nIndex = 0; nIndex < ruUnion.tseqssVal.size() / 2; nIndex++) - // ruUnion.tseqssVal[nIndex + ruUnion.tseqssVal.size() / 2] = ruUnion.tseqssVal[nIndex]; - // break; - //case 242: - // for (size_t nIndex = 0; nIndex < ruUnion.tseq8FixVal.size(); nIndex++) - // ruUnion.tseq8FixVal[nIndex] = static_cast(ruUnion.tseq8FixVal[nIndex] << 1); - // break; - //case 243: - // for (size_t nIndex = 0; nIndex < ruUnion.tseqssFixVal.size(); nIndex++) - // ruUnion.tseqssFixVal[nIndex] = ruUnion.tseqssFixVal[nIndex] + ruUnion.tseqssFixVal[nIndex]; - // break; - case 244: - if (ruUnion.tifcVal.get() && ruUnion.tifcVal.id() == sdv::GetInterfaceId()) - { - m_summerTypedef.set(ruUnion.tifcVal.get()); - ruUnion.tifcVal = static_cast(&m_summerTypedef); - } - else - ruUnion.tifcVal = nullptr; - break; - // Typedef of interfaces current not possible. BUG #399464 - //case 245: - // if (ruUnion.tpMultiplyValue) - // { - // m_multiplierTypedef.set(ruUnion.tpMultiplyValue); - // ruUnion.tpMultiplyValue = static_cast(&m_multiplierTypedef); - // } - // else - // ruUnion.tpMultiplyValue = nullptr; - // break; - case 246: ruUnion.tsSubVal.i += 1; break; - // Incompatible serdes generation: BUG #398509 - case 247: ruUnion.tsIndVal.i += 10; break; - // Unnamed struct inside unions are not supported yet: PBI #398230 - //case 248: ruUnion.tsUnnamedVal.i += 20; break; - // Unnamed unions are not supported yet: PBI #398209 - //case 249: union switch(int32) { case 0: int32 i; default: int32 j; } uUnnamedVal; - // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 - //case 250: struct { int32 iAnonymous1; int32 iAnonymous2; }; - //case 251: union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; - default: break; - } - } - - /** - * @brief Update the mega union. This will test marshalling and unmarshalling of the mega union. Overload of - * IMegaTest::ProcessMegaTypeBasedUnion. - */ - virtual void ProcessMegaVarBasedUnion(/*inout*/ SMegaVarBasedUnion& rsUnion) override - { - switch (rsUnion.get_switch()) - { - case 1: rsUnion.uVal.bVal = !rsUnion.uVal.bVal; break; - case 2: rsUnion.uVal.sVal--; break; - case 3: rsUnion.uVal.usVal++; break; - case 4: rsUnion.uVal.lVal--; break; - case 5: rsUnion.uVal.ulVal++; break; - case 6: rsUnion.uVal.llVal--; break; - case 7: rsUnion.uVal.ullVal++; break; - case 8: rsUnion.uVal.i8Val--; break; - case 9: rsUnion.uVal.ui8Val++; break; - case 10: rsUnion.uVal.i16Val--; break; - case 11: rsUnion.uVal.ui16Val++; break; - case 12: rsUnion.uVal.i32Val--; break; - case 13: rsUnion.uVal.ui32Val++; break; - case 14: rsUnion.uVal.i64Val--; break; - case 15: rsUnion.uVal.ui64Val++; break; - case 16: rsUnion.uVal.cVal++; break; - case 17: rsUnion.uVal.c16Val++; break; - case 18: rsUnion.uVal.c32Val++; break; - case 19: rsUnion.uVal.wcVal++; break; - case 20: rsUnion.uVal.fVal -= 1000.000f; break; - case 21: rsUnion.uVal.dVal += 1000.000; break; - // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 - //case 22: rsUnion.uVal.ldVal -= 1000.000; break; - case 23: rsUnion.uVal.nVal++; break; - case 24: rsUnion.uVal.eHelloVal = rsUnion.uVal.eHelloVal++; break; - case 25: - if (rsUnion.uVal.idVal == sdv::GetInterfaceId()) - rsUnion.uVal.idVal = sdv::GetInterfaceId(); - else - rsUnion.uVal.idVal = 0; - break; - case 26: rsUnion.uVal.ssVal += rsUnion.uVal.ssVal.empty() ? 'x' : rsUnion.uVal.ssVal.back() + 1; break; - case 27: rsUnion.uVal.ss8Val += rsUnion.uVal.ss8Val.empty() ? 'x' : rsUnion.uVal.ss8Val.back() + 1; break; - case 28: rsUnion.uVal.ss16Val += rsUnion.uVal.ss16Val.empty() ? u'x' : rsUnion.uVal.ss16Val.back() + 1; break; - case 29: rsUnion.uVal.ss32Val += rsUnion.uVal.ss32Val.empty() ? U'x' : rsUnion.uVal.ss32Val.back() + 1; break; - case 30: rsUnion.uVal.wssVal += rsUnion.uVal.wssVal.empty() ? L'x' : rsUnion.uVal.wssVal.back() + 1; break; - case 31: rsUnion.uVal.ssFixVal += rsUnion.uVal.ssFixVal.empty() ? 'x' : rsUnion.uVal.ssFixVal.back() + 1; break; - case 32: rsUnion.uVal.ss8FixVal += rsUnion.uVal.ss8FixVal.empty() ? 'x' : rsUnion.uVal.ss8FixVal.back() + 1; break; - case 33: rsUnion.uVal.ss16FixVal += rsUnion.uVal.ss16FixVal.empty() ? u'x' : rsUnion.uVal.ss16FixVal.back() + 1; break; - case 34: rsUnion.uVal.ss32FixVal += rsUnion.uVal.ss32FixVal.empty() ? U'x' : rsUnion.uVal.ss32FixVal.back() + 1; break; - case 35: rsUnion.uVal.wssFixVal += rsUnion.uVal.wssFixVal.empty() ? L'x' : rsUnion.uVal.wssFixVal.back() + 1; break; - case 36: - rsUnion.uVal.ptr8Val.resize(rsUnion.uVal.ptr8Val.size() * 2); - for (size_t nIndex = 0; nIndex < rsUnion.uVal.ptr8Val.size() / 2; nIndex++) - rsUnion.uVal.ptr8Val[nIndex + rsUnion.uVal.ptr8Val.size() / 2] = rsUnion.uVal.ptr8Val[nIndex]; - break; - case 37: - rsUnion.uVal.ptrssVal.resize(rsUnion.uVal.ptrssVal.size() * 2); - for (size_t nIndex = 0; nIndex < rsUnion.uVal.ptrssVal.size() / 2; nIndex++) - rsUnion.uVal.ptrssVal[nIndex + rsUnion.uVal.ptrssVal.size() / 2] = rsUnion.uVal.ptrssVal[nIndex]; - break; - case 38: - for (size_t nIndex = 0; nIndex < rsUnion.uVal.ptr8FixVal.size(); nIndex++) - rsUnion.uVal.ptr8FixVal[nIndex] = static_cast(rsUnion.uVal.ptr8FixVal[nIndex] << 1); - break; - case 39: - for (size_t nIndex = 0; nIndex < rsUnion.uVal.ptrssFixVal.size(); nIndex++) - rsUnion.uVal.ptrssFixVal[nIndex] = rsUnion.uVal.ptrssFixVal[nIndex] + rsUnion.uVal.ptrssFixVal[nIndex]; - break; - case 40: - rsUnion.uVal.seq8Val.resize(rsUnion.uVal.seq8Val.size() * 2); - for (size_t nIndex = 0; nIndex < rsUnion.uVal.seq8Val.size() / 2; nIndex++) - rsUnion.uVal.seq8Val[nIndex + rsUnion.uVal.seq8Val.size() / 2] = rsUnion.uVal.seq8Val[nIndex]; - break; - case 41: - rsUnion.uVal.seqssVal.resize(rsUnion.uVal.seqssVal.size() * 2); - for (size_t nIndex = 0; nIndex < rsUnion.uVal.seqssVal.size() / 2; nIndex++) - rsUnion.uVal.seqssVal[nIndex + rsUnion.uVal.seqssVal.size() / 2] = rsUnion.uVal.seqssVal[nIndex]; - break; - case 42: - for (size_t nIndex = 0; nIndex < rsUnion.uVal.seq8FixVal.size(); nIndex++) - rsUnion.uVal.seq8FixVal[nIndex] = static_cast(rsUnion.uVal.seq8FixVal[nIndex] << 1); - break; - case 43: - for (size_t nIndex = 0; nIndex < rsUnion.uVal.seqssFixVal.size(); nIndex++) - rsUnion.uVal.seqssFixVal[nIndex] = rsUnion.uVal.seqssFixVal[nIndex] + rsUnion.uVal.seqssFixVal[nIndex]; - break; - case 44: - if (rsUnion.uVal.ifcVal.get() && rsUnion.uVal.ifcVal.id() == sdv::GetInterfaceId()) - { - m_summer.set(rsUnion.uVal.ifcVal.get()); - rsUnion.uVal.ifcVal = static_cast(&m_summer); - } - else - rsUnion.uVal.ifcVal = nullptr; - break; - case 45: - if (rsUnion.uVal.pMultiplyValue) - { - m_multiplier.set(rsUnion.uVal.pMultiplyValue); - rsUnion.uVal.pMultiplyValue = static_cast(&m_multiplier); - } - else - rsUnion.uVal.pMultiplyValue = nullptr; - break; - case 46: rsUnion.uVal.sSubVal.i += 1; break; - // Incompatible serdes generation: BUG #398509 - //case 47: rsUnion.uVal.sIndVal.i += 10; break; - // Unnamed struct inside unions are not supported yet: PBI #398230 - //case 48: rsUnion.uVal.sUnnamedVal.i += 20; break; - // Unnamed unions are not supported yet: PBI #398209 - //case 49: union switch(int32) { case 0: int32 i; default: int32 j; } uUnnamedVal; - // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 - //case 50: struct { int32 iAnonymous1; int32 iAnonymous2; }; - //case 51: union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; - - case 201: rsUnion.uVal.tbVal = !rsUnion.uVal.tbVal; break; - case 202: rsUnion.uVal.tsVal--; break; - case 203: rsUnion.uVal.tusVal++; break; - case 204: rsUnion.uVal.tlVal--; break; - case 205: rsUnion.uVal.tulVal++; break; - case 206: rsUnion.uVal.tllVal--; break; - case 207: rsUnion.uVal.tullVal++; break; - case 208: rsUnion.uVal.ti8Val--; break; - case 209: rsUnion.uVal.tui8Val++; break; - case 210: rsUnion.uVal.ti16Val--; break; - case 211: rsUnion.uVal.tui16Val++; break; - case 212: rsUnion.uVal.ti32Val--; break; - case 213: rsUnion.uVal.tui32Val++; break; - case 214: rsUnion.uVal.ti64Val--; break; - case 215: rsUnion.uVal.tui64Val++; break; - case 216: rsUnion.uVal.tcVal++; break; - case 217: rsUnion.uVal.tc16Val++; break; - case 218: rsUnion.uVal.tc32Val++; break; - case 219: rsUnion.uVal.twcVal++; break; - case 220: rsUnion.uVal.tfVal -= 1000.000f; break; - case 221: rsUnion.uVal.tdVal += 1000.000; break; - // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 - //case 22: rsUnion.uVal.tldVal -= 1000.000; break; - case 223: rsUnion.uVal.tnVal++; break; - case 224: rsUnion.uVal.teHelloVal = rsUnion.uVal.teHelloVal++; break; - case 225: - if (rsUnion.uVal.tidVal == sdv::GetInterfaceId()) - rsUnion.uVal.tidVal = sdv::GetInterfaceId(); - else - rsUnion.uVal.tidVal = 0; - break; - case 226: rsUnion.uVal.tssVal += rsUnion.uVal.tssVal.empty() ? 'x' : rsUnion.uVal.tssVal.back() + 1; break; - case 227: rsUnion.uVal.tss8Val += rsUnion.uVal.tss8Val.empty() ? 'x' : rsUnion.uVal.tss8Val.back() + 1; break; - case 228: rsUnion.uVal.tss16Val += rsUnion.uVal.tss16Val.empty() ? u'x' : rsUnion.uVal.tss16Val.back() + 1; break; - case 229: rsUnion.uVal.tss32Val += rsUnion.uVal.tss32Val.empty() ? U'x' : rsUnion.uVal.tss32Val.back() + 1; break; - case 230: rsUnion.uVal.twssVal += rsUnion.uVal.twssVal.empty() ? L'x' : rsUnion.uVal.twssVal.back() + 1; break; - case 231: rsUnion.uVal.tssFixVal += rsUnion.uVal.tssFixVal.empty() ? 'x' : rsUnion.uVal.tssFixVal.back() + 1; break; - case 232: rsUnion.uVal.tss8FixVal += rsUnion.uVal.tss8FixVal.empty() ? 'x' : rsUnion.uVal.tss8FixVal.back() + 1; break; - case 233: rsUnion.uVal.tss16FixVal += rsUnion.uVal.tss16FixVal.empty() ? u'x' : rsUnion.uVal.tss16FixVal.back() + 1; break; - case 234: rsUnion.uVal.tss32FixVal += rsUnion.uVal.tss32FixVal.empty() ? U'x' : rsUnion.uVal.tss32FixVal.back() + 1; break; - case 235: rsUnion.uVal.twssFixVal += rsUnion.uVal.twssFixVal.empty() ? L'x' : rsUnion.uVal.twssFixVal.back() + 1; break; - // Using typedefs of template based types produced an error: BUG #398266 - //case 236: - // rsUnion.uVal.tptr8Val.resize(rsUnion.uVal.tptr8Val.size() * 2); - // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tptr8Val.size() / 2; nIndex++) - // rsUnion.uVal.tptr8Val[nIndex + rsUnion.uVal.tptr8Val.size() / 2] = rsUnion.uVal.tptr8Val[nIndex]; - // break; - //case 237: - // rsUnion.uVal.tptrssVal.resize(rsUnion.uVal.tptrssVal.size() * 2); - // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tptrssVal.size() / 2; nIndex++) - // rsUnion.uVal.tptrssVal[nIndex + rsUnion.uVal.tptrssVal.size() / 2] = rsUnion.uVal.tptrssVal[nIndex]; - // break; - //case 238: - // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tptr8FixVal.size(); nIndex++) - // rsUnion.uVal.tptr8FixVal[nIndex] = static_cast(rsUnion.uVal.tptr8FixVal[nIndex] << 1); - // break; - //case 239: - // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tptrssFixVal.size(); nIndex++) - // rsUnion.uVal.tptrssFixVal[nIndex] = rsUnion.uVal.tptrssFixVal[nIndex] + rsUnion.uVal.tptrssFixVal[nIndex]; - // break; - //case 240: - // rsUnion.uVal.tseq8Val.resize(rsUnion.uVal.tseq8Val.size() * 2); - // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tseq8Val.size() / 2; nIndex++) - // rsUnion.uVal.tseq8Val[nIndex + rsUnion.uVal.tseq8Val.size() / 2] = rsUnion.uVal.tseq8Val[nIndex]; - // break; - //case 241: - // rsUnion.uVal.tseqssVal.resize(rsUnion.uVal.tseqssVal.size() * 2); - // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tseqssVal.size() / 2; nIndex++) - // rsUnion.uVal.tseqssVal[nIndex + rsUnion.uVal.tseqssVal.size() / 2] = rsUnion.uVal.tseqssVal[nIndex]; - // break; - //case 242: - // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tseq8FixVal.size(); nIndex++) - // rsUnion.uVal.tseq8FixVal[nIndex] = static_cast(rsUnion.uVal.tseq8FixVal[nIndex] << 1); - // break; - //case 243: - // for (size_t nIndex = 0; nIndex < rsUnion.uVal.tseqssFixVal.size(); nIndex++) - // rsUnion.uVal.tseqssFixVal[nIndex] = rsUnion.uVal.tseqssFixVal[nIndex] + rsUnion.uVal.tseqssFixVal[nIndex]; - // break; - case 244: - if (rsUnion.uVal.tifcVal.get() && rsUnion.uVal.tifcVal.id() == sdv::GetInterfaceId()) - { - m_summerTypedef.set(rsUnion.uVal.tifcVal.get()); - rsUnion.uVal.tifcVal = static_cast(&m_summerTypedef); - } - else - rsUnion.uVal.tifcVal = nullptr; - break; - // Typedef of interfaces current not possible. BUG #399464 - //case 245: - // if (rsUnion.uVal.tpMultiplyValue) - // { - // m_multiplierTypedef.set(rsUnion.uVal.tpMultiplyValue); - // rsUnion.uVal.tpMultiplyValue = static_cast(&m_multiplierTypedef); - // } - // else - // rsUnion.uVal.tpMultiplyValue = nullptr; - // break; - case 246: rsUnion.uVal.tsSubVal.i += 1; break; - // Incompatible serdes generation: BUG #398509 - case 247: rsUnion.uVal.tsIndVal.i += 10; break; - // Unnamed struct inside unions are not supported yet: PBI #398230 - //case 248: rsUnion.uVal.tsUnnamedVal.i += 20; break; - // Unnamed unions are not supported yet: PBI #398209 - //case 249: union switch(int32) { case 0: int32 i; default: int32 j; } uUnnamedVal; - // Anonymous structs and unions (unnamed and no declaration) are not supported yet: PBI #397894 - //case 250: struct { int32 iAnonymous1; int32 iAnonymous2; }; - //case 251: union switch(int32) { case 0: int32 iAnonymous1; default: int32 iAnonymous2; }; - default: break; - } - } - - ISayHello* m_pHello = nullptr; - - struct SInternalMultiply : IMultiplyValue - { - void set(IMultiplyValue* p) { pMultiplyValue = p; } - size_t Multiply(size_t n) - { - if (!pMultiplyValue) return 0; - return pMultiplyValue->Multiply(n) * pMultiplyValue->Multiply(n); - } - IMultiplyValue* pMultiplyValue = nullptr; - }; - SInternalMultiply m_multiplier; - SInternalMultiply m_multiplierTypedef; - - struct SInternalAdd : IAddValue - { - void set(IMultiplyValue* p) { pMultiplyValue = p; } - size_t Add(size_t n) - { - if (!pMultiplyValue) return 0; - return pMultiplyValue->Multiply(n) + pMultiplyValue->Multiply(n); - } - IMultiplyValue* pMultiplyValue = nullptr; - }; - SInternalAdd m_summer; - SInternalAdd m_summerTypedef; -}; - -void LoadIPCModules(CCommunicationControl& rControl) -{ - // Load the IPC modules - sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); - ASSERT_NE(pModuleControl, nullptr); - EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0u); - EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0u); - EXPECT_NE(pModuleControl->Load("UnitTest_IPC_Communication_ps.sdv"), 0u); - - // Create the services - sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); - ASSERT_NE(pRepositoryControl, nullptr); - EXPECT_NE(pRepositoryControl->CreateObject("DefaultSharedMemoryChannelControl", {}, {}), 0u); - - // Register the communication control service - sdv::core::IRegisterForeignObject* pRegisterObject = sdv::core::GetObject("RepositoryService"); - ASSERT_NE(pRegisterObject, nullptr); - EXPECT_NE(pRegisterObject->RegisterObject(&rControl, "CommunicationControl"), 0u); -} - -TEST(IPC_Communication_Test, Instantiate) +TEST(IPC_Communication_Test_SharedMem, Instantiate) { CCommunicationControl control; - EXPECT_NO_THROW(control.Initialize("")); + EXPECT_NO_THROW(control.Initialize(sdv::SObjectInfo())); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(control.Shutdown()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::destruction_pending); } -TEST(IPC_Communication_Test, AssignServerEndpoint) +TEST(IPC_Communication_Test_SharedMem, AssignServerEndpoint) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -947,21 +31,24 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create a channel endpoint - sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("LocalChannelControl"); + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("DefaultSharedMemory"); ASSERT_NE(ptrCreateEndpoint, nullptr); sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -979,7 +66,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, AssignClientEndpoint) +TEST(IPC_Communication_Test_SharedMem, AssignClientEndpoint) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -988,21 +75,24 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create a server endpoint - sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("LocalChannelControl"); + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("DefaultSharedMemory"); ASSERT_NE(ptrCreateEndpoint, nullptr); sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -1011,7 +101,7 @@ ViewFilter = "Fatal" EXPECT_TRUE(ptrServerEndpoint); // Create a client endpoint - sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject("LocalChannelControl"); + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject("DefaultSharedMemory"); ASSERT_NE(pChannelAccess, nullptr); sdv::TObjectPtr ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); EXPECT_TRUE(ptrClientEndpoint); @@ -1030,13 +120,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -// Disabled the following test due to an unidentified crash/heap corruption occurring with MINGW on the build-server. -// Bug-report #610009 describes this issue: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/610009 -//#ifdef _WIN32 -TEST(IPC_Communication_Test, DISABLED_AssignClientEndpoint_Reconnect) -//#else -//TEST(IPC_Communication_Test, AssignClientEndpoint_Reconnect) -//#endif +TEST(IPC_Communication_Test_SharedMem, AssignClientEndpoint_Reconnect) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1045,21 +129,24 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create a server endpoint - sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("LocalChannelControl"); + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("DefaultSharedMemory"); ASSERT_NE(ptrCreateEndpoint, nullptr); sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -1068,7 +155,7 @@ ViewFilter = "Fatal" EXPECT_TRUE(ptrServerEndpoint); // Create a client endpoint - sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject("LocalChannelControl"); + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject("DefaultSharedMemory"); ASSERT_NE(pChannelAccess, nullptr); sdv::TObjectPtr ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); EXPECT_TRUE(ptrClientEndpoint); @@ -1106,7 +193,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, AssignClientEndpoint_FailReconnect) +TEST(IPC_Communication_Test_SharedMem, AssignClientEndpoint_FailReconnect) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1115,21 +202,24 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create a server endpoint - sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("LocalChannelControl"); + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("DefaultSharedMemory"); ASSERT_NE(ptrCreateEndpoint, nullptr); sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -1138,7 +228,7 @@ ViewFilter = "Fatal" EXPECT_TRUE(ptrServerEndpoint); // Create a client endpoint - sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject("LocalChannelControl"); + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject("DefaultSharedMemory"); ASSERT_NE(pChannelAccess, nullptr); sdv::TObjectPtr ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); EXPECT_TRUE(ptrClientEndpoint); @@ -1176,7 +266,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, CreateServerConnection) +TEST(IPC_Communication_Test_SharedMem, CreateServerConnection) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1185,18 +275,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1208,7 +301,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, CreateClientConnection) +TEST(IPC_Communication_Test_SharedMem, CreateClientConnection) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1217,18 +310,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1245,7 +341,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, CreateClientConnection_FailReconnect) +TEST(IPC_Communication_Test_SharedMem, CreateClientConnection_FailReconnect) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1254,18 +350,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1292,7 +391,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallClientToServer) +TEST(IPC_Communication_Test_SharedMem, MarshallClientToServer) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1301,18 +400,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1336,7 +438,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallInterfaceFromServer) +TEST(IPC_Communication_Test_SharedMem, MarshallInterfaceFromServer) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1345,18 +447,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1385,7 +490,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallInterfaceFromClient) +TEST(IPC_Communication_Test_SharedMem, MarshallInterfaceFromClient) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1394,18 +499,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1434,7 +542,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallGenericInterfaceFromServer) +TEST(IPC_Communication_Test_SharedMem, MarshallGenericInterfaceFromServer) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1443,18 +551,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1487,7 +598,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructBoolean) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructBoolean) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1496,18 +607,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1534,7 +648,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructIntegral) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructIntegral) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1543,18 +657,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1608,7 +725,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructCharacter) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructCharacter) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1622,13 +739,13 @@ ViewFilter = "Fatal" // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1660,7 +777,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructFloatingPoint) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructFloatingPoint) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1669,18 +786,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1713,7 +833,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructEnum) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructEnum) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1722,18 +842,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1759,7 +882,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructString) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructString) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1768,18 +891,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1823,7 +949,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructPointer) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructPointer) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1832,18 +958,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1898,7 +1027,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructSequence) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructSequence) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1907,18 +1036,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -1973,7 +1105,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructInterface) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructInterface) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -1982,18 +1114,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2036,7 +1171,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructSubCompound) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructSubCompound) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2045,18 +1180,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2086,7 +1224,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructBooleanArray) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructBooleanArray) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2095,18 +1233,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2135,7 +1276,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructIntegralArray) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructIntegralArray) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2144,18 +1285,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2239,7 +1383,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructCharacterArray) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructCharacterArray) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2248,18 +1392,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2299,7 +1446,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructFloatingPointArray) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructFloatingPointArray) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2308,18 +1455,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2358,7 +1508,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructEnumArray) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructEnumArray) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2367,18 +1517,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2406,7 +1559,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructStringArray) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructStringArray) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2415,18 +1568,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2490,7 +1646,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructPointerArray) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructPointerArray) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2499,18 +1655,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2596,7 +1755,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructSequenceArray) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructSequenceArray) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2605,18 +1764,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2702,7 +1864,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructInterfaceArray) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructInterfaceArray) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2711,18 +1873,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2775,7 +1940,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructSubCompoundArray) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructSubCompoundArray) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2784,18 +1949,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2833,7 +2001,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructBooleanTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructBooleanTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2842,18 +2010,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2880,7 +2051,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructIntegralTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructIntegralTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2889,18 +2060,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -2954,7 +2128,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructCharacterTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructCharacterTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -2963,18 +2137,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3006,7 +2183,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructFloatingPointTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructFloatingPointTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3015,18 +2192,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3059,7 +2239,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructEnumTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructEnumTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3068,18 +2248,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3105,7 +2288,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructStringTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructStringTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3114,18 +2297,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3170,7 +2356,7 @@ ViewFilter = "Fatal" } // Using typedefs of template based types produced an error: BUG #398266 -TEST(IPC_Communication_Test, DISABLED_MarshallMegaStructPointerTypedef) +TEST(IPC_Communication_Test_SharedMem, DISABLED_MarshallMegaStructPointerTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3179,18 +2365,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3246,7 +2435,7 @@ ViewFilter = "Fatal" } // Using typedefs of template based types produced an error: BUG #398266 -TEST(IPC_Communication_Test, DISABLED_MarshallMegaStructSequenceTypedef) +TEST(IPC_Communication_Test_SharedMem, DISABLED_MarshallMegaStructSequenceTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3255,18 +2444,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3321,7 +2513,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructInterfaceTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructInterfaceTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3330,18 +2522,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3386,7 +2581,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaStructSubCompoundTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaStructSubCompoundTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3395,18 +2590,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3438,7 +2636,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionBoolean) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionBoolean) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3447,18 +2645,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3486,7 +2687,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionIntegral) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionIntegral) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3495,18 +2696,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3589,7 +2793,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionCharacter) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionCharacter) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3598,18 +2802,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3648,7 +2855,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionFloatingPoint) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionFloatingPoint) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3657,18 +2864,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3707,7 +2917,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionEnum) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionEnum) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3716,18 +2926,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3754,7 +2967,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionString) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionString) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3763,18 +2976,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3837,7 +3053,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionPointer) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionPointer) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3846,18 +3062,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -3919,7 +3138,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionSequence) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionSequence) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -3928,18 +3147,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4001,7 +3223,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionInterface) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionInterface) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4010,18 +3232,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4069,7 +3294,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionSubCompound) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionSubCompound) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4078,18 +3303,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4116,7 +3344,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionBooleanTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionBooleanTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4125,18 +3353,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4164,7 +3395,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionIntegralTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionIntegralTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4173,18 +3404,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4267,7 +3501,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionCharacterTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionCharacterTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4276,18 +3510,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4326,7 +3563,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionFloatingPointTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionFloatingPointTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4335,18 +3572,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4385,7 +3625,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionEnumTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionEnumTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4394,18 +3634,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4432,7 +3675,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionStringTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionStringTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4441,18 +3684,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4516,7 +3762,7 @@ ViewFilter = "Fatal" } // Using typedefs of template based types produced an error: BUG #398266 -TEST(IPC_Communication_Test, DISABLED_MarshallMegaTypeBasedUnionPointerTypedef) +TEST(IPC_Communication_Test_SharedMem, DISABLED_MarshallMegaTypeBasedUnionPointerTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4525,18 +3771,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4599,7 +3848,7 @@ ViewFilter = "Fatal" } // Using typedefs of template based types produced an error: BUG #398266 -TEST(IPC_Communication_Test, DISABLED_MarshallMegaTypeBasedUnionSequenceTypedef) +TEST(IPC_Communication_Test_SharedMem, DISABLED_MarshallMegaTypeBasedUnionSequenceTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4608,18 +3857,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4681,7 +3933,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionInterfaceTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionInterfaceTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4690,18 +3942,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4750,7 +4005,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaTypeBasedUnionSubCompoundTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaTypeBasedUnionSubCompoundTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4759,18 +4014,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4801,7 +4059,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionBoolean) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionBoolean) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4810,18 +4068,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4849,7 +4110,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionIntegral) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionIntegral) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4858,18 +4119,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -4952,7 +4216,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionCharacter) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionCharacter) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -4961,18 +4225,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5011,7 +4278,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionFloatingPoint) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionFloatingPoint) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5020,18 +4287,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5070,7 +4340,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionEnum) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionEnum) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5079,18 +4349,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5117,7 +4390,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionString) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionString) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5126,18 +4399,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5200,7 +4476,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionPointer) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionPointer) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5209,18 +4485,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5282,7 +4561,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionSequence) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionSequence) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5291,18 +4570,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5364,7 +4646,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionInterface) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionInterface) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5373,18 +4655,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5432,7 +4717,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionSubCompound) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionSubCompound) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5441,18 +4726,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5479,7 +4767,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionBooleanTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionBooleanTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5488,18 +4776,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5527,7 +4818,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionIntegralTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionIntegralTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5536,18 +4827,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5630,7 +4924,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionCharacterTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionCharacterTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5639,18 +4933,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5689,7 +4986,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionFloatingPointTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionFloatingPointTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5698,18 +4995,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5748,7 +5048,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionEnumTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionEnumTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5757,18 +5057,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5795,7 +5098,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionStringTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionStringTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5804,18 +5107,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5879,7 +5185,7 @@ ViewFilter = "Fatal" } // Using typedefs of template based types produced an error: BUG #398266 -TEST(IPC_Communication_Test, DISABLED_MarshallMegaVarBasedUnionPointerTypedef) +TEST(IPC_Communication_Test_SharedMem, DISABLED_MarshallMegaVarBasedUnionPointerTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5888,18 +5194,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -5962,7 +5271,7 @@ ViewFilter = "Fatal" } // Using typedefs of template based types produced an error: BUG #398266 -TEST(IPC_Communication_Test, DISABLED_MarshallMegaVarBasedUnionSequenceTypedef) +TEST(IPC_Communication_Test_SharedMem, DISABLED_MarshallMegaVarBasedUnionSequenceTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -5971,18 +5280,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -6044,7 +5356,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionInterfaceTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionInterfaceTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -6053,18 +5365,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; @@ -6113,7 +5428,7 @@ ViewFilter = "Fatal" appcontrol.Shutdown(); } -TEST(IPC_Communication_Test, MarshallMegaVarBasedUnionSubCompoundTypedef) +TEST(IPC_Communication_Test_SharedMem, MarshallMegaVarBasedUnionSubCompoundTypedef) { sdv::app::CAppControl appcontrol; ASSERT_TRUE(appcontrol.Startup(R"config( @@ -6122,18 +5437,21 @@ Mode = "Essential" [LogHandler] ViewFilter = "Fatal" + +[Console] +Report = "Silent" )config")); ASSERT_TRUE(appcontrol.IsRunning()); // Start communication control CCommunicationControl control; - control.Initialize(""); + control.Initialize(sdv::SObjectInfo()); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); control.SetOperationMode(sdv::EOperationMode::configuring); EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); // Load the shared memory components - LoadIPCModules(control); + LoadIPCModules(control, ELocalIpcBackend::sharedmem); // Create the server connection CInterfaceTest test; diff --git a/tests/unit_tests/ipc_com/ipc_com_uds.cpp b/tests/unit_tests/ipc_com/ipc_com_uds.cpp new file mode 100644 index 0000000..8c6f472 --- /dev/null +++ b/tests/unit_tests/ipc_com/ipc_com_uds.cpp @@ -0,0 +1,5476 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + + +#include "include.h" + +TEST(IPC_Communication_Test_UDS, AssignServerEndpoint) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create a channel endpoint + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("unix_domain_sockets"); + ASSERT_NE(ptrCreateEndpoint, nullptr); + sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(""); + EXPECT_NE(sChannelEndpoint.pConnection, nullptr); + EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); + EXPECT_NE(sChannelEndpoint.ssConnectString.find("proto=uds"), std::string::npos); + sdv::TObjectPtr ptrEndpoint = sChannelEndpoint.pConnection; // Automatic lifetime management + EXPECT_TRUE(ptrEndpoint); + + // Assign the server endpoint + CInterfaceTest test; + EXPECT_TRUE(control.AssignServerEndpoint(ptrEndpoint, &test, 100, false) != 0u); + ptrEndpoint.Clear(); // Lifetime taken over by communication control. + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, AssignClientEndpoint) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create a server endpoint + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("unix_domain_sockets"); + ASSERT_NE(ptrCreateEndpoint, nullptr); + sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(""); + EXPECT_NE(sChannelEndpoint.pConnection, nullptr); + EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); + sdv::TObjectPtr ptrServerEndpoint = sChannelEndpoint.pConnection; // Automatic lifetime management + EXPECT_TRUE(ptrServerEndpoint); + + // Create a client endpoint + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject("unix_domain_sockets"); + ASSERT_NE(pChannelAccess, nullptr); + sdv::TObjectPtr ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); + EXPECT_TRUE(ptrClientEndpoint); + + // Assign the server and client endpoints + CInterfaceTest test; + EXPECT_TRUE(control.AssignServerEndpoint(ptrServerEndpoint, &test, 100, false) != 0u); + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.AssignClientEndpoint(ptrClientEndpoint, 1000, pObjectProxy) != 0u); + EXPECT_NE(pObjectProxy, nullptr); + ptrServerEndpoint.Clear(); // Lifetime taken over by communication control. + ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, AssignClientEndpoint_Reconnect) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create a server endpoint + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("unix_domain_sockets"); + ASSERT_NE(ptrCreateEndpoint, nullptr); + sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(""); + EXPECT_NE(sChannelEndpoint.pConnection, nullptr); + EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); + sdv::TObjectPtr ptrServerEndpoint = sChannelEndpoint.pConnection; // Automatic lifetime management + EXPECT_TRUE(ptrServerEndpoint); + + // Create a client endpoint + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject("unix_domain_sockets"); + ASSERT_NE(pChannelAccess, nullptr); + sdv::TObjectPtr ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); + EXPECT_TRUE(ptrClientEndpoint); + + // Assign the server and client endpoints + CInterfaceTest test; + EXPECT_TRUE(control.AssignServerEndpoint(ptrServerEndpoint, &test, 100, true) != 0u); + ptrServerEndpoint.Clear(); // Lifetime taken over by communication control. + sdv::IInterfaceAccess* pObjectProxy = nullptr; + sdv::com::TConnectionID tConnectionID = control.AssignClientEndpoint(ptrClientEndpoint, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID != 0u); + EXPECT_NE(pObjectProxy, nullptr); + + // Disconnect from the client + sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface(); + ASSERT_NE(pClientConnect, nullptr); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::connected); + control.RemoveConnection(tConnectionID); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected); + ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. + + // Create another client endpoint + ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); + EXPECT_TRUE(ptrClientEndpoint); + + // Assign the new client endpoint + pObjectProxy = nullptr; + tConnectionID = control.AssignClientEndpoint(ptrClientEndpoint, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID != 0u); + EXPECT_NE(pObjectProxy, nullptr); + ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, AssignClientEndpoint_FailReconnect) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create a server endpoint + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject("unix_domain_sockets"); + ASSERT_NE(ptrCreateEndpoint, nullptr); + sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(""); + EXPECT_NE(sChannelEndpoint.pConnection, nullptr); + EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); + sdv::TObjectPtr ptrServerEndpoint = sChannelEndpoint.pConnection; // Automatic lifetime management + EXPECT_TRUE(ptrServerEndpoint); + + // Create a client endpoint + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject("unix_domain_sockets"); + ASSERT_NE(pChannelAccess, nullptr); + sdv::TObjectPtr ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); + EXPECT_TRUE(ptrClientEndpoint); + + // Assign the server and client endpoints + CInterfaceTest test; + EXPECT_TRUE(control.AssignServerEndpoint(ptrServerEndpoint, &test, 100, false) != 0u); + ptrServerEndpoint.Clear(); // Lifetime taken over by communication control. + sdv::IInterfaceAccess* pObjectProxy = nullptr; + sdv::com::TConnectionID tConnectionID = control.AssignClientEndpoint(ptrClientEndpoint, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID != 0u); + EXPECT_NE(pObjectProxy, nullptr); + + // Disconnect from the client + sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface(); + ASSERT_NE(pClientConnect, nullptr); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::connected); + control.RemoveConnection(tConnectionID); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected); + ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. + + // Create another client endpoint + ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); + EXPECT_TRUE(ptrClientEndpoint); + + // Assign the new client endpoint - this should fail! + pObjectProxy = nullptr; + tConnectionID = control.AssignClientEndpoint(ptrClientEndpoint, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID == 0u); + EXPECT_EQ(pObjectProxy, nullptr); + ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, CreateServerConnection) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, CreateClientConnection) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + EXPECT_NE(pObjectProxy, nullptr); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, CreateClientConnection_FailReconnect) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + sdv::com::TConnectionID tConnectionID = control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID != 0u); + EXPECT_NE(pObjectProxy, nullptr); + + // Disconnect from the client + control.RemoveConnection(tConnectionID); + + // Create a new client connection + pObjectProxy = nullptr; + tConnectionID = control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID == 0u); + EXPECT_EQ(pObjectProxy, nullptr); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallClientToServer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Request the ISayHello interface + ISayHello* pSayHello = nullptr; + EXPECT_NO_THROW(pSayHello = pObjectProxy->GetInterface()); + ASSERT_NE(pSayHello, nullptr); + + EXPECT_EQ(pSayHello->Hello(), "Hello"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallInterfaceFromServer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Request the IRequestHello interface + IRequestHello* pRequestHello = nullptr; + EXPECT_NO_THROW(pRequestHello = pObjectProxy->GetInterface()); + ASSERT_NE(pRequestHello, nullptr); + + // Request the ISayHello interface + ISayHello* pSayHello = pRequestHello->Request(); + + // Call the interface as if talking from the client + ASSERT_NE(pSayHello, nullptr); + EXPECT_EQ(pSayHello->Hello(), "Hello"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallInterfaceFromClient) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Request the IRegisterHelloCallback interface + IRegisterHelloCallback* pRegisterHelloCallback = nullptr; + EXPECT_NO_THROW(pRegisterHelloCallback = pObjectProxy->GetInterface()); + ASSERT_NE(pRegisterHelloCallback, nullptr); + + // Register the callback (stored in the object) + pRegisterHelloCallback->Register(&test); + + // Call the interface as if talking from the server + ASSERT_NE(test.m_pHello, nullptr); + EXPECT_EQ(test.m_pHello->Hello(), "Hello"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallGenericInterfaceFromServer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Request the IRegisterHelloCallback interface + sdv::IInterfaceAccess* pRegisterHelloCallback = nullptr; + EXPECT_NO_THROW(pRegisterHelloCallback = pObjectProxy->GetInterface()); + ASSERT_NE(pRegisterHelloCallback, nullptr); + + // Call GetInterface to get the IInterfaceAccess pointer once more (to be able to test marshalling). + sdv::IInterfaceAccess* pInterfaceAccess = pObjectProxy->GetInterface(); + ASSERT_NE(pInterfaceAccess, nullptr); + + // Request the pSayHello interface (this will be marshalled as well) + ISayHello* pSayHello = pInterfaceAccess->GetInterface(); + + // Call the interface as if talking from the client + ASSERT_NE(pSayHello, nullptr); + EXPECT_EQ(pSayHello->Hello(), "Hello"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructBoolean) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.bVal = false; + EXPECT_FALSE(sStruct.bVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_TRUE(sStruct.bVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructIntegral) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.sVal = -10; + sStruct.usVal = 10; + sStruct.lVal = -20; + sStruct.ulVal = 20; + sStruct.llVal = -30; + sStruct.ullVal = 30; + sStruct.i8Val = -40; + sStruct.ui8Val = 40; + sStruct.i16Val = -50; + sStruct.ui16Val = 50; + sStruct.i32Val = -60; + sStruct.ui32Val = 60; + sStruct.i64Val = -70; + sStruct.ui64Val = 70; + sStruct.nVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.sVal, -11); + EXPECT_EQ(sStruct.usVal, 11u); + EXPECT_EQ(sStruct.lVal, -21); + EXPECT_EQ(sStruct.ulVal, 21u); + EXPECT_EQ(sStruct.llVal, -31); + EXPECT_EQ(sStruct.ullVal, 31u); + EXPECT_EQ(static_cast(sStruct.i8Val), static_cast(-41)); // Cast needed for ARM compatibility + EXPECT_EQ(sStruct.ui8Val, 41u); + EXPECT_EQ(sStruct.i16Val, -51); + EXPECT_EQ(sStruct.ui16Val, 51u); + EXPECT_EQ(sStruct.i32Val, -61); + EXPECT_EQ(sStruct.ui32Val, 61u); + EXPECT_EQ(sStruct.i64Val, -71); + EXPECT_EQ(sStruct.ui64Val, 71u); + EXPECT_EQ(sStruct.nVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructCharacter) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.cVal = 'A'; + sStruct.c16Val = u'D'; + sStruct.c32Val = U'G'; + sStruct.wcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.cVal, 'B'); + EXPECT_EQ(sStruct.c16Val, u'E'); + EXPECT_EQ(sStruct.c32Val, U'H'); + EXPECT_EQ(sStruct.wcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructFloatingPoint) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + float fVal = -1234.5678f; + sStruct.fVal = fVal; + double dVal = 8765.4321; + sStruct.dVal = dVal; + long double ldVal = -1234.4321; + sStruct.ldVal = ldVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.fVal, fVal - 1000.000f); + EXPECT_EQ(sStruct.dVal, dVal + 1000.000); + EXPECT_EQ(sStruct.ldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructEnum) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.eHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.eHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructString) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.ssVal = "hi"; + sStruct.ss8Val = "huhu"; + sStruct.ss16Val = u"hey"; + sStruct.ss32Val = U"hello"; + sStruct.wssVal = L"servus"; + sStruct.ssFixVal = "hi"; + sStruct.ss8FixVal = "huhu"; + sStruct.ss16FixVal = u"hey"; + sStruct.ss32FixVal = U"hello"; + sStruct.wssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.ssVal, "hij"); + EXPECT_EQ(sStruct.ss8Val, "huhuv"); + EXPECT_EQ(sStruct.ss16Val, u"heyz"); + EXPECT_EQ(sStruct.ss32Val, U"hellop"); + EXPECT_EQ(sStruct.wssVal, L"servust"); + EXPECT_EQ(sStruct.ssFixVal, "hij"); + EXPECT_EQ(sStruct.ss8FixVal, "huhuv"); + EXPECT_EQ(sStruct.ss16FixVal, u"heyz"); + EXPECT_EQ(sStruct.ss32FixVal, U"hellop"); + EXPECT_EQ(sStruct.wssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructPointer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.ptr8Val.resize(100); + for (size_t nIndex = 0; nIndex < sStruct.ptr8Val.size(); nIndex++) + sStruct.ptr8Val[nIndex] = static_cast(nIndex); + sStruct.ptrssVal.resize(3); + sStruct.ptrssVal[0] = "hello"; + sStruct.ptrssVal[1] = "huhu"; + sStruct.ptrssVal[2] = "hi"; + sStruct.ptr8FixVal.resize(sStruct.ptr8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < sStruct.ptr8FixVal.size(); nIndex++) + sStruct.ptr8FixVal[nIndex] = static_cast(nIndex); + sStruct.ptrssFixVal.resize(sStruct.ptrssFixVal.capacity()); + sStruct.ptrssFixVal[0] = "hi"; + sStruct.ptrssFixVal[1] = "hoi"; + sStruct.ptrssFixVal[2] = "hello"; + sStruct.ptrssFixVal[3] = "hallo"; + sStruct.ptrssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.ptr8Val.size(), 200); + for (size_t nIndex = 0; nIndex < sStruct.ptr8Val.size() / 2; nIndex++) + EXPECT_EQ(sStruct.ptr8Val[nIndex], sStruct.ptr8Val[nIndex + sStruct.ptr8Val.size() / 2]); + EXPECT_EQ(sStruct.ptrssVal.size(), 6); + for (size_t nIndex = 0; nIndex < sStruct.ptrssVal.size() / 2; nIndex++) + EXPECT_EQ(sStruct.ptrssVal[nIndex], sStruct.ptrssVal[nIndex + sStruct.ptrssVal.size() / 2]); + EXPECT_EQ(sStruct.ptr8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < sStruct.ptr8FixVal.size(); nIndex++) + EXPECT_EQ(sStruct.ptr8FixVal[nIndex], static_cast(nIndex << 1)); + EXPECT_EQ(sStruct.ptrssFixVal.size(), 5); + EXPECT_EQ(sStruct.ptrssFixVal[0], "hihi"); + EXPECT_EQ(sStruct.ptrssFixVal[1], "hoihoi"); + EXPECT_EQ(sStruct.ptrssFixVal[2], "hellohello"); + EXPECT_EQ(sStruct.ptrssFixVal[3], "hallohallo"); + EXPECT_EQ(sStruct.ptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructSequence) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.seq8Val.resize(100); + for (size_t nIndex = 0; nIndex < sStruct.seq8Val.size(); nIndex++) + sStruct.seq8Val[nIndex] = static_cast(nIndex); + sStruct.seqssVal.resize(3); + sStruct.seqssVal[0] = "hello"; + sStruct.seqssVal[1] = "huhu"; + sStruct.seqssVal[2] = "hi"; + sStruct.seq8FixVal.resize(sStruct.seq8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < sStruct.seq8FixVal.size(); nIndex++) + sStruct.seq8FixVal[nIndex] = static_cast(nIndex); + sStruct.seqssFixVal.resize(sStruct.seqssFixVal.capacity()); + sStruct.seqssFixVal[0] = "hi"; + sStruct.seqssFixVal[1] = "hoi"; + sStruct.seqssFixVal[2] = "hello"; + sStruct.seqssFixVal[3] = "hallo"; + sStruct.seqssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.seq8Val.size(), 200); + for (size_t nIndex = 0; nIndex < sStruct.seq8Val.size() / 2; nIndex++) + EXPECT_EQ(sStruct.seq8Val[nIndex], sStruct.seq8Val[nIndex + sStruct.seq8Val.size() / 2]); + EXPECT_EQ(sStruct.seqssVal.size(), 6); + for (size_t nIndex = 0; nIndex < sStruct.seqssVal.size() / 2; nIndex++) + EXPECT_EQ(sStruct.seqssVal[nIndex], sStruct.seqssVal[nIndex + sStruct.seqssVal.size() / 2]); + EXPECT_EQ(sStruct.seq8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < sStruct.seq8FixVal.size(); nIndex++) + EXPECT_EQ(sStruct.seq8FixVal[nIndex], static_cast(nIndex << 1)); + EXPECT_EQ(sStruct.seqssFixVal.size(), 5); + EXPECT_EQ(sStruct.seqssFixVal[0], "hihi"); + EXPECT_EQ(sStruct.seqssFixVal[1], "hoihoi"); + EXPECT_EQ(sStruct.seqssFixVal[2], "hellohello"); + EXPECT_EQ(sStruct.seqssFixVal[3], "hallohallo"); + EXPECT_EQ(sStruct.seqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructInterface) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + + sStruct.idVal = sdv::GetInterfaceId(); + sStruct.ifcVal = static_cast(&sMultiply); + sStruct.pMultiplyValue = &sMultiply; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.idVal, sdv::GetInterfaceId()); + EXPECT_EQ(sStruct.ifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(sStruct.ifcVal, nullptr); + ASSERT_NE(sStruct.ifcVal.get(), nullptr); + EXPECT_EQ(sStruct.ifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + ASSERT_NE(sStruct.pMultiplyValue, nullptr); + EXPECT_EQ(sStruct.pMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructSubCompound) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.sSubVal.i = 1; + sStruct.sIndVal.i = 10; + //sStruct.sUnnamedVal.i = 100; // 12.04.2024 EVE Unnamed structures are not supported by IDL compiler. + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.sSubVal.i, 2); + EXPECT_EQ(sStruct.sIndVal.i, 20); + //EXPECT_EQ(sStruct.sUnnamedVal.i, 120); // 12.04.2024 EVE Unnamed structures are not supported by IDL compiler. + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructBooleanArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgbVal[0] = false; + sStruct.rgbVal[1] = true; + EXPECT_FALSE(sStruct.bVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_TRUE(sStruct.rgbVal[0]); + EXPECT_FALSE(sStruct.rgbVal[1]); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructIntegralArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgsVal[0] = -10; + sStruct.rgsVal[1] = -11; + sStruct.rgusVal[0] = 10; + sStruct.rgusVal[1] = 11; + sStruct.rglVal[0] = -20; + sStruct.rglVal[1] = -21; + sStruct.rgulVal[0] = 20; + sStruct.rgulVal[1] = 21; + sStruct.rgllVal[0] = -30; + sStruct.rgllVal[1] = -31; + sStruct.rgullVal[0] = 30; + sStruct.rgullVal[1] = 31; + sStruct.rgi8Val[0] = -40; + sStruct.rgi8Val[1] = -41; + sStruct.rgui8Val[0] = 40; + sStruct.rgui8Val[1] = 41; + sStruct.rgi16Val[0] = -50; + sStruct.rgi16Val[1] = -51; + sStruct.rgui16Val[0] = 50; + sStruct.rgui16Val[1] = 51; + sStruct.rgi32Val[0] = -60; + sStruct.rgi32Val[1] = -61; + sStruct.rgui32Val[0] = 60; + sStruct.rgui32Val[1] = 61; + sStruct.rgi64Val[0] = -70; + sStruct.rgi64Val[1] = -71; + sStruct.rgui64Val[0] = 70; + sStruct.rgui64Val[1] = 71; + sStruct.rgnVal[0] = 80; + sStruct.rgnVal[1] = 81; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgsVal[0], -11); + EXPECT_EQ(sStruct.rgsVal[1], -10); + EXPECT_EQ(sStruct.rgusVal[0], 11u); + EXPECT_EQ(sStruct.rgusVal[1], 13u); + EXPECT_EQ(sStruct.rglVal[0], -21); + EXPECT_EQ(sStruct.rglVal[1], -20); + EXPECT_EQ(sStruct.rgulVal[0], 21u); + EXPECT_EQ(sStruct.rgulVal[1], 23u); + EXPECT_EQ(sStruct.rgllVal[0], -31); + EXPECT_EQ(sStruct.rgllVal[1], -30); + EXPECT_EQ(sStruct.rgullVal[0], 31u); + EXPECT_EQ(sStruct.rgullVal[1], 33u); + EXPECT_EQ(static_cast(sStruct.rgi8Val[0]), static_cast(-41)); // Cast needed for ARM compatibility + EXPECT_EQ(static_cast(sStruct.rgi8Val[1]), static_cast(-40)); // Cast needed for ARM compatibility + EXPECT_EQ(sStruct.rgui8Val[0], 41u); + EXPECT_EQ(sStruct.rgui8Val[1], 43u); + EXPECT_EQ(sStruct.rgi16Val[0], -51); + EXPECT_EQ(sStruct.rgi16Val[1], -50); + EXPECT_EQ(sStruct.rgui16Val[0], 51u); + EXPECT_EQ(sStruct.rgui16Val[1], 53u); + EXPECT_EQ(sStruct.rgi32Val[0], -61); + EXPECT_EQ(sStruct.rgi32Val[1], -60); + EXPECT_EQ(sStruct.rgui32Val[0], 61u); + EXPECT_EQ(sStruct.rgui32Val[1], 63u); + EXPECT_EQ(sStruct.rgi64Val[0], -71); + EXPECT_EQ(sStruct.rgi64Val[1], -70); + EXPECT_EQ(sStruct.rgui64Val[0], 71u); + EXPECT_EQ(sStruct.rgui64Val[1], 73u); + EXPECT_EQ(sStruct.rgnVal[0], 81); + EXPECT_EQ(sStruct.rgnVal[1], 83); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructCharacterArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgcVal[0] = 'A'; + sStruct.rgcVal[1] = 'B'; + sStruct.rgc16Val[0] = u'D'; + sStruct.rgc16Val[1] = u'E'; + sStruct.rgc32Val[0] = U'G'; + sStruct.rgc32Val[1] = U'H'; + sStruct.rgwcVal[0] = L'J'; + sStruct.rgwcVal[1] = L'K'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgcVal[0], 'B'); + EXPECT_EQ(sStruct.rgcVal[1], 'D'); + EXPECT_EQ(sStruct.rgc16Val[0], u'E'); + EXPECT_EQ(sStruct.rgc16Val[1], u'G'); + EXPECT_EQ(sStruct.rgc32Val[0], U'H'); + EXPECT_EQ(sStruct.rgc32Val[1], U'J'); + EXPECT_EQ(sStruct.rgwcVal[0], L'K'); + EXPECT_EQ(sStruct.rgwcVal[1], L'M'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructFloatingPointArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + float fVal = -1234.5678f; + sStruct.rgfVal[0] = fVal; + sStruct.rgfVal[1] = fVal; + double dVal = 8765.4321; + sStruct.rgdVal[0] = dVal; + sStruct.rgdVal[1] = dVal; + long double ldVal = -1234.4321; + sStruct.rgldVal[0] = ldVal; + sStruct.rgldVal[1] = ldVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgfVal[0], fVal - 1000.000f); + EXPECT_EQ(sStruct.rgfVal[1], fVal + 2000.000f); + EXPECT_EQ(sStruct.rgdVal[0], dVal + 1000.000); + EXPECT_EQ(sStruct.rgdVal[1], dVal - 2000.000); + EXPECT_EQ(sStruct.rgldVal[0], ldVal - 1000.000); + EXPECT_EQ(sStruct.rgldVal[1], ldVal + 2000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructEnumArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgeHelloVal[0] = EHello::hello; + sStruct.rgeHelloVal[1] = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgeHelloVal[0], EHello::hallo); + EXPECT_EQ(sStruct.rgeHelloVal[1], EHello::huhu); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructStringArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgssVal[0] = "hi"; + sStruct.rgssVal[1] = "hi"; + sStruct.rgss8Val[0] = "huhu"; + sStruct.rgss8Val[1] = "huhu"; + sStruct.rgss16Val[0] = u"hey"; + sStruct.rgss16Val[1] = u"hey"; + sStruct.rgss32Val[0] = U"hello"; + sStruct.rgss32Val[1] = U"hello"; + sStruct.rgwssVal[0] = L"servus"; + sStruct.rgwssVal[1] = L"servus"; + sStruct.rgssFixVal[0] = "hi"; + sStruct.rgssFixVal[1] = "hi"; + sStruct.rgss8FixVal[0] = "huhu"; + sStruct.rgss8FixVal[1] = "huhu"; + sStruct.rgss16FixVal[0] = u"hey"; + sStruct.rgss16FixVal[1] = u"hey"; + sStruct.rgss32FixVal[0] = U"hello"; + sStruct.rgss32FixVal[1] = U"hello"; + sStruct.rgwssFixVal[0] = L"servus"; + sStruct.rgwssFixVal[1] = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgssVal[0], "hij"); + EXPECT_EQ(sStruct.rgssVal[1], "hii"); + EXPECT_EQ(sStruct.rgss8Val[0], "huhuv"); + EXPECT_EQ(sStruct.rgss8Val[1], "huhui"); + EXPECT_EQ(sStruct.rgss16Val[0], u"heyz"); + EXPECT_EQ(sStruct.rgss16Val[1], u"heyi"); + EXPECT_EQ(sStruct.rgss32Val[0], U"hellop"); + EXPECT_EQ(sStruct.rgss32Val[1], U"helloi"); + EXPECT_EQ(sStruct.rgwssVal[0], L"servust"); + EXPECT_EQ(sStruct.rgwssVal[1], L"servust"); + EXPECT_EQ(sStruct.rgssFixVal[0], "hij"); + EXPECT_EQ(sStruct.rgssFixVal[1], "hii"); + EXPECT_EQ(sStruct.rgss8FixVal[0], "huhuv"); + EXPECT_EQ(sStruct.rgss8FixVal[1], "huhui"); + EXPECT_EQ(sStruct.rgss16FixVal[0], u"heyz"); + EXPECT_EQ(sStruct.rgss16FixVal[1], u"heyi"); + EXPECT_EQ(sStruct.rgss32FixVal[0], U"hellop"); + EXPECT_EQ(sStruct.rgss32FixVal[1], U"helloi"); + EXPECT_EQ(sStruct.rgwssFixVal[0], L"servust"); + EXPECT_EQ(sStruct.rgwssFixVal[1], L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructPointerArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgptr8Val[0].resize(100); + sStruct.rgptr8Val[1].resize(100); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8Val[0].size(); nIndex++) + sStruct.rgptr8Val[0][nIndex] = static_cast(nIndex); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8Val[1].size(); nIndex++) + sStruct.rgptr8Val[1][nIndex] = static_cast(nIndex); + sStruct.rgptrssVal[0].resize(3); + sStruct.rgptrssVal[1].resize(3); + sStruct.rgptrssVal[0][0] = "hello"; + sStruct.rgptrssVal[1][0] = "ohhello"; + sStruct.rgptrssVal[0][1] = "huhu"; + sStruct.rgptrssVal[1][1] = "ohhuhu"; + sStruct.rgptrssVal[0][2] = "hi"; + sStruct.rgptrssVal[1][2] = "ohhi"; + sStruct.rgptr8FixVal[0].resize(sStruct.rgptr8FixVal[0].capacity()); + sStruct.rgptr8FixVal[1].resize(sStruct.rgptr8FixVal[1].capacity()); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8FixVal[0].size(); nIndex++) + sStruct.rgptr8FixVal[0][nIndex] = static_cast(nIndex); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8FixVal[1].size(); nIndex++) + sStruct.rgptr8FixVal[1][nIndex] = static_cast(nIndex); + sStruct.rgptrssFixVal[0].resize(sStruct.rgptrssFixVal[0].capacity()); + sStruct.rgptrssFixVal[1].resize(sStruct.rgptrssFixVal[1].capacity()); + sStruct.rgptrssFixVal[0][0] = "hi"; + sStruct.rgptrssFixVal[1][0] = "ohhi"; + sStruct.rgptrssFixVal[0][1] = "hoi"; + sStruct.rgptrssFixVal[1][1] = "ohhoi"; + sStruct.rgptrssFixVal[0][2] = "hello"; + sStruct.rgptrssFixVal[1][2] = "ohhello"; + sStruct.rgptrssFixVal[0][3] = "hallo"; + sStruct.rgptrssFixVal[1][3] = "ohhallo"; + sStruct.rgptrssFixVal[0][4] = "servus"; + sStruct.rgptrssFixVal[1][4] = "ohservus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgptr8Val[0].size(), 200); + EXPECT_EQ(sStruct.rgptr8Val[1].size(), 300); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8Val[0].size() / 2; nIndex++) + EXPECT_EQ(sStruct.rgptr8Val[0][nIndex], sStruct.rgptr8Val[0][nIndex + sStruct.rgptr8Val[0].size() / 2]); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8Val[1].size() / 3; nIndex++) + EXPECT_EQ(sStruct.rgptr8Val[1][nIndex], sStruct.rgptr8Val[1][nIndex + sStruct.rgptr8Val[1].size() / 3]); + EXPECT_EQ(sStruct.rgptrssVal[0].size(), 6); + EXPECT_EQ(sStruct.rgptrssVal[1].size(), 9); + for (size_t nIndex = 0; nIndex < sStruct.rgptrssVal[0].size() / 2; nIndex++) + EXPECT_EQ(sStruct.rgptrssVal[0][nIndex], sStruct.rgptrssVal[0][nIndex + sStruct.rgptrssVal[0].size() / 2]); + for (size_t nIndex = 0; nIndex < sStruct.rgptrssVal[1].size() / 3; nIndex++) + EXPECT_EQ(sStruct.rgptrssVal[1][nIndex], sStruct.rgptrssVal[1][nIndex + sStruct.rgptrssVal[1].size() / 3]); + EXPECT_EQ(sStruct.rgptr8FixVal[0].size(), 20); + EXPECT_EQ(sStruct.rgptr8FixVal[1].size(), 20); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8FixVal[0].size(); nIndex++) + EXPECT_EQ(sStruct.rgptr8FixVal[0][nIndex], static_cast(nIndex << 1)); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8FixVal[1].size(); nIndex++) + EXPECT_EQ(sStruct.rgptr8FixVal[1][nIndex], static_cast(nIndex << 1)); + EXPECT_EQ(sStruct.rgptrssFixVal[0].size(), 5); + EXPECT_EQ(sStruct.rgptrssFixVal[1].size(), 5); + EXPECT_EQ(sStruct.rgptrssFixVal[0][0], "hihi"); + EXPECT_EQ(sStruct.rgptrssFixVal[1][0], "ohhiohhi"); + EXPECT_EQ(sStruct.rgptrssFixVal[0][1], "hoihoi"); + EXPECT_EQ(sStruct.rgptrssFixVal[1][1], "ohhoiohhoi"); + EXPECT_EQ(sStruct.rgptrssFixVal[0][2], "hellohello"); + EXPECT_EQ(sStruct.rgptrssFixVal[1][2], "ohhelloohhello"); + EXPECT_EQ(sStruct.rgptrssFixVal[0][3], "hallohallo"); + EXPECT_EQ(sStruct.rgptrssFixVal[1][3], "ohhalloohhallo"); + EXPECT_EQ(sStruct.rgptrssFixVal[0][4], "servusservus"); + EXPECT_EQ(sStruct.rgptrssFixVal[1][4], "ohservusohservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructSequenceArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgseq8Val[0].resize(100); + sStruct.rgseq8Val[1].resize(100); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8Val[0].size(); nIndex++) + sStruct.rgseq8Val[0][nIndex] = static_cast(nIndex); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8Val[1].size(); nIndex++) + sStruct.rgseq8Val[1][nIndex] = static_cast(nIndex); + sStruct.rgseqssVal[0].resize(3); + sStruct.rgseqssVal[1].resize(3); + sStruct.rgseqssVal[0][0] = "hello"; + sStruct.rgseqssVal[1][0] = "ohhello"; + sStruct.rgseqssVal[0][1] = "huhu"; + sStruct.rgseqssVal[1][1] = "ohhuhu"; + sStruct.rgseqssVal[0][2] = "hi"; + sStruct.rgseqssVal[1][2] = "ohhi"; + sStruct.rgseq8FixVal[0].resize(sStruct.rgseq8FixVal[0].capacity()); + sStruct.rgseq8FixVal[1].resize(sStruct.rgseq8FixVal[1].capacity()); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8FixVal[0].size(); nIndex++) + sStruct.rgseq8FixVal[0][nIndex] = static_cast(nIndex); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8FixVal[1].size(); nIndex++) + sStruct.rgseq8FixVal[1][nIndex] = static_cast(nIndex); + sStruct.rgseqssFixVal[0].resize(sStruct.seqssFixVal.capacity()); + sStruct.rgseqssFixVal[1].resize(sStruct.seqssFixVal.capacity()); + sStruct.rgseqssFixVal[0][0] = "hi"; + sStruct.rgseqssFixVal[1][0] = "ohhi"; + sStruct.rgseqssFixVal[0][1] = "hoi"; + sStruct.rgseqssFixVal[1][1] = "ohhoi"; + sStruct.rgseqssFixVal[0][2] = "hello"; + sStruct.rgseqssFixVal[1][2] = "ohhello"; + sStruct.rgseqssFixVal[0][3] = "hallo"; + sStruct.rgseqssFixVal[1][3] = "ohhallo"; + sStruct.rgseqssFixVal[0][4] = "servus"; + sStruct.rgseqssFixVal[1][4] = "ohservus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgseq8Val[0].size(), 200); + EXPECT_EQ(sStruct.rgseq8Val[1].size(), 300); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8Val[0].size() / 2; nIndex++) + EXPECT_EQ(sStruct.rgseq8Val[0][nIndex], sStruct.rgseq8Val[0][nIndex + sStruct.rgseq8Val[0].size() / 2]); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8Val[1].size() / 3; nIndex++) + EXPECT_EQ(sStruct.rgseq8Val[1][nIndex], sStruct.rgseq8Val[1][nIndex + sStruct.rgseq8Val[1].size() / 3]); + EXPECT_EQ(sStruct.rgseqssVal[0].size(), 6); + EXPECT_EQ(sStruct.rgseqssVal[1].size(), 9); + for (size_t nIndex = 0; nIndex < sStruct.rgseqssVal[0].size() / 2; nIndex++) + EXPECT_EQ(sStruct.rgseqssVal[0][nIndex], sStruct.rgseqssVal[0][nIndex + sStruct.rgseqssVal[0].size() / 2]); + for (size_t nIndex = 0; nIndex < sStruct.rgseqssVal[1].size() / 3; nIndex++) + EXPECT_EQ(sStruct.rgseqssVal[1][nIndex], sStruct.rgseqssVal[1][nIndex + sStruct.rgseqssVal[1].size() / 3]); + EXPECT_EQ(sStruct.rgseq8FixVal[0].size(), 20); + EXPECT_EQ(sStruct.rgseq8FixVal[1].size(), 20); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8FixVal[0].size(); nIndex++) + EXPECT_EQ(sStruct.rgseq8FixVal[0][nIndex], static_cast(nIndex << 1)); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8FixVal[1].size(); nIndex++) + EXPECT_EQ(sStruct.rgseq8FixVal[1][nIndex], static_cast(nIndex << 1)); + EXPECT_EQ(sStruct.rgseqssFixVal[0].size(), 5); + EXPECT_EQ(sStruct.rgseqssFixVal[1].size(), 5); + EXPECT_EQ(sStruct.rgseqssFixVal[0][0], "hihi"); + EXPECT_EQ(sStruct.rgseqssFixVal[1][0], "ohhiohhi"); + EXPECT_EQ(sStruct.rgseqssFixVal[0][1], "hoihoi"); + EXPECT_EQ(sStruct.rgseqssFixVal[1][1], "ohhoiohhoi"); + EXPECT_EQ(sStruct.rgseqssFixVal[0][2], "hellohello"); + EXPECT_EQ(sStruct.rgseqssFixVal[1][2], "ohhelloohhello"); + EXPECT_EQ(sStruct.rgseqssFixVal[0][3], "hallohallo"); + EXPECT_EQ(sStruct.rgseqssFixVal[1][3], "ohhalloohhallo"); + EXPECT_EQ(sStruct.rgseqssFixVal[0][4], "servusservus"); + EXPECT_EQ(sStruct.rgseqssFixVal[1][4], "ohservusohservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructInterfaceArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + + //sStruct.rgidVal[0] = sdv::GetInterfaceId(); + //sStruct.rgidVal[1] = sdv::GetInterfaceId(); + //sStruct.rgifcVal[0] = static_cast(&sMultiply); + //sStruct.rgifcVal[1] = static_cast(&sMultiply); + //sStruct.rgpMultiplyValue[0] = &sMultiply; + //sStruct.rgpMultiplyValue[1] = &sMultiply; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + //EXPECT_EQ(sStruct.rgidVal[0], sdv::GetInterfaceId()); + //EXPECT_EQ(sStruct.rgidVal[1], sdv::GetInterfaceId()); + //EXPECT_EQ(sStruct.rgifcVal[0].id(), sdv::GetInterfaceId()); + //EXPECT_EQ(sStruct.rgifcVal[1].id(), sdv::GetInterfaceId()); + //ASSERT_NE(sStruct.rgifcVal[0], nullptr); + //ASSERT_NE(sStruct.rgifcVal[1], nullptr); + //ASSERT_NE(sStruct.rgifcVal[0].get(), nullptr); + //ASSERT_NE(sStruct.rgifcVal[1].get(), nullptr); + //EXPECT_EQ(sStruct.rgifcVal[0].get()->Add(99), 19602); // 99 * 99 + 99 * 99 + //EXPECT_EQ(sStruct.rgifcVal[1].get()->Add(99), 19602); // 99 * 99 + 99 * 99 + //ASSERT_NE(sStruct.rgpMultiplyValue[0], nullptr); + //ASSERT_NE(sStruct.rgpMultiplyValue[1], nullptr); + //EXPECT_EQ(sStruct.rgpMultiplyValue[0]->Multiply(5), 625); // 5 * 5 * 5 * 5 + //EXPECT_EQ(sStruct.rgpMultiplyValue[1]->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructSubCompoundArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgsSubVal[0].i = 1; + sStruct.rgsSubVal[1].i = 2; + // Incompatible serdes generation: BUG #398509 + //sStruct.rgsIndVal[0].i = 10; + //sStruct.rgsIndVal[1].i = 20; + // Unnamed struct with array is not yet supported due to incorret serdes code generation: BUG #398246 + //sStruct.rgsUnnamedVal[0].i = 100; + //sStruct.rgsUnnamedVal[1].i = 200; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgsSubVal[0].i, 2); + EXPECT_EQ(sStruct.rgsSubVal[1].i, 4); + //EXPECT_EQ(sStruct.rgsIndVal[0].i, 20); + //EXPECT_EQ(sStruct.rgsIndVal[1].i, 40); + //EXPECT_EQ(sStruct.rgsUnnamedVal[0].i, 120); + //EXPECT_EQ(sStruct.rgsUnnamedVal[1].i, 240); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructBooleanTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.tbVal = false; + EXPECT_FALSE(sStruct.tbVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_TRUE(sStruct.tbVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructIntegralTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.tsVal = -10; + sStruct.tusVal = 10; + sStruct.tlVal = -20; + sStruct.tulVal = 20; + sStruct.tllVal = -30; + sStruct.tullVal = 30; + sStruct.ti8Val = -40; + sStruct.tui8Val = 40; + sStruct.ti16Val = -50; + sStruct.tui16Val = 50; + sStruct.ti32Val = -60; + sStruct.tui32Val = 60; + sStruct.ti64Val = -70; + sStruct.tui64Val = 70; + sStruct.tnVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tsVal, -11); + EXPECT_EQ(sStruct.tusVal, 11u); + EXPECT_EQ(sStruct.tlVal, -21); + EXPECT_EQ(sStruct.tulVal, 21u); + EXPECT_EQ(sStruct.tllVal, -31); + EXPECT_EQ(sStruct.tullVal, 31u); + EXPECT_EQ(static_cast(sStruct.ti8Val), static_cast(-41)); // Cast needed for ARM compatibility + EXPECT_EQ(sStruct.tui8Val, 41u); + EXPECT_EQ(sStruct.ti16Val, -51); + EXPECT_EQ(sStruct.tui16Val, 51u); + EXPECT_EQ(sStruct.ti32Val, -61); + EXPECT_EQ(sStruct.tui32Val, 61u); + EXPECT_EQ(sStruct.ti64Val, -71); + EXPECT_EQ(sStruct.tui64Val, 71u); + EXPECT_EQ(sStruct.tnVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructCharacterTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.tcVal = 'A'; + sStruct.tc16Val = u'D'; + sStruct.tc32Val = U'G'; + sStruct.twcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tcVal, 'B'); + EXPECT_EQ(sStruct.tc16Val, u'E'); + EXPECT_EQ(sStruct.tc32Val, U'H'); + EXPECT_EQ(sStruct.twcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructFloatingPointTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + float fVal = -1234.5678f; + sStruct.tfVal = fVal; + double dVal = 8765.4321; + sStruct.tdVal = dVal; + long double ldVal = -1234.4321; + sStruct.tldVal = ldVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tfVal, fVal - 1000.000f); + EXPECT_EQ(sStruct.tdVal, dVal + 1000.000); + EXPECT_EQ(sStruct.tldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructEnumTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.teHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.teHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructStringTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.tssVal = "hi"; + sStruct.tss8Val = "huhu"; + sStruct.tss16Val = u"hey"; + sStruct.tss32Val = U"hello"; + sStruct.twssVal = L"servus"; + sStruct.tssFixVal = "hi"; + sStruct.tss8FixVal = "huhu"; + sStruct.tss16FixVal = u"hey"; + sStruct.tss32FixVal = U"hello"; + sStruct.twssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tssVal, "hij"); + EXPECT_EQ(sStruct.tss8Val, "huhuv"); + EXPECT_EQ(sStruct.tss16Val, u"heyz"); + EXPECT_EQ(sStruct.tss32Val, U"hellop"); + EXPECT_EQ(sStruct.twssVal, L"servust"); + EXPECT_EQ(sStruct.tssFixVal, "hij"); + EXPECT_EQ(sStruct.tss8FixVal, "huhuv"); + EXPECT_EQ(sStruct.tss16FixVal, u"heyz"); + EXPECT_EQ(sStruct.tss32FixVal, U"hellop"); + EXPECT_EQ(sStruct.twssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS, DISABLED_MarshallMegaStructPointerTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + //sStruct.tptr8Val.resize(100); + //for (size_t nIndex = 0; nIndex < sStruct.tptr8Val.size(); nIndex++) + // sStruct.tptr8Val[nIndex] = static_cast(nIndex); + //sStruct.tptrssVal.resize(3); + //sStruct.tptrssVal[0] = "hello"; + //sStruct.tptrssVal[1] = "huhu"; + //sStruct.tptrssVal[2] = "hi"; + //sStruct.tptr8FixVal.resize(sStruct.tptr8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < sStruct.tptr8FixVal.size(); nIndex++) + // sStruct.tptr8FixVal[nIndex] = static_cast(nIndex); + //sStruct.tptrssFixVal.resize(sStruct.tptrssFixVal.capacity()); + //sStruct.tptrssFixVal[0] = "hi"; + //sStruct.tptrssFixVal[1] = "hoi"; + //sStruct.tptrssFixVal[2] = "hello"; + //sStruct.tptrssFixVal[3] = "hallo"; + //sStruct.tptrssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + //EXPECT_EQ(sStruct.tptr8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < sStruct.tptr8Val.size() / 2; nIndex++) + // EXPECT_EQ(sStruct.tptr8Val[nIndex], sStruct.tptr8Val[nIndex + sStruct.tptr8Val.size() / 2]); + //EXPECT_EQ(sStruct.tptrssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < sStruct.tptrssVal.size() / 2; nIndex++) + // EXPECT_EQ(sStruct.tptrssVal[nIndex], sStruct.tptrssVal[nIndex + sStruct.tptrssVal.size() / 2]); + //EXPECT_EQ(sStruct.tptr8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < sStruct.tptr8FixVal.size(); nIndex++) + // EXPECT_EQ(sStruct.tptr8FixVal[nIndex], static_cast(nIndex << 1)); + //EXPECT_EQ(sStruct.tptrssFixVal.size(), 5); + //EXPECT_EQ(sStruct.tptrssFixVal[0], "hihi"); + //EXPECT_EQ(sStruct.tptrssFixVal[1], "hoihoi"); + //EXPECT_EQ(sStruct.tptrssFixVal[2], "hellohello"); + //EXPECT_EQ(sStruct.tptrssFixVal[3], "hallohallo"); + //EXPECT_EQ(sStruct.tptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS, DISABLED_MarshallMegaStructSequenceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + //sStruct.tseq8Val.resize(100); + //for (size_t nIndex = 0; nIndex < sStruct.tseq8Val.size(); nIndex++) + // sStruct.tseq8Val[nIndex] = static_cast(nIndex); + //sStruct.tseqssVal.resize(3); + //sStruct.tseqssVal[0] = "hello"; + //sStruct.tseqssVal[1] = "huhu"; + //sStruct.tseqssVal[2] = "hi"; + //sStruct.tseq8FixVal.resize(sStruct.tseq8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < sStruct.tseq8FixVal.size(); nIndex++) + // sStruct.tseq8FixVal[nIndex] = static_cast(nIndex); + //sStruct.tseqssFixVal.resize(sStruct.tseqssFixVal.capacity()); + //sStruct.tseqssFixVal[0] = "hi"; + //sStruct.tseqssFixVal[1] = "hoi"; + //sStruct.tseqssFixVal[2] = "hello"; + //sStruct.tseqssFixVal[3] = "hallo"; + //sStruct.tseqssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + //EXPECT_EQ(sStruct.tseq8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < sStruct.tseq8Val.size() / 2; nIndex++) + // EXPECT_EQ(sStruct.tseq8Val[nIndex], sStruct.tseq8Val[nIndex + sStruct.tseq8Val.size() / 2]); + //EXPECT_EQ(sStruct.tseqssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < sStruct.tseqssVal.size() / 2; nIndex++) + // EXPECT_EQ(sStruct.tseqssVal[nIndex], sStruct.tseqssVal[nIndex + sStruct.tseqssVal.size() / 2]); + //EXPECT_EQ(sStruct.tseq8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < sStruct.tseq8FixVal.size(); nIndex++) + // EXPECT_EQ(sStruct.tseq8FixVal[nIndex], static_cast(nIndex << 1)); + //EXPECT_EQ(sStruct.tseqssFixVal.size(), 5); + //EXPECT_EQ(sStruct.tseqssFixVal[0], "hihi"); + //EXPECT_EQ(sStruct.tseqssFixVal[1], "hoihoi"); + //EXPECT_EQ(sStruct.tseqssFixVal[2], "hellohello"); + //EXPECT_EQ(sStruct.tseqssFixVal[3], "hallohallo"); + //EXPECT_EQ(sStruct.tseqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructInterfaceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + + sStruct.tidVal = sdv::GetInterfaceId(); + sStruct.tifcVal = static_cast(&sMultiply); + // Typedef of interfaces current not possible. BUG #399464 + //sStruct.ptMultiplyValue = &sMultiply; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tidVal, sdv::GetInterfaceId()); + EXPECT_EQ(sStruct.tifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(sStruct.tifcVal, nullptr); + ASSERT_NE(sStruct.tifcVal.get(), nullptr); + EXPECT_EQ(sStruct.tifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + // Typedef of interfaces current not possible. BUG #399464 + //ASSERT_NE(sStruct.tpMultiplyValue, nullptr); + //EXPECT_EQ(sStruct.tpMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaStructSubCompoundTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.tsSubVal.i = 1; + sStruct.tsIndVal.i = 10; + // Incompatible serdes generation: BUG #398509 + //sStruct.tsUnnamedVal.i = 100; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tsSubVal.i, 2); + EXPECT_EQ(sStruct.tsIndVal.i, 20); + // Incompatible serdes generation: BUG #398509 + //EXPECT_EQ(sStruct.tsUnnamedVal.i, 120); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionBoolean) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(1); + uUnion.bVal = false; + EXPECT_FALSE(uUnion.bVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_TRUE(uUnion.bVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionIntegral) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(2); + uUnion.sVal = -10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.sVal, -11); + uUnion.switch_to(3); + uUnion.usVal = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.usVal, 11u); + uUnion.switch_to(4); + uUnion.lVal = -20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.lVal, -21); + uUnion.switch_to(5); + uUnion.ulVal = 20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ulVal, 21u); + uUnion.switch_to(6); + uUnion.llVal = -30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.llVal, -31); + uUnion.switch_to(7); + uUnion.ullVal = 30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ullVal, 31u); + uUnion.switch_to(8); + uUnion.i8Val = -40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(static_cast(uUnion.i8Val), static_cast(-41)); // Cast needed for ARM compatibility + uUnion.switch_to(9); + uUnion.ui8Val = 40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ui8Val, 41u); + uUnion.switch_to(10); + uUnion.i16Val = -50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.i16Val, -51); + uUnion.switch_to(11); + uUnion.ui16Val = 50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ui16Val, 51u); + uUnion.switch_to(12); + uUnion.i32Val = -60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.i32Val, -61); + uUnion.switch_to(13); + uUnion.ui32Val = 60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ui32Val, 61u); + uUnion.switch_to(14); + uUnion.i64Val = -70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.i64Val, -71); + uUnion.switch_to(15); + uUnion.ui64Val = 70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ui64Val, 71u); + uUnion.switch_to(23); + uUnion.nVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.nVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionCharacter) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(16); + uUnion.cVal = 'A'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.cVal, 'B'); + uUnion.switch_to(17); + uUnion.c16Val = u'D'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.c16Val, u'E'); + uUnion.switch_to(18); + uUnion.c32Val = U'G'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.c32Val, U'H'); + uUnion.switch_to(19); + uUnion.wcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.wcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionFloatingPoint) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(20); + float fVal = -1234.5678f; + uUnion.fVal = fVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.fVal, fVal - 1000.000f); + uUnion.switch_to(21); + double dVal = 8765.4321; + uUnion.dVal = dVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.dVal, dVal + 1000.000); + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + //uUnion.switch_to(22); + //long double ldVal = -1234.4321; + //uUnion.ldVal = ldVal; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.ldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionEnum) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(24); + uUnion.eHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.eHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionString) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(26); + uUnion.ssVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ssVal, "hij"); + uUnion.switch_to(27); + uUnion.ss8Val = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss8Val, "huhuv"); + uUnion.switch_to(28); + uUnion.ss16Val = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss16Val, u"heyz"); + uUnion.switch_to(29); + uUnion.ss32Val = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss32Val, U"hellop"); + uUnion.switch_to(30); + uUnion.wssVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.wssVal, L"servust"); + uUnion.switch_to(31); + uUnion.ssFixVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ssFixVal, "hij"); + uUnion.switch_to(32); + uUnion.ss8FixVal = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss8FixVal, "huhuv"); + uUnion.switch_to(33); + uUnion.ss16FixVal = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss16FixVal, u"heyz"); + uUnion.switch_to(34); + uUnion.ss32FixVal = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss32FixVal, U"hellop"); + uUnion.switch_to(35); + uUnion.wssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.wssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionPointer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(36); + uUnion.ptr8Val.resize(100); + for (size_t nIndex = 0; nIndex < uUnion.ptr8Val.size(); nIndex++) + uUnion.ptr8Val[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ptr8Val.size(), 200); + for (size_t nIndex = 0; nIndex < uUnion.ptr8Val.size() / 2; nIndex++) + EXPECT_EQ(uUnion.ptr8Val[nIndex], uUnion.ptr8Val[nIndex + uUnion.ptr8Val.size() / 2]); + uUnion.switch_to(37); + uUnion.ptrssVal.resize(3); + uUnion.ptrssVal[0] = "hello"; + uUnion.ptrssVal[1] = "huhu"; + uUnion.ptrssVal[2] = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ptrssVal.size(), 6); + for (size_t nIndex = 0; nIndex < uUnion.ptrssVal.size() / 2; nIndex++) + EXPECT_EQ(uUnion.ptrssVal[nIndex], uUnion.ptrssVal[nIndex + uUnion.ptrssVal.size() / 2]); + uUnion.switch_to(38); + uUnion.ptr8FixVal.resize(uUnion.ptr8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < uUnion.ptr8FixVal.size(); nIndex++) + uUnion.ptr8FixVal[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ptr8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < uUnion.ptr8FixVal.size(); nIndex++) + EXPECT_EQ(uUnion.ptr8FixVal[nIndex], static_cast(nIndex << 1)); + uUnion.switch_to(39); + uUnion.ptrssFixVal.resize(uUnion.ptrssFixVal.capacity()); + uUnion.ptrssFixVal[0] = "hi"; + uUnion.ptrssFixVal[1] = "hoi"; + uUnion.ptrssFixVal[2] = "hello"; + uUnion.ptrssFixVal[3] = "hallo"; + uUnion.ptrssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ptrssFixVal.size(), 5); + EXPECT_EQ(uUnion.ptrssFixVal[0], "hihi"); + EXPECT_EQ(uUnion.ptrssFixVal[1], "hoihoi"); + EXPECT_EQ(uUnion.ptrssFixVal[2], "hellohello"); + EXPECT_EQ(uUnion.ptrssFixVal[3], "hallohallo"); + EXPECT_EQ(uUnion.ptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionSequence) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(36); + uUnion.seq8Val.resize(100); + for (size_t nIndex = 0; nIndex < uUnion.seq8Val.size(); nIndex++) + uUnion.seq8Val[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.seq8Val.size(), 200); + for (size_t nIndex = 0; nIndex < uUnion.seq8Val.size() / 2; nIndex++) + EXPECT_EQ(uUnion.seq8Val[nIndex], uUnion.seq8Val[nIndex + uUnion.seq8Val.size() / 2]); + uUnion.switch_to(37); + uUnion.seqssVal.resize(3); + uUnion.seqssVal[0] = "hello"; + uUnion.seqssVal[1] = "huhu"; + uUnion.seqssVal[2] = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.seqssVal.size(), 6); + for (size_t nIndex = 0; nIndex < uUnion.seqssVal.size() / 2; nIndex++) + EXPECT_EQ(uUnion.seqssVal[nIndex], uUnion.seqssVal[nIndex + uUnion.seqssVal.size() / 2]); + uUnion.switch_to(38); + uUnion.seq8FixVal.resize(uUnion.seq8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < uUnion.seq8FixVal.size(); nIndex++) + uUnion.seq8FixVal[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.seq8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < uUnion.seq8FixVal.size(); nIndex++) + EXPECT_EQ(uUnion.seq8FixVal[nIndex], static_cast(nIndex << 1)); + uUnion.switch_to(39); + uUnion.seqssFixVal.resize(uUnion.seqssFixVal.capacity()); + uUnion.seqssFixVal[0] = "hi"; + uUnion.seqssFixVal[1] = "hoi"; + uUnion.seqssFixVal[2] = "hello"; + uUnion.seqssFixVal[3] = "hallo"; + uUnion.seqssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.seqssFixVal.size(), 5); + EXPECT_EQ(uUnion.seqssFixVal[0], "hihi"); + EXPECT_EQ(uUnion.seqssFixVal[1], "hoihoi"); + EXPECT_EQ(uUnion.seqssFixVal[2], "hellohello"); + EXPECT_EQ(uUnion.seqssFixVal[3], "hallohallo"); + EXPECT_EQ(uUnion.seqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionInterface) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(25); + + uUnion.idVal = sdv::GetInterfaceId(); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.idVal, sdv::GetInterfaceId()); + uUnion.switch_to(44); + uUnion.ifcVal = static_cast(&sMultiply); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(uUnion.ifcVal, nullptr); + ASSERT_NE(uUnion.ifcVal.get(), nullptr); + EXPECT_EQ(uUnion.ifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + uUnion.switch_to(45); + uUnion.pMultiplyValue = &sMultiply; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + ASSERT_NE(uUnion.pMultiplyValue, nullptr); + EXPECT_EQ(uUnion.pMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionSubCompound) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(46); + uUnion.sSubVal.i = 1; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.sSubVal.i, 2); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionBooleanTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(201); + uUnion.tbVal = false; + EXPECT_FALSE(uUnion.tbVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_TRUE(uUnion.tbVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionIntegralTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(202); + uUnion.tsVal = -10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tsVal, -11); + uUnion.switch_to(203); + uUnion.tusVal = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tusVal, 11u); + uUnion.switch_to(204); + uUnion.tlVal = -20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tlVal, -21); + uUnion.switch_to(205); + uUnion.tulVal = 20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tulVal, 21u); + uUnion.switch_to(206); + uUnion.tllVal = -30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tllVal, -31); + uUnion.switch_to(207); + uUnion.tullVal = 30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tullVal, 31u); + uUnion.switch_to(208); + uUnion.ti8Val = -40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(static_cast(uUnion.ti8Val), static_cast(-41)); // Cast needed for ARM compatibility + uUnion.switch_to(209); + uUnion.tui8Val = 40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tui8Val, 41u); + uUnion.switch_to(210); + uUnion.ti16Val = -50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ti16Val, -51); + uUnion.switch_to(211); + uUnion.tui16Val = 50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tui16Val, 51u); + uUnion.switch_to(212); + uUnion.ti32Val = -60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ti32Val, -61); + uUnion.switch_to(213); + uUnion.tui32Val = 60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tui32Val, 61u); + uUnion.switch_to(214); + uUnion.ti64Val = -70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ti64Val, -71); + uUnion.switch_to(215); + uUnion.tui64Val = 70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tui64Val, 71u); + uUnion.switch_to(223); + uUnion.tnVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tnVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionCharacterTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(216); + uUnion.tcVal = 'A'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tcVal, 'B'); + uUnion.switch_to(217); + uUnion.tc16Val = u'D'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tc16Val, u'E'); + uUnion.switch_to(218); + uUnion.tc32Val = U'G'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tc32Val, U'H'); + uUnion.switch_to(219); + uUnion.twcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.twcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionFloatingPointTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(220); + float fVal = -1234.5678f; + uUnion.tfVal = fVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tfVal, fVal - 1000.000f); + uUnion.switch_to(221); + double dVal = 8765.4321; + uUnion.tdVal = dVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tdVal, dVal + 1000.000); + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + //uUnion.switch_to(222); + //long double ldVal = -1234.4321; + //uUnion.tldVal = ldVal; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionEnumTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(224); + uUnion.teHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.teHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionStringTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(226); + uUnion.tssVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tssVal, "hij"); + uUnion.switch_to(227); + uUnion.tss8Val = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss8Val, "huhuv"); + uUnion.switch_to(228); + uUnion.tss16Val = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss16Val, u"heyz"); + uUnion.switch_to(229); + uUnion.tss32Val = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss32Val, U"hellop"); + uUnion.switch_to(230); + uUnion.twssVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.twssVal, L"servust"); + uUnion.switch_to(231); + uUnion.tssFixVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tssFixVal, "hij"); + uUnion.switch_to(232); + uUnion.tss8FixVal = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss8FixVal, "huhuv"); + uUnion.switch_to(233); + uUnion.tss16FixVal = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss16FixVal, u"heyz"); + uUnion.switch_to(234); + uUnion.tss32FixVal = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss32FixVal, U"hellop"); + uUnion.switch_to(235); + uUnion.twssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.twssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS, DISABLED_MarshallMegaTypeBasedUnionPointerTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(236); + //uUnion.tptr8Val.resize(100); + //for (size_t nIndex = 0; nIndex < uUnion.tptr8Val.size(); nIndex++) + // uUnion.tptr8Val[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tptr8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < uUnion.tptr8Val.size() / 2; nIndex++) + // EXPECT_EQ(uUnion.tptr8Val[nIndex], uUnion.tptr8Val[nIndex + uUnion.tptr8Val.size() / 2]); + //uUnion.switch_to(237); + //uUnion.tptrssVal.resize(3); + //uUnion.tptrssVal[0] = "hello"; + //uUnion.tptrssVal[1] = "huhu"; + //uUnion.tptrssVal[2] = "hi"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tptrssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < uUnion.tptrssVal.size() / 2; nIndex++) + // EXPECT_EQ(uUnion.tptrssVal[nIndex], uUnion.tptrssVal[nIndex + uUnion.tptrssVal.size() / 2]); + //uUnion.switch_to(238); + //uUnion.tptr8FixVal.resize(uUnion.tptr8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < uUnion.tptr8FixVal.size(); nIndex++) + // uUnion.tptr8FixVal[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tptr8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < uUnion.tptr8FixVal.size(); nIndex++) + // EXPECT_EQ(uUnion.tptr8FixVal[nIndex], static_cast(nIndex << 1)); + //uUnion.switch_to(239); + //uUnion.tptrssFixVal.resize(uUnion.tptrssFixVal.capacity()); + //uUnion.tptrssFixVal[0] = "hi"; + //uUnion.tptrssFixVal[1] = "hoi"; + //uUnion.tptrssFixVal[2] = "hello"; + //uUnion.tptrssFixVal[3] = "hallo"; + //uUnion.tptrssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tptrssFixVal.size(), 5); + //EXPECT_EQ(uUnion.tptrssFixVal[0], "hihi"); + //EXPECT_EQ(uUnion.tptrssFixVal[1], "hoihoi"); + //EXPECT_EQ(uUnion.tptrssFixVal[2], "hellohello"); + //EXPECT_EQ(uUnion.tptrssFixVal[3], "hallohallo"); + //EXPECT_EQ(uUnion.tptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS, DISABLED_MarshallMegaTypeBasedUnionSequenceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(236); + //uUnion.tseq8Val.resize(100); + //for (size_t nIndex = 0; nIndex < uUnion.tseq8Val.size(); nIndex++) + // uUnion.tseq8Val[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tseq8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < uUnion.tseq8Val.size() / 2; nIndex++) + // EXPECT_EQ(uUnion.tseq8Val[nIndex], uUnion.tseq8Val[nIndex + uUnion.tseq8Val.size() / 2]); + //uUnion.switch_to(237); + //uUnion.tseqssVal.resize(3); + //uUnion.tseqssVal[0] = "hello"; + //uUnion.tseqssVal[1] = "huhu"; + //uUnion.tseqssVal[2] = "hi"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tseqssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < uUnion.tseqssVal.size() / 2; nIndex++) + // EXPECT_EQ(uUnion.tseqssVal[nIndex], uUnion.tseqssVal[nIndex + uUnion.tseqssVal.size() / 2]); + //uUnion.switch_to(238); + //uUnion.tseq8FixVal.resize(uUnion.tseq8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < uUnion.tseq8FixVal.size(); nIndex++) + // uUnion.tseq8FixVal[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tseq8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < uUnion.tseq8FixVal.size(); nIndex++) + // EXPECT_EQ(uUnion.tseq8FixVal[nIndex], static_cast(nIndex << 1)); + //uUnion.switch_to(239); + //uUnion.tseqssFixVal.resize(uUnion.tseqssFixVal.capacity()); + //uUnion.tseqssFixVal[0] = "hi"; + //uUnion.tseqssFixVal[1] = "hoi"; + //uUnion.tseqssFixVal[2] = "hello"; + //uUnion.tseqssFixVal[3] = "hallo"; + //uUnion.tseqssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tseqssFixVal.size(), 5); + //EXPECT_EQ(uUnion.tseqssFixVal[0], "hihi"); + //EXPECT_EQ(uUnion.tseqssFixVal[1], "hoihoi"); + //EXPECT_EQ(uUnion.tseqssFixVal[2], "hellohello"); + //EXPECT_EQ(uUnion.tseqssFixVal[3], "hallohallo"); + //EXPECT_EQ(uUnion.tseqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionInterfaceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(225); + + uUnion.tidVal = sdv::GetInterfaceId(); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tidVal, sdv::GetInterfaceId()); + uUnion.switch_to(244); + uUnion.tifcVal = static_cast(&sMultiply); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(uUnion.tifcVal, nullptr); + ASSERT_NE(uUnion.tifcVal.get(), nullptr); + EXPECT_EQ(uUnion.tifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + // Typedef of interfaces current not possible. BUG #399464 + //uUnion.switch_to(245); + //uUnion.tpMultiplyValue = &sMultiply; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //ASSERT_NE(uUnion.tpMultiplyValue, nullptr); + //EXPECT_EQ(uUnion.tpMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaTypeBasedUnionSubCompoundTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(246); + uUnion.tsSubVal.i = 1; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tsSubVal.i, 2); + uUnion.switch_to(247); + uUnion.tsIndVal.i = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tsIndVal.i, 20); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionBoolean) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(1); + sUnion.uVal.bVal = false; + EXPECT_FALSE(sUnion.uVal.bVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_TRUE(sUnion.uVal.bVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionIntegral) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(2); + sUnion.uVal.sVal = -10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.sVal, -11); + sUnion.switch_to(3); + sUnion.uVal.usVal = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.usVal, 11u); + sUnion.switch_to(4); + sUnion.uVal.lVal = -20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.lVal, -21); + sUnion.switch_to(5); + sUnion.uVal.ulVal = 20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ulVal, 21u); + sUnion.switch_to(6); + sUnion.uVal.llVal = -30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.llVal, -31); + sUnion.switch_to(7); + sUnion.uVal.ullVal = 30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ullVal, 31u); + sUnion.switch_to(8); + sUnion.uVal.i8Val = -40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(static_cast(sUnion.uVal.i8Val), static_cast(-41)); // Cast needed for ARM compatibility + sUnion.switch_to(9); + sUnion.uVal.ui8Val = 40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ui8Val, 41u); + sUnion.switch_to(10); + sUnion.uVal.i16Val = -50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.i16Val, -51); + sUnion.switch_to(11); + sUnion.uVal.ui16Val = 50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ui16Val, 51u); + sUnion.switch_to(12); + sUnion.uVal.i32Val = -60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.i32Val, -61); + sUnion.switch_to(13); + sUnion.uVal.ui32Val = 60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ui32Val, 61u); + sUnion.switch_to(14); + sUnion.uVal.i64Val = -70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.i64Val, -71); + sUnion.switch_to(15); + sUnion.uVal.ui64Val = 70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ui64Val, 71u); + sUnion.switch_to(23); + sUnion.uVal.nVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.nVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionCharacter) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(16); + sUnion.uVal.cVal = 'A'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.cVal, 'B'); + sUnion.switch_to(17); + sUnion.uVal.c16Val = u'D'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.c16Val, u'E'); + sUnion.switch_to(18); + sUnion.uVal.c32Val = U'G'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.c32Val, U'H'); + sUnion.switch_to(19); + sUnion.uVal.wcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.wcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionFloatingPoint) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(20); + float fVal = -1234.5678f; + sUnion.uVal.fVal = fVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.fVal, fVal - 1000.000f); + sUnion.switch_to(21); + double dVal = 8765.4321; + sUnion.uVal.dVal = dVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.dVal, dVal + 1000.000); + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + //sUnion.switch_to(22); + //long double ldVal = -1234.4321; + //sUnion.uVal.ldVal = ldVal; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.ldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionEnum) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(24); + sUnion.uVal.eHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.eHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionString) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(26); + sUnion.uVal.ssVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ssVal, "hij"); + sUnion.switch_to(27); + sUnion.uVal.ss8Val = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss8Val, "huhuv"); + sUnion.switch_to(28); + sUnion.uVal.ss16Val = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss16Val, u"heyz"); + sUnion.switch_to(29); + sUnion.uVal.ss32Val = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss32Val, U"hellop"); + sUnion.switch_to(30); + sUnion.uVal.wssVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.wssVal, L"servust"); + sUnion.switch_to(31); + sUnion.uVal.ssFixVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ssFixVal, "hij"); + sUnion.switch_to(32); + sUnion.uVal.ss8FixVal = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss8FixVal, "huhuv"); + sUnion.switch_to(33); + sUnion.uVal.ss16FixVal = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss16FixVal, u"heyz"); + sUnion.switch_to(34); + sUnion.uVal.ss32FixVal = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss32FixVal, U"hellop"); + sUnion.switch_to(35); + sUnion.uVal.wssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.wssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionPointer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(36); + sUnion.uVal.ptr8Val.resize(100); + for (size_t nIndex = 0; nIndex < sUnion.uVal.ptr8Val.size(); nIndex++) + sUnion.uVal.ptr8Val[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ptr8Val.size(), 200); + for (size_t nIndex = 0; nIndex < sUnion.uVal.ptr8Val.size() / 2; nIndex++) + EXPECT_EQ(sUnion.uVal.ptr8Val[nIndex], sUnion.uVal.ptr8Val[nIndex + sUnion.uVal.ptr8Val.size() / 2]); + sUnion.switch_to(37); + sUnion.uVal.ptrssVal.resize(3); + sUnion.uVal.ptrssVal[0] = "hello"; + sUnion.uVal.ptrssVal[1] = "huhu"; + sUnion.uVal.ptrssVal[2] = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ptrssVal.size(), 6); + for (size_t nIndex = 0; nIndex < sUnion.uVal.ptrssVal.size() / 2; nIndex++) + EXPECT_EQ(sUnion.uVal.ptrssVal[nIndex], sUnion.uVal.ptrssVal[nIndex + sUnion.uVal.ptrssVal.size() / 2]); + sUnion.switch_to(38); + sUnion.uVal.ptr8FixVal.resize(sUnion.uVal.ptr8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < sUnion.uVal.ptr8FixVal.size(); nIndex++) + sUnion.uVal.ptr8FixVal[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ptr8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < sUnion.uVal.ptr8FixVal.size(); nIndex++) + EXPECT_EQ(sUnion.uVal.ptr8FixVal[nIndex], static_cast(nIndex << 1)); + sUnion.switch_to(39); + sUnion.uVal.ptrssFixVal.resize(sUnion.uVal.ptrssFixVal.capacity()); + sUnion.uVal.ptrssFixVal[0] = "hi"; + sUnion.uVal.ptrssFixVal[1] = "hoi"; + sUnion.uVal.ptrssFixVal[2] = "hello"; + sUnion.uVal.ptrssFixVal[3] = "hallo"; + sUnion.uVal.ptrssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ptrssFixVal.size(), 5); + EXPECT_EQ(sUnion.uVal.ptrssFixVal[0], "hihi"); + EXPECT_EQ(sUnion.uVal.ptrssFixVal[1], "hoihoi"); + EXPECT_EQ(sUnion.uVal.ptrssFixVal[2], "hellohello"); + EXPECT_EQ(sUnion.uVal.ptrssFixVal[3], "hallohallo"); + EXPECT_EQ(sUnion.uVal.ptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionSequence) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(36); + sUnion.uVal.seq8Val.resize(100); + for (size_t nIndex = 0; nIndex < sUnion.uVal.seq8Val.size(); nIndex++) + sUnion.uVal.seq8Val[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.seq8Val.size(), 200); + for (size_t nIndex = 0; nIndex < sUnion.uVal.seq8Val.size() / 2; nIndex++) + EXPECT_EQ(sUnion.uVal.seq8Val[nIndex], sUnion.uVal.seq8Val[nIndex + sUnion.uVal.seq8Val.size() / 2]); + sUnion.switch_to(37); + sUnion.uVal.seqssVal.resize(3); + sUnion.uVal.seqssVal[0] = "hello"; + sUnion.uVal.seqssVal[1] = "huhu"; + sUnion.uVal.seqssVal[2] = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.seqssVal.size(), 6); + for (size_t nIndex = 0; nIndex < sUnion.uVal.seqssVal.size() / 2; nIndex++) + EXPECT_EQ(sUnion.uVal.seqssVal[nIndex], sUnion.uVal.seqssVal[nIndex + sUnion.uVal.seqssVal.size() / 2]); + sUnion.switch_to(38); + sUnion.uVal.seq8FixVal.resize(sUnion.uVal.seq8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < sUnion.uVal.seq8FixVal.size(); nIndex++) + sUnion.uVal.seq8FixVal[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.seq8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < sUnion.uVal.seq8FixVal.size(); nIndex++) + EXPECT_EQ(sUnion.uVal.seq8FixVal[nIndex], static_cast(nIndex << 1)); + sUnion.switch_to(39); + sUnion.uVal.seqssFixVal.resize(sUnion.uVal.seqssFixVal.capacity()); + sUnion.uVal.seqssFixVal[0] = "hi"; + sUnion.uVal.seqssFixVal[1] = "hoi"; + sUnion.uVal.seqssFixVal[2] = "hello"; + sUnion.uVal.seqssFixVal[3] = "hallo"; + sUnion.uVal.seqssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.seqssFixVal.size(), 5); + EXPECT_EQ(sUnion.uVal.seqssFixVal[0], "hihi"); + EXPECT_EQ(sUnion.uVal.seqssFixVal[1], "hoihoi"); + EXPECT_EQ(sUnion.uVal.seqssFixVal[2], "hellohello"); + EXPECT_EQ(sUnion.uVal.seqssFixVal[3], "hallohallo"); + EXPECT_EQ(sUnion.uVal.seqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionInterface) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(25); + + sUnion.uVal.idVal = sdv::GetInterfaceId(); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.idVal, sdv::GetInterfaceId()); + sUnion.switch_to(44); + sUnion.uVal.ifcVal = static_cast(&sMultiply); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(sUnion.uVal.ifcVal, nullptr); + ASSERT_NE(sUnion.uVal.ifcVal.get(), nullptr); + EXPECT_EQ(sUnion.uVal.ifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + sUnion.switch_to(45); + sUnion.uVal.pMultiplyValue = &sMultiply; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + ASSERT_NE(sUnion.uVal.pMultiplyValue, nullptr); + EXPECT_EQ(sUnion.uVal.pMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionSubCompound) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(46); + sUnion.uVal.sSubVal.i = 1; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.sSubVal.i, 2); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionBooleanTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(201); + sUnion.uVal.tbVal = false; + EXPECT_FALSE(sUnion.uVal.tbVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_TRUE(sUnion.uVal.tbVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionIntegralTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(202); + sUnion.uVal.tsVal = -10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tsVal, -11); + sUnion.switch_to(203); + sUnion.uVal.tusVal = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tusVal, 11u); + sUnion.switch_to(204); + sUnion.uVal.tlVal = -20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tlVal, -21); + sUnion.switch_to(205); + sUnion.uVal.tulVal = 20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tulVal, 21u); + sUnion.switch_to(206); + sUnion.uVal.tllVal = -30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tllVal, -31); + sUnion.switch_to(207); + sUnion.uVal.tullVal = 30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tullVal, 31u); + sUnion.switch_to(208); + sUnion.uVal.ti8Val = -40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(static_cast(sUnion.uVal.ti8Val), static_cast(-41)); // Cast needed for ARM compatibility + sUnion.switch_to(209); + sUnion.uVal.tui8Val = 40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tui8Val, 41u); + sUnion.switch_to(210); + sUnion.uVal.ti16Val = -50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ti16Val, -51); + sUnion.switch_to(211); + sUnion.uVal.tui16Val = 50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tui16Val, 51u); + sUnion.switch_to(212); + sUnion.uVal.ti32Val = -60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ti32Val, -61); + sUnion.switch_to(213); + sUnion.uVal.tui32Val = 60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tui32Val, 61u); + sUnion.switch_to(214); + sUnion.uVal.ti64Val = -70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ti64Val, -71); + sUnion.switch_to(215); + sUnion.uVal.tui64Val = 70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tui64Val, 71u); + sUnion.switch_to(223); + sUnion.uVal.tnVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tnVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionCharacterTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(216); + sUnion.uVal.tcVal = 'A'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tcVal, 'B'); + sUnion.switch_to(217); + sUnion.uVal.tc16Val = u'D'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tc16Val, u'E'); + sUnion.switch_to(218); + sUnion.uVal.tc32Val = U'G'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tc32Val, U'H'); + sUnion.switch_to(219); + sUnion.uVal.twcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.twcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionFloatingPointTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(220); + float fVal = -1234.5678f; + sUnion.uVal.tfVal = fVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tfVal, fVal - 1000.000f); + sUnion.switch_to(221); + double dVal = 8765.4321; + sUnion.uVal.tdVal = dVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tdVal, dVal + 1000.000); + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + //sUnion.switch_to(222); + //long double ldVal = -1234.4321; + //sUnion.uVal.tldVal = ldVal; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionEnumTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(224); + sUnion.uVal.teHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.teHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionStringTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(226); + sUnion.uVal.tssVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tssVal, "hij"); + sUnion.switch_to(227); + sUnion.uVal.tss8Val = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss8Val, "huhuv"); + sUnion.switch_to(228); + sUnion.uVal.tss16Val = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss16Val, u"heyz"); + sUnion.switch_to(229); + sUnion.uVal.tss32Val = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss32Val, U"hellop"); + sUnion.switch_to(230); + sUnion.uVal.twssVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.twssVal, L"servust"); + sUnion.switch_to(231); + sUnion.uVal.tssFixVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tssFixVal, "hij"); + sUnion.switch_to(232); + sUnion.uVal.tss8FixVal = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss8FixVal, "huhuv"); + sUnion.switch_to(233); + sUnion.uVal.tss16FixVal = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss16FixVal, u"heyz"); + sUnion.switch_to(234); + sUnion.uVal.tss32FixVal = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss32FixVal, U"hellop"); + sUnion.switch_to(235); + sUnion.uVal.twssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.twssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS, DISABLED_MarshallMegaVarBasedUnionPointerTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(236); + //sUnion.uVal.tptr8Val.resize(100); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tptr8Val.size(); nIndex++) + // sUnion.uVal.tptr8Val[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tptr8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tptr8Val.size() / 2; nIndex++) + // EXPECT_EQ(sUnion.uVal.tptr8Val[nIndex], sUnion.uVal.tptr8Val[nIndex + sUnion.uVal.tptr8Val.size() / 2]); + //sUnion.switch_to(237); + //sUnion.uVal.tptrssVal.resize(3); + //sUnion.uVal.tptrssVal[0] = "hello"; + //sUnion.uVal.tptrssVal[1] = "huhu"; + //sUnion.uVal.tptrssVal[2] = "hi"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tptrssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tptrssVal.size() / 2; nIndex++) + // EXPECT_EQ(sUnion.uVal.tptrssVal[nIndex], sUnion.uVal.tptrssVal[nIndex + sUnion.uVal.tptrssVal.size() / 2]); + //sUnion.switch_to(238); + //sUnion.uVal.tptr8FixVal.resize(sUnion.uVal.tptr8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tptr8FixVal.size(); nIndex++) + // sUnion.uVal.tptr8FixVal[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tptr8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tptr8FixVal.size(); nIndex++) + // EXPECT_EQ(sUnion.uVal.tptr8FixVal[nIndex], static_cast(nIndex << 1)); + //sUnion.switch_to(239); + //sUnion.uVal.tptrssFixVal.resize(sUnion.uVal.tptrssFixVal.capacity()); + //sUnion.uVal.tptrssFixVal[0] = "hi"; + //sUnion.uVal.tptrssFixVal[1] = "hoi"; + //sUnion.uVal.tptrssFixVal[2] = "hello"; + //sUnion.uVal.tptrssFixVal[3] = "hallo"; + //sUnion.uVal.tptrssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal.size(), 5); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal[0], "hihi"); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal[1], "hoihoi"); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal[2], "hellohello"); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal[3], "hallohallo"); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS, DISABLED_MarshallMegaVarBasedUnionSequenceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(236); + //sUnion.uVal.tseq8Val.resize(100); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tseq8Val.size(); nIndex++) + // sUnion.uVal.tseq8Val[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tseq8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tseq8Val.size() / 2; nIndex++) + // EXPECT_EQ(sUnion.uVal.tseq8Val[nIndex], sUnion.uVal.tseq8Val[nIndex + sUnion.uVal.tseq8Val.size() / 2]); + //sUnion.switch_to(237); + //sUnion.uVal.tseqssVal.resize(3); + //sUnion.uVal.tseqssVal[0] = "hello"; + //sUnion.uVal.tseqssVal[1] = "huhu"; + //sUnion.uVal.tseqssVal[2] = "hi"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tseqssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tseqssVal.size() / 2; nIndex++) + // EXPECT_EQ(sUnion.uVal.tseqssVal[nIndex], sUnion.uVal.tseqssVal[nIndex + sUnion.uVal.tseqssVal.size() / 2]); + //sUnion.switch_to(238); + //sUnion.uVal.tseq8FixVal.resize(sUnion.uVal.tseq8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tseq8FixVal.size(); nIndex++) + // sUnion.uVal.tseq8FixVal[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tseq8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tseq8FixVal.size(); nIndex++) + // EXPECT_EQ(sUnion.uVal.tseq8FixVal[nIndex], static_cast(nIndex << 1)); + //sUnion.switch_to(239); + //sUnion.uVal.tseqssFixVal.resize(sUnion.uVal.tseqssFixVal.capacity()); + //sUnion.uVal.tseqssFixVal[0] = "hi"; + //sUnion.uVal.tseqssFixVal[1] = "hoi"; + //sUnion.uVal.tseqssFixVal[2] = "hello"; + //sUnion.uVal.tseqssFixVal[3] = "hallo"; + //sUnion.uVal.tseqssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal.size(), 5); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal[0], "hihi"); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal[1], "hoihoi"); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal[2], "hellohello"); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal[3], "hallohallo"); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionInterfaceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(225); + + sUnion.uVal.tidVal = sdv::GetInterfaceId(); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tidVal, sdv::GetInterfaceId()); + sUnion.switch_to(244); + sUnion.uVal.tifcVal = static_cast(&sMultiply); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(sUnion.uVal.tifcVal, nullptr); + ASSERT_NE(sUnion.uVal.tifcVal.get(), nullptr); + EXPECT_EQ(sUnion.uVal.tifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + // Typedef of interfaces current not possible. BUG #399464 + //sUnion.switch_to(245); + //sUnion.uVal.tpMultiplyValue = &sMultiply; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //ASSERT_NE(sUnion.uVal.tpMultiplyValue, nullptr); + //EXPECT_EQ(sUnion.uVal.tpMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS, MarshallMegaVarBasedUnionSubCompoundTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::sockets); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(246); + sUnion.uVal.tsSubVal.i = 1; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tsSubVal.i, 2); + sUnion.switch_to(247); + sUnion.uVal.tsIndVal.i = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tsIndVal.i, 20); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} diff --git a/tests/unit_tests/ipc_com/ipc_com_uds_tunnel.cpp b/tests/unit_tests/ipc_com/ipc_com_uds_tunnel.cpp new file mode 100644 index 0000000..b96c576 --- /dev/null +++ b/tests/unit_tests/ipc_com/ipc_com_uds_tunnel.cpp @@ -0,0 +1,5534 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + + +#include "include.h" + +namespace +{ +constexpr const char* kTunnelChannelObjectName = "DEFAULT_CHANNEL"; +constexpr const char* kTunnelEndpointConfig = "proto=tunnel;path=DEFAULT_CHANNEL;tunnel=DEFAULT_CHANNEL;"; +} + +TEST(IPC_Communication_Test_UDS_Tunnel, AssignServerEndpoint) +{ + sdv::app::CAppControl appcontrol; + + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[IpcProvider] +Name = "unix_domain_sockets_tunnel" + +[[IpcChannel]] +Name = "DEFAULT_CHANNEL" +Provider = "unix_domain_sockets_tunnel" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + control.SetOperationMode(sdv::EOperationMode::running); + + // Create a channel endpoint + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject(kTunnelChannelObjectName); + ASSERT_NE(ptrCreateEndpoint, nullptr); + + sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(kTunnelEndpointConfig); + EXPECT_NE(sChannelEndpoint.pConnection, nullptr); + EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); + EXPECT_NE(sChannelEndpoint.ssConnectString.find("proto=tunnel"), std::string::npos); + sdv::TObjectPtr ptrEndpoint = sChannelEndpoint.pConnection; // Automatic lifetime management + EXPECT_TRUE(ptrEndpoint); + + // Assign the server endpoint + CInterfaceTest test; + EXPECT_TRUE(control.AssignServerEndpoint(ptrEndpoint, &test, 100, false) != 0u); + ptrEndpoint.Clear(); // Lifetime taken over by communication control. + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, AssignClientEndpoint) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[IpcProvider] +Name = "unix_domain_sockets_tunnel" + +[[IpcChannel]] +Name = "DEFAULT_CHANNEL" +Provider = "unix_domain_sockets_tunnel" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + control.SetOperationMode(sdv::EOperationMode::running); + + // Create a server endpoint + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject(kTunnelChannelObjectName); + ASSERT_NE(ptrCreateEndpoint, nullptr); + + sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(kTunnelEndpointConfig); + EXPECT_NE(sChannelEndpoint.pConnection, nullptr); + EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); + sdv::TObjectPtr ptrServerEndpoint = sChannelEndpoint.pConnection; // Automatic lifetime management + EXPECT_TRUE(ptrServerEndpoint); + + // Create a client endpoint + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject(kTunnelChannelObjectName); + ASSERT_NE(pChannelAccess, nullptr); + sdv::TObjectPtr ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); + EXPECT_TRUE(ptrClientEndpoint); + + // Assign the server and client endpoints + CInterfaceTest test; + EXPECT_TRUE(control.AssignServerEndpoint(ptrServerEndpoint, &test, 100, false) != 0u); + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.AssignClientEndpoint(ptrClientEndpoint, 1000, pObjectProxy) != 0u); + EXPECT_NE(pObjectProxy, nullptr); + ptrServerEndpoint.Clear(); // Lifetime taken over by communication control. + ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, AssignClientEndpoint_Reconnect) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[IpcProvider] +Name = "unix_domain_sockets_tunnel" + +[[IpcChannel]] +Name = "DEFAULT_CHANNEL" +Provider = "unix_domain_sockets_tunnel" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + control.SetOperationMode(sdv::EOperationMode::running); + + // Create a server endpoint + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject(kTunnelChannelObjectName); + ASSERT_NE(ptrCreateEndpoint, nullptr); + + sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(kTunnelEndpointConfig); + EXPECT_NE(sChannelEndpoint.pConnection, nullptr); + EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); + sdv::TObjectPtr ptrServerEndpoint = sChannelEndpoint.pConnection; // Automatic lifetime management + EXPECT_TRUE(ptrServerEndpoint); + + // Create a client endpoint + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject(kTunnelChannelObjectName); + ASSERT_NE(pChannelAccess, nullptr); + sdv::TObjectPtr ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); + EXPECT_TRUE(ptrClientEndpoint); + + // Assign the server and client endpoints + CInterfaceTest test; + EXPECT_TRUE(control.AssignServerEndpoint(ptrServerEndpoint, &test, 100, true) != 0u); + ptrServerEndpoint.Clear(); // Lifetime taken over by communication control. + sdv::IInterfaceAccess* pObjectProxy = nullptr; + sdv::com::TConnectionID tConnectionID = control.AssignClientEndpoint(ptrClientEndpoint, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID != 0u); + EXPECT_NE(pObjectProxy, nullptr); + + // Disconnect from the client + sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface(); + ASSERT_NE(pClientConnect, nullptr); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::connected); + control.RemoveConnection(tConnectionID); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected); + ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. + + // Create another client endpoint + ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); + EXPECT_TRUE(ptrClientEndpoint); + + // Assign the new client endpoint + pObjectProxy = nullptr; + tConnectionID = control.AssignClientEndpoint(ptrClientEndpoint, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID != 0u); + EXPECT_NE(pObjectProxy, nullptr); + ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, AssignClientEndpoint_FailReconnect) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[IpcProvider] +Name = "unix_domain_sockets_tunnel" + +[[IpcChannel]] +Name = "DEFAULT_CHANNEL" +Provider = "unix_domain_sockets_tunnel" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + control.SetOperationMode(sdv::EOperationMode::running); + + // Create a server endpoint + sdv::ipc::ICreateEndpoint* ptrCreateEndpoint = sdv::core::GetObject(kTunnelChannelObjectName); + ASSERT_NE(ptrCreateEndpoint, nullptr); + + sdv::ipc::SChannelEndpoint sChannelEndpoint = ptrCreateEndpoint->CreateEndpoint(kTunnelEndpointConfig); + EXPECT_NE(sChannelEndpoint.pConnection, nullptr); + EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); + sdv::TObjectPtr ptrServerEndpoint = sChannelEndpoint.pConnection; // Automatic lifetime management + EXPECT_TRUE(ptrServerEndpoint); + + // Create a client endpoint + sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject(kTunnelChannelObjectName); + ASSERT_NE(pChannelAccess, nullptr); + sdv::TObjectPtr ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); + EXPECT_TRUE(ptrClientEndpoint); + + // Assign the server and client endpoints + CInterfaceTest test; + EXPECT_TRUE(control.AssignServerEndpoint(ptrServerEndpoint, &test, 100, false) != 0u); + ptrServerEndpoint.Clear(); // Lifetime taken over by communication control. + sdv::IInterfaceAccess* pObjectProxy = nullptr; + sdv::com::TConnectionID tConnectionID = control.AssignClientEndpoint(ptrClientEndpoint, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID != 0u); + EXPECT_NE(pObjectProxy, nullptr); + + // Disconnect from the client + sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface(); + ASSERT_NE(pClientConnect, nullptr); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::connected); + control.RemoveConnection(tConnectionID); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected); + ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. + + // Create another client endpoint + ptrClientEndpoint = pChannelAccess->Access(sChannelEndpoint.ssConnectString); + + // Assign the new client endpoint - this should fail. + // On tunnel backends, Access may return nullptr or succeed but AssignClientEndpoint fails. + pObjectProxy = nullptr; + if (ptrClientEndpoint) + { + tConnectionID = control.AssignClientEndpoint(ptrClientEndpoint, 1000, pObjectProxy); + } + else + { + tConnectionID = sdv::com::TConnectionID{}; + } + + // Verify reconnect failed (either at Access or at AssignClientEndpoint) + EXPECT_TRUE(tConnectionID.uiControl == 0u); + EXPECT_TRUE(tConnectionID.uiIdent == 0u); + EXPECT_EQ(pObjectProxy, nullptr); + ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, CreateServerConnection) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, CreateClientConnection) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + EXPECT_NE(pObjectProxy, nullptr); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, CreateClientConnection_FailReconnect) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + sdv::com::TConnectionID tConnectionID = control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID != 0u); + EXPECT_NE(pObjectProxy, nullptr); + + // Disconnect from the client + control.RemoveConnection(tConnectionID); + + // Create a new client connection + pObjectProxy = nullptr; + tConnectionID = control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy); + EXPECT_TRUE(tConnectionID == 0u); + EXPECT_EQ(pObjectProxy, nullptr); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallClientToServer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Request the ISayHello interface + ISayHello* pSayHello = nullptr; + EXPECT_NO_THROW(pSayHello = pObjectProxy->GetInterface()); + ASSERT_NE(pSayHello, nullptr); + + EXPECT_EQ(pSayHello->Hello(), "Hello"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallInterfaceFromServer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Request the IRequestHello interface + IRequestHello* pRequestHello = nullptr; + EXPECT_NO_THROW(pRequestHello = pObjectProxy->GetInterface()); + ASSERT_NE(pRequestHello, nullptr); + + // Request the ISayHello interface + ISayHello* pSayHello = pRequestHello->Request(); + + // Call the interface as if talking from the client + ASSERT_NE(pSayHello, nullptr); + EXPECT_EQ(pSayHello->Hello(), "Hello"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallInterfaceFromClient) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Request the IRegisterHelloCallback interface + IRegisterHelloCallback* pRegisterHelloCallback = nullptr; + EXPECT_NO_THROW(pRegisterHelloCallback = pObjectProxy->GetInterface()); + ASSERT_NE(pRegisterHelloCallback, nullptr); + + // Register the callback (stored in the object) + pRegisterHelloCallback->Register(&test); + + // Call the interface as if talking from the server + ASSERT_NE(test.m_pHello, nullptr); + EXPECT_EQ(test.m_pHello->Hello(), "Hello"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallGenericInterfaceFromServer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Request the IRegisterHelloCallback interface + sdv::IInterfaceAccess* pRegisterHelloCallback = nullptr; + EXPECT_NO_THROW(pRegisterHelloCallback = pObjectProxy->GetInterface()); + ASSERT_NE(pRegisterHelloCallback, nullptr); + + // Call GetInterface to get the IInterfaceAccess pointer once more (to be able to test marshalling). + sdv::IInterfaceAccess* pInterfaceAccess = pObjectProxy->GetInterface(); + ASSERT_NE(pInterfaceAccess, nullptr); + + // Request the pSayHello interface (this will be marshalled as well) + ISayHello* pSayHello = pInterfaceAccess->GetInterface(); + + // Call the interface as if talking from the client + ASSERT_NE(pSayHello, nullptr); + EXPECT_EQ(pSayHello->Hello(), "Hello"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructBoolean) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.bVal = false; + EXPECT_FALSE(sStruct.bVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_TRUE(sStruct.bVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructIntegral) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.sVal = -10; + sStruct.usVal = 10; + sStruct.lVal = -20; + sStruct.ulVal = 20; + sStruct.llVal = -30; + sStruct.ullVal = 30; + sStruct.i8Val = -40; + sStruct.ui8Val = 40; + sStruct.i16Val = -50; + sStruct.ui16Val = 50; + sStruct.i32Val = -60; + sStruct.ui32Val = 60; + sStruct.i64Val = -70; + sStruct.ui64Val = 70; + sStruct.nVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.sVal, -11); + EXPECT_EQ(sStruct.usVal, 11u); + EXPECT_EQ(sStruct.lVal, -21); + EXPECT_EQ(sStruct.ulVal, 21u); + EXPECT_EQ(sStruct.llVal, -31); + EXPECT_EQ(sStruct.ullVal, 31u); + EXPECT_EQ(static_cast(sStruct.i8Val), static_cast(-41)); // Cast needed for ARM compatibility + EXPECT_EQ(sStruct.ui8Val, 41u); + EXPECT_EQ(sStruct.i16Val, -51); + EXPECT_EQ(sStruct.ui16Val, 51u); + EXPECT_EQ(sStruct.i32Val, -61); + EXPECT_EQ(sStruct.ui32Val, 61u); + EXPECT_EQ(sStruct.i64Val, -71); + EXPECT_EQ(sStruct.ui64Val, 71u); + EXPECT_EQ(sStruct.nVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructCharacter) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.cVal = 'A'; + sStruct.c16Val = u'D'; + sStruct.c32Val = U'G'; + sStruct.wcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.cVal, 'B'); + EXPECT_EQ(sStruct.c16Val, u'E'); + EXPECT_EQ(sStruct.c32Val, U'H'); + EXPECT_EQ(sStruct.wcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructFloatingPoint) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + float fVal = -1234.5678f; + sStruct.fVal = fVal; + double dVal = 8765.4321; + sStruct.dVal = dVal; + long double ldVal = -1234.4321; + sStruct.ldVal = ldVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.fVal, fVal - 1000.000f); + EXPECT_EQ(sStruct.dVal, dVal + 1000.000); + EXPECT_EQ(sStruct.ldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructEnum) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.eHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.eHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructString) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.ssVal = "hi"; + sStruct.ss8Val = "huhu"; + sStruct.ss16Val = u"hey"; + sStruct.ss32Val = U"hello"; + sStruct.wssVal = L"servus"; + sStruct.ssFixVal = "hi"; + sStruct.ss8FixVal = "huhu"; + sStruct.ss16FixVal = u"hey"; + sStruct.ss32FixVal = U"hello"; + sStruct.wssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.ssVal, "hij"); + EXPECT_EQ(sStruct.ss8Val, "huhuv"); + EXPECT_EQ(sStruct.ss16Val, u"heyz"); + EXPECT_EQ(sStruct.ss32Val, U"hellop"); + EXPECT_EQ(sStruct.wssVal, L"servust"); + EXPECT_EQ(sStruct.ssFixVal, "hij"); + EXPECT_EQ(sStruct.ss8FixVal, "huhuv"); + EXPECT_EQ(sStruct.ss16FixVal, u"heyz"); + EXPECT_EQ(sStruct.ss32FixVal, U"hellop"); + EXPECT_EQ(sStruct.wssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructPointer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.ptr8Val.resize(100); + for (size_t nIndex = 0; nIndex < sStruct.ptr8Val.size(); nIndex++) + sStruct.ptr8Val[nIndex] = static_cast(nIndex); + sStruct.ptrssVal.resize(3); + sStruct.ptrssVal[0] = "hello"; + sStruct.ptrssVal[1] = "huhu"; + sStruct.ptrssVal[2] = "hi"; + sStruct.ptr8FixVal.resize(sStruct.ptr8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < sStruct.ptr8FixVal.size(); nIndex++) + sStruct.ptr8FixVal[nIndex] = static_cast(nIndex); + sStruct.ptrssFixVal.resize(sStruct.ptrssFixVal.capacity()); + sStruct.ptrssFixVal[0] = "hi"; + sStruct.ptrssFixVal[1] = "hoi"; + sStruct.ptrssFixVal[2] = "hello"; + sStruct.ptrssFixVal[3] = "hallo"; + sStruct.ptrssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.ptr8Val.size(), 200); + for (size_t nIndex = 0; nIndex < sStruct.ptr8Val.size() / 2; nIndex++) + EXPECT_EQ(sStruct.ptr8Val[nIndex], sStruct.ptr8Val[nIndex + sStruct.ptr8Val.size() / 2]); + EXPECT_EQ(sStruct.ptrssVal.size(), 6); + for (size_t nIndex = 0; nIndex < sStruct.ptrssVal.size() / 2; nIndex++) + EXPECT_EQ(sStruct.ptrssVal[nIndex], sStruct.ptrssVal[nIndex + sStruct.ptrssVal.size() / 2]); + EXPECT_EQ(sStruct.ptr8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < sStruct.ptr8FixVal.size(); nIndex++) + EXPECT_EQ(sStruct.ptr8FixVal[nIndex], static_cast(nIndex << 1)); + EXPECT_EQ(sStruct.ptrssFixVal.size(), 5); + EXPECT_EQ(sStruct.ptrssFixVal[0], "hihi"); + EXPECT_EQ(sStruct.ptrssFixVal[1], "hoihoi"); + EXPECT_EQ(sStruct.ptrssFixVal[2], "hellohello"); + EXPECT_EQ(sStruct.ptrssFixVal[3], "hallohallo"); + EXPECT_EQ(sStruct.ptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructSequence) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.seq8Val.resize(100); + for (size_t nIndex = 0; nIndex < sStruct.seq8Val.size(); nIndex++) + sStruct.seq8Val[nIndex] = static_cast(nIndex); + sStruct.seqssVal.resize(3); + sStruct.seqssVal[0] = "hello"; + sStruct.seqssVal[1] = "huhu"; + sStruct.seqssVal[2] = "hi"; + sStruct.seq8FixVal.resize(sStruct.seq8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < sStruct.seq8FixVal.size(); nIndex++) + sStruct.seq8FixVal[nIndex] = static_cast(nIndex); + sStruct.seqssFixVal.resize(sStruct.seqssFixVal.capacity()); + sStruct.seqssFixVal[0] = "hi"; + sStruct.seqssFixVal[1] = "hoi"; + sStruct.seqssFixVal[2] = "hello"; + sStruct.seqssFixVal[3] = "hallo"; + sStruct.seqssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.seq8Val.size(), 200); + for (size_t nIndex = 0; nIndex < sStruct.seq8Val.size() / 2; nIndex++) + EXPECT_EQ(sStruct.seq8Val[nIndex], sStruct.seq8Val[nIndex + sStruct.seq8Val.size() / 2]); + EXPECT_EQ(sStruct.seqssVal.size(), 6); + for (size_t nIndex = 0; nIndex < sStruct.seqssVal.size() / 2; nIndex++) + EXPECT_EQ(sStruct.seqssVal[nIndex], sStruct.seqssVal[nIndex + sStruct.seqssVal.size() / 2]); + EXPECT_EQ(sStruct.seq8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < sStruct.seq8FixVal.size(); nIndex++) + EXPECT_EQ(sStruct.seq8FixVal[nIndex], static_cast(nIndex << 1)); + EXPECT_EQ(sStruct.seqssFixVal.size(), 5); + EXPECT_EQ(sStruct.seqssFixVal[0], "hihi"); + EXPECT_EQ(sStruct.seqssFixVal[1], "hoihoi"); + EXPECT_EQ(sStruct.seqssFixVal[2], "hellohello"); + EXPECT_EQ(sStruct.seqssFixVal[3], "hallohallo"); + EXPECT_EQ(sStruct.seqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructInterface) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + + sStruct.idVal = sdv::GetInterfaceId(); + sStruct.ifcVal = static_cast(&sMultiply); + sStruct.pMultiplyValue = &sMultiply; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.idVal, sdv::GetInterfaceId()); + EXPECT_EQ(sStruct.ifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(sStruct.ifcVal, nullptr); + ASSERT_NE(sStruct.ifcVal.get(), nullptr); + EXPECT_EQ(sStruct.ifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + ASSERT_NE(sStruct.pMultiplyValue, nullptr); + EXPECT_EQ(sStruct.pMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructSubCompound) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.sSubVal.i = 1; + sStruct.sIndVal.i = 10; + //sStruct.sUnnamedVal.i = 100; // 12.04.2024 EVE Unnamed structures are not supported by IDL compiler. + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.sSubVal.i, 2); + EXPECT_EQ(sStruct.sIndVal.i, 20); + //EXPECT_EQ(sStruct.sUnnamedVal.i, 120); // 12.04.2024 EVE Unnamed structures are not supported by IDL compiler. + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructBooleanArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgbVal[0] = false; + sStruct.rgbVal[1] = true; + EXPECT_FALSE(sStruct.bVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_TRUE(sStruct.rgbVal[0]); + EXPECT_FALSE(sStruct.rgbVal[1]); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructIntegralArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgsVal[0] = -10; + sStruct.rgsVal[1] = -11; + sStruct.rgusVal[0] = 10; + sStruct.rgusVal[1] = 11; + sStruct.rglVal[0] = -20; + sStruct.rglVal[1] = -21; + sStruct.rgulVal[0] = 20; + sStruct.rgulVal[1] = 21; + sStruct.rgllVal[0] = -30; + sStruct.rgllVal[1] = -31; + sStruct.rgullVal[0] = 30; + sStruct.rgullVal[1] = 31; + sStruct.rgi8Val[0] = -40; + sStruct.rgi8Val[1] = -41; + sStruct.rgui8Val[0] = 40; + sStruct.rgui8Val[1] = 41; + sStruct.rgi16Val[0] = -50; + sStruct.rgi16Val[1] = -51; + sStruct.rgui16Val[0] = 50; + sStruct.rgui16Val[1] = 51; + sStruct.rgi32Val[0] = -60; + sStruct.rgi32Val[1] = -61; + sStruct.rgui32Val[0] = 60; + sStruct.rgui32Val[1] = 61; + sStruct.rgi64Val[0] = -70; + sStruct.rgi64Val[1] = -71; + sStruct.rgui64Val[0] = 70; + sStruct.rgui64Val[1] = 71; + sStruct.rgnVal[0] = 80; + sStruct.rgnVal[1] = 81; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgsVal[0], -11); + EXPECT_EQ(sStruct.rgsVal[1], -10); + EXPECT_EQ(sStruct.rgusVal[0], 11u); + EXPECT_EQ(sStruct.rgusVal[1], 13u); + EXPECT_EQ(sStruct.rglVal[0], -21); + EXPECT_EQ(sStruct.rglVal[1], -20); + EXPECT_EQ(sStruct.rgulVal[0], 21u); + EXPECT_EQ(sStruct.rgulVal[1], 23u); + EXPECT_EQ(sStruct.rgllVal[0], -31); + EXPECT_EQ(sStruct.rgllVal[1], -30); + EXPECT_EQ(sStruct.rgullVal[0], 31u); + EXPECT_EQ(sStruct.rgullVal[1], 33u); + EXPECT_EQ(static_cast(sStruct.rgi8Val[0]), static_cast(-41)); // Cast needed for ARM compatibility + EXPECT_EQ(static_cast(sStruct.rgi8Val[1]), static_cast(-40)); // Cast needed for ARM compatibility + EXPECT_EQ(sStruct.rgui8Val[0], 41u); + EXPECT_EQ(sStruct.rgui8Val[1], 43u); + EXPECT_EQ(sStruct.rgi16Val[0], -51); + EXPECT_EQ(sStruct.rgi16Val[1], -50); + EXPECT_EQ(sStruct.rgui16Val[0], 51u); + EXPECT_EQ(sStruct.rgui16Val[1], 53u); + EXPECT_EQ(sStruct.rgi32Val[0], -61); + EXPECT_EQ(sStruct.rgi32Val[1], -60); + EXPECT_EQ(sStruct.rgui32Val[0], 61u); + EXPECT_EQ(sStruct.rgui32Val[1], 63u); + EXPECT_EQ(sStruct.rgi64Val[0], -71); + EXPECT_EQ(sStruct.rgi64Val[1], -70); + EXPECT_EQ(sStruct.rgui64Val[0], 71u); + EXPECT_EQ(sStruct.rgui64Val[1], 73u); + EXPECT_EQ(sStruct.rgnVal[0], 81); + EXPECT_EQ(sStruct.rgnVal[1], 83); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructCharacterArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgcVal[0] = 'A'; + sStruct.rgcVal[1] = 'B'; + sStruct.rgc16Val[0] = u'D'; + sStruct.rgc16Val[1] = u'E'; + sStruct.rgc32Val[0] = U'G'; + sStruct.rgc32Val[1] = U'H'; + sStruct.rgwcVal[0] = L'J'; + sStruct.rgwcVal[1] = L'K'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgcVal[0], 'B'); + EXPECT_EQ(sStruct.rgcVal[1], 'D'); + EXPECT_EQ(sStruct.rgc16Val[0], u'E'); + EXPECT_EQ(sStruct.rgc16Val[1], u'G'); + EXPECT_EQ(sStruct.rgc32Val[0], U'H'); + EXPECT_EQ(sStruct.rgc32Val[1], U'J'); + EXPECT_EQ(sStruct.rgwcVal[0], L'K'); + EXPECT_EQ(sStruct.rgwcVal[1], L'M'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructFloatingPointArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + float fVal = -1234.5678f; + sStruct.rgfVal[0] = fVal; + sStruct.rgfVal[1] = fVal; + double dVal = 8765.4321; + sStruct.rgdVal[0] = dVal; + sStruct.rgdVal[1] = dVal; + long double ldVal = -1234.4321; + sStruct.rgldVal[0] = ldVal; + sStruct.rgldVal[1] = ldVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgfVal[0], fVal - 1000.000f); + EXPECT_EQ(sStruct.rgfVal[1], fVal + 2000.000f); + EXPECT_EQ(sStruct.rgdVal[0], dVal + 1000.000); + EXPECT_EQ(sStruct.rgdVal[1], dVal - 2000.000); + EXPECT_EQ(sStruct.rgldVal[0], ldVal - 1000.000); + EXPECT_EQ(sStruct.rgldVal[1], ldVal + 2000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructEnumArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgeHelloVal[0] = EHello::hello; + sStruct.rgeHelloVal[1] = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgeHelloVal[0], EHello::hallo); + EXPECT_EQ(sStruct.rgeHelloVal[1], EHello::huhu); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructStringArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgssVal[0] = "hi"; + sStruct.rgssVal[1] = "hi"; + sStruct.rgss8Val[0] = "huhu"; + sStruct.rgss8Val[1] = "huhu"; + sStruct.rgss16Val[0] = u"hey"; + sStruct.rgss16Val[1] = u"hey"; + sStruct.rgss32Val[0] = U"hello"; + sStruct.rgss32Val[1] = U"hello"; + sStruct.rgwssVal[0] = L"servus"; + sStruct.rgwssVal[1] = L"servus"; + sStruct.rgssFixVal[0] = "hi"; + sStruct.rgssFixVal[1] = "hi"; + sStruct.rgss8FixVal[0] = "huhu"; + sStruct.rgss8FixVal[1] = "huhu"; + sStruct.rgss16FixVal[0] = u"hey"; + sStruct.rgss16FixVal[1] = u"hey"; + sStruct.rgss32FixVal[0] = U"hello"; + sStruct.rgss32FixVal[1] = U"hello"; + sStruct.rgwssFixVal[0] = L"servus"; + sStruct.rgwssFixVal[1] = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgssVal[0], "hij"); + EXPECT_EQ(sStruct.rgssVal[1], "hii"); + EXPECT_EQ(sStruct.rgss8Val[0], "huhuv"); + EXPECT_EQ(sStruct.rgss8Val[1], "huhui"); + EXPECT_EQ(sStruct.rgss16Val[0], u"heyz"); + EXPECT_EQ(sStruct.rgss16Val[1], u"heyi"); + EXPECT_EQ(sStruct.rgss32Val[0], U"hellop"); + EXPECT_EQ(sStruct.rgss32Val[1], U"helloi"); + EXPECT_EQ(sStruct.rgwssVal[0], L"servust"); + EXPECT_EQ(sStruct.rgwssVal[1], L"servust"); + EXPECT_EQ(sStruct.rgssFixVal[0], "hij"); + EXPECT_EQ(sStruct.rgssFixVal[1], "hii"); + EXPECT_EQ(sStruct.rgss8FixVal[0], "huhuv"); + EXPECT_EQ(sStruct.rgss8FixVal[1], "huhui"); + EXPECT_EQ(sStruct.rgss16FixVal[0], u"heyz"); + EXPECT_EQ(sStruct.rgss16FixVal[1], u"heyi"); + EXPECT_EQ(sStruct.rgss32FixVal[0], U"hellop"); + EXPECT_EQ(sStruct.rgss32FixVal[1], U"helloi"); + EXPECT_EQ(sStruct.rgwssFixVal[0], L"servust"); + EXPECT_EQ(sStruct.rgwssFixVal[1], L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructPointerArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgptr8Val[0].resize(100); + sStruct.rgptr8Val[1].resize(100); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8Val[0].size(); nIndex++) + sStruct.rgptr8Val[0][nIndex] = static_cast(nIndex); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8Val[1].size(); nIndex++) + sStruct.rgptr8Val[1][nIndex] = static_cast(nIndex); + sStruct.rgptrssVal[0].resize(3); + sStruct.rgptrssVal[1].resize(3); + sStruct.rgptrssVal[0][0] = "hello"; + sStruct.rgptrssVal[1][0] = "ohhello"; + sStruct.rgptrssVal[0][1] = "huhu"; + sStruct.rgptrssVal[1][1] = "ohhuhu"; + sStruct.rgptrssVal[0][2] = "hi"; + sStruct.rgptrssVal[1][2] = "ohhi"; + sStruct.rgptr8FixVal[0].resize(sStruct.rgptr8FixVal[0].capacity()); + sStruct.rgptr8FixVal[1].resize(sStruct.rgptr8FixVal[1].capacity()); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8FixVal[0].size(); nIndex++) + sStruct.rgptr8FixVal[0][nIndex] = static_cast(nIndex); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8FixVal[1].size(); nIndex++) + sStruct.rgptr8FixVal[1][nIndex] = static_cast(nIndex); + sStruct.rgptrssFixVal[0].resize(sStruct.rgptrssFixVal[0].capacity()); + sStruct.rgptrssFixVal[1].resize(sStruct.rgptrssFixVal[1].capacity()); + sStruct.rgptrssFixVal[0][0] = "hi"; + sStruct.rgptrssFixVal[1][0] = "ohhi"; + sStruct.rgptrssFixVal[0][1] = "hoi"; + sStruct.rgptrssFixVal[1][1] = "ohhoi"; + sStruct.rgptrssFixVal[0][2] = "hello"; + sStruct.rgptrssFixVal[1][2] = "ohhello"; + sStruct.rgptrssFixVal[0][3] = "hallo"; + sStruct.rgptrssFixVal[1][3] = "ohhallo"; + sStruct.rgptrssFixVal[0][4] = "servus"; + sStruct.rgptrssFixVal[1][4] = "ohservus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgptr8Val[0].size(), 200); + EXPECT_EQ(sStruct.rgptr8Val[1].size(), 300); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8Val[0].size() / 2; nIndex++) + EXPECT_EQ(sStruct.rgptr8Val[0][nIndex], sStruct.rgptr8Val[0][nIndex + sStruct.rgptr8Val[0].size() / 2]); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8Val[1].size() / 3; nIndex++) + EXPECT_EQ(sStruct.rgptr8Val[1][nIndex], sStruct.rgptr8Val[1][nIndex + sStruct.rgptr8Val[1].size() / 3]); + EXPECT_EQ(sStruct.rgptrssVal[0].size(), 6); + EXPECT_EQ(sStruct.rgptrssVal[1].size(), 9); + for (size_t nIndex = 0; nIndex < sStruct.rgptrssVal[0].size() / 2; nIndex++) + EXPECT_EQ(sStruct.rgptrssVal[0][nIndex], sStruct.rgptrssVal[0][nIndex + sStruct.rgptrssVal[0].size() / 2]); + for (size_t nIndex = 0; nIndex < sStruct.rgptrssVal[1].size() / 3; nIndex++) + EXPECT_EQ(sStruct.rgptrssVal[1][nIndex], sStruct.rgptrssVal[1][nIndex + sStruct.rgptrssVal[1].size() / 3]); + EXPECT_EQ(sStruct.rgptr8FixVal[0].size(), 20); + EXPECT_EQ(sStruct.rgptr8FixVal[1].size(), 20); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8FixVal[0].size(); nIndex++) + EXPECT_EQ(sStruct.rgptr8FixVal[0][nIndex], static_cast(nIndex << 1)); + for (size_t nIndex = 0; nIndex < sStruct.rgptr8FixVal[1].size(); nIndex++) + EXPECT_EQ(sStruct.rgptr8FixVal[1][nIndex], static_cast(nIndex << 1)); + EXPECT_EQ(sStruct.rgptrssFixVal[0].size(), 5); + EXPECT_EQ(sStruct.rgptrssFixVal[1].size(), 5); + EXPECT_EQ(sStruct.rgptrssFixVal[0][0], "hihi"); + EXPECT_EQ(sStruct.rgptrssFixVal[1][0], "ohhiohhi"); + EXPECT_EQ(sStruct.rgptrssFixVal[0][1], "hoihoi"); + EXPECT_EQ(sStruct.rgptrssFixVal[1][1], "ohhoiohhoi"); + EXPECT_EQ(sStruct.rgptrssFixVal[0][2], "hellohello"); + EXPECT_EQ(sStruct.rgptrssFixVal[1][2], "ohhelloohhello"); + EXPECT_EQ(sStruct.rgptrssFixVal[0][3], "hallohallo"); + EXPECT_EQ(sStruct.rgptrssFixVal[1][3], "ohhalloohhallo"); + EXPECT_EQ(sStruct.rgptrssFixVal[0][4], "servusservus"); + EXPECT_EQ(sStruct.rgptrssFixVal[1][4], "ohservusohservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructSequenceArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgseq8Val[0].resize(100); + sStruct.rgseq8Val[1].resize(100); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8Val[0].size(); nIndex++) + sStruct.rgseq8Val[0][nIndex] = static_cast(nIndex); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8Val[1].size(); nIndex++) + sStruct.rgseq8Val[1][nIndex] = static_cast(nIndex); + sStruct.rgseqssVal[0].resize(3); + sStruct.rgseqssVal[1].resize(3); + sStruct.rgseqssVal[0][0] = "hello"; + sStruct.rgseqssVal[1][0] = "ohhello"; + sStruct.rgseqssVal[0][1] = "huhu"; + sStruct.rgseqssVal[1][1] = "ohhuhu"; + sStruct.rgseqssVal[0][2] = "hi"; + sStruct.rgseqssVal[1][2] = "ohhi"; + sStruct.rgseq8FixVal[0].resize(sStruct.rgseq8FixVal[0].capacity()); + sStruct.rgseq8FixVal[1].resize(sStruct.rgseq8FixVal[1].capacity()); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8FixVal[0].size(); nIndex++) + sStruct.rgseq8FixVal[0][nIndex] = static_cast(nIndex); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8FixVal[1].size(); nIndex++) + sStruct.rgseq8FixVal[1][nIndex] = static_cast(nIndex); + sStruct.rgseqssFixVal[0].resize(sStruct.seqssFixVal.capacity()); + sStruct.rgseqssFixVal[1].resize(sStruct.seqssFixVal.capacity()); + sStruct.rgseqssFixVal[0][0] = "hi"; + sStruct.rgseqssFixVal[1][0] = "ohhi"; + sStruct.rgseqssFixVal[0][1] = "hoi"; + sStruct.rgseqssFixVal[1][1] = "ohhoi"; + sStruct.rgseqssFixVal[0][2] = "hello"; + sStruct.rgseqssFixVal[1][2] = "ohhello"; + sStruct.rgseqssFixVal[0][3] = "hallo"; + sStruct.rgseqssFixVal[1][3] = "ohhallo"; + sStruct.rgseqssFixVal[0][4] = "servus"; + sStruct.rgseqssFixVal[1][4] = "ohservus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgseq8Val[0].size(), 200); + EXPECT_EQ(sStruct.rgseq8Val[1].size(), 300); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8Val[0].size() / 2; nIndex++) + EXPECT_EQ(sStruct.rgseq8Val[0][nIndex], sStruct.rgseq8Val[0][nIndex + sStruct.rgseq8Val[0].size() / 2]); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8Val[1].size() / 3; nIndex++) + EXPECT_EQ(sStruct.rgseq8Val[1][nIndex], sStruct.rgseq8Val[1][nIndex + sStruct.rgseq8Val[1].size() / 3]); + EXPECT_EQ(sStruct.rgseqssVal[0].size(), 6); + EXPECT_EQ(sStruct.rgseqssVal[1].size(), 9); + for (size_t nIndex = 0; nIndex < sStruct.rgseqssVal[0].size() / 2; nIndex++) + EXPECT_EQ(sStruct.rgseqssVal[0][nIndex], sStruct.rgseqssVal[0][nIndex + sStruct.rgseqssVal[0].size() / 2]); + for (size_t nIndex = 0; nIndex < sStruct.rgseqssVal[1].size() / 3; nIndex++) + EXPECT_EQ(sStruct.rgseqssVal[1][nIndex], sStruct.rgseqssVal[1][nIndex + sStruct.rgseqssVal[1].size() / 3]); + EXPECT_EQ(sStruct.rgseq8FixVal[0].size(), 20); + EXPECT_EQ(sStruct.rgseq8FixVal[1].size(), 20); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8FixVal[0].size(); nIndex++) + EXPECT_EQ(sStruct.rgseq8FixVal[0][nIndex], static_cast(nIndex << 1)); + for (size_t nIndex = 0; nIndex < sStruct.rgseq8FixVal[1].size(); nIndex++) + EXPECT_EQ(sStruct.rgseq8FixVal[1][nIndex], static_cast(nIndex << 1)); + EXPECT_EQ(sStruct.rgseqssFixVal[0].size(), 5); + EXPECT_EQ(sStruct.rgseqssFixVal[1].size(), 5); + EXPECT_EQ(sStruct.rgseqssFixVal[0][0], "hihi"); + EXPECT_EQ(sStruct.rgseqssFixVal[1][0], "ohhiohhi"); + EXPECT_EQ(sStruct.rgseqssFixVal[0][1], "hoihoi"); + EXPECT_EQ(sStruct.rgseqssFixVal[1][1], "ohhoiohhoi"); + EXPECT_EQ(sStruct.rgseqssFixVal[0][2], "hellohello"); + EXPECT_EQ(sStruct.rgseqssFixVal[1][2], "ohhelloohhello"); + EXPECT_EQ(sStruct.rgseqssFixVal[0][3], "hallohallo"); + EXPECT_EQ(sStruct.rgseqssFixVal[1][3], "ohhalloohhallo"); + EXPECT_EQ(sStruct.rgseqssFixVal[0][4], "servusservus"); + EXPECT_EQ(sStruct.rgseqssFixVal[1][4], "ohservusohservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructInterfaceArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + + //sStruct.rgidVal[0] = sdv::GetInterfaceId(); + //sStruct.rgidVal[1] = sdv::GetInterfaceId(); + //sStruct.rgifcVal[0] = static_cast(&sMultiply); + //sStruct.rgifcVal[1] = static_cast(&sMultiply); + //sStruct.rgpMultiplyValue[0] = &sMultiply; + //sStruct.rgpMultiplyValue[1] = &sMultiply; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + //EXPECT_EQ(sStruct.rgidVal[0], sdv::GetInterfaceId()); + //EXPECT_EQ(sStruct.rgidVal[1], sdv::GetInterfaceId()); + //EXPECT_EQ(sStruct.rgifcVal[0].id(), sdv::GetInterfaceId()); + //EXPECT_EQ(sStruct.rgifcVal[1].id(), sdv::GetInterfaceId()); + //ASSERT_NE(sStruct.rgifcVal[0], nullptr); + //ASSERT_NE(sStruct.rgifcVal[1], nullptr); + //ASSERT_NE(sStruct.rgifcVal[0].get(), nullptr); + //ASSERT_NE(sStruct.rgifcVal[1].get(), nullptr); + //EXPECT_EQ(sStruct.rgifcVal[0].get()->Add(99), 19602); // 99 * 99 + 99 * 99 + //EXPECT_EQ(sStruct.rgifcVal[1].get()->Add(99), 19602); // 99 * 99 + 99 * 99 + //ASSERT_NE(sStruct.rgpMultiplyValue[0], nullptr); + //ASSERT_NE(sStruct.rgpMultiplyValue[1], nullptr); + //EXPECT_EQ(sStruct.rgpMultiplyValue[0]->Multiply(5), 625); // 5 * 5 * 5 * 5 + //EXPECT_EQ(sStruct.rgpMultiplyValue[1]->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructSubCompoundArray) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.rgsSubVal[0].i = 1; + sStruct.rgsSubVal[1].i = 2; + // Incompatible serdes generation: BUG #398509 + //sStruct.rgsIndVal[0].i = 10; + //sStruct.rgsIndVal[1].i = 20; + // Unnamed struct with array is not yet supported due to incorret serdes code generation: BUG #398246 + //sStruct.rgsUnnamedVal[0].i = 100; + //sStruct.rgsUnnamedVal[1].i = 200; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.rgsSubVal[0].i, 2); + EXPECT_EQ(sStruct.rgsSubVal[1].i, 4); + //EXPECT_EQ(sStruct.rgsIndVal[0].i, 20); + //EXPECT_EQ(sStruct.rgsIndVal[1].i, 40); + //EXPECT_EQ(sStruct.rgsUnnamedVal[0].i, 120); + //EXPECT_EQ(sStruct.rgsUnnamedVal[1].i, 240); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructBooleanTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.tbVal = false; + EXPECT_FALSE(sStruct.tbVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_TRUE(sStruct.tbVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructIntegralTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.tsVal = -10; + sStruct.tusVal = 10; + sStruct.tlVal = -20; + sStruct.tulVal = 20; + sStruct.tllVal = -30; + sStruct.tullVal = 30; + sStruct.ti8Val = -40; + sStruct.tui8Val = 40; + sStruct.ti16Val = -50; + sStruct.tui16Val = 50; + sStruct.ti32Val = -60; + sStruct.tui32Val = 60; + sStruct.ti64Val = -70; + sStruct.tui64Val = 70; + sStruct.tnVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tsVal, -11); + EXPECT_EQ(sStruct.tusVal, 11u); + EXPECT_EQ(sStruct.tlVal, -21); + EXPECT_EQ(sStruct.tulVal, 21u); + EXPECT_EQ(sStruct.tllVal, -31); + EXPECT_EQ(sStruct.tullVal, 31u); + EXPECT_EQ(static_cast(sStruct.ti8Val), static_cast(-41)); // Cast needed for ARM compatibility + EXPECT_EQ(sStruct.tui8Val, 41u); + EXPECT_EQ(sStruct.ti16Val, -51); + EXPECT_EQ(sStruct.tui16Val, 51u); + EXPECT_EQ(sStruct.ti32Val, -61); + EXPECT_EQ(sStruct.tui32Val, 61u); + EXPECT_EQ(sStruct.ti64Val, -71); + EXPECT_EQ(sStruct.tui64Val, 71u); + EXPECT_EQ(sStruct.tnVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructCharacterTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.tcVal = 'A'; + sStruct.tc16Val = u'D'; + sStruct.tc32Val = U'G'; + sStruct.twcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tcVal, 'B'); + EXPECT_EQ(sStruct.tc16Val, u'E'); + EXPECT_EQ(sStruct.tc32Val, U'H'); + EXPECT_EQ(sStruct.twcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructFloatingPointTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + float fVal = -1234.5678f; + sStruct.tfVal = fVal; + double dVal = 8765.4321; + sStruct.tdVal = dVal; + long double ldVal = -1234.4321; + sStruct.tldVal = ldVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tfVal, fVal - 1000.000f); + EXPECT_EQ(sStruct.tdVal, dVal + 1000.000); + EXPECT_EQ(sStruct.tldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructEnumTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.teHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.teHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructStringTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.tssVal = "hi"; + sStruct.tss8Val = "huhu"; + sStruct.tss16Val = u"hey"; + sStruct.tss32Val = U"hello"; + sStruct.twssVal = L"servus"; + sStruct.tssFixVal = "hi"; + sStruct.tss8FixVal = "huhu"; + sStruct.tss16FixVal = u"hey"; + sStruct.tss32FixVal = U"hello"; + sStruct.twssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tssVal, "hij"); + EXPECT_EQ(sStruct.tss8Val, "huhuv"); + EXPECT_EQ(sStruct.tss16Val, u"heyz"); + EXPECT_EQ(sStruct.tss32Val, U"hellop"); + EXPECT_EQ(sStruct.twssVal, L"servust"); + EXPECT_EQ(sStruct.tssFixVal, "hij"); + EXPECT_EQ(sStruct.tss8FixVal, "huhuv"); + EXPECT_EQ(sStruct.tss16FixVal, u"heyz"); + EXPECT_EQ(sStruct.tss32FixVal, U"hellop"); + EXPECT_EQ(sStruct.twssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS_Tunnel, DISABLED_MarshallMegaStructPointerTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + //sStruct.tptr8Val.resize(100); + //for (size_t nIndex = 0; nIndex < sStruct.tptr8Val.size(); nIndex++) + // sStruct.tptr8Val[nIndex] = static_cast(nIndex); + //sStruct.tptrssVal.resize(3); + //sStruct.tptrssVal[0] = "hello"; + //sStruct.tptrssVal[1] = "huhu"; + //sStruct.tptrssVal[2] = "hi"; + //sStruct.tptr8FixVal.resize(sStruct.tptr8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < sStruct.tptr8FixVal.size(); nIndex++) + // sStruct.tptr8FixVal[nIndex] = static_cast(nIndex); + //sStruct.tptrssFixVal.resize(sStruct.tptrssFixVal.capacity()); + //sStruct.tptrssFixVal[0] = "hi"; + //sStruct.tptrssFixVal[1] = "hoi"; + //sStruct.tptrssFixVal[2] = "hello"; + //sStruct.tptrssFixVal[3] = "hallo"; + //sStruct.tptrssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + //EXPECT_EQ(sStruct.tptr8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < sStruct.tptr8Val.size() / 2; nIndex++) + // EXPECT_EQ(sStruct.tptr8Val[nIndex], sStruct.tptr8Val[nIndex + sStruct.tptr8Val.size() / 2]); + //EXPECT_EQ(sStruct.tptrssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < sStruct.tptrssVal.size() / 2; nIndex++) + // EXPECT_EQ(sStruct.tptrssVal[nIndex], sStruct.tptrssVal[nIndex + sStruct.tptrssVal.size() / 2]); + //EXPECT_EQ(sStruct.tptr8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < sStruct.tptr8FixVal.size(); nIndex++) + // EXPECT_EQ(sStruct.tptr8FixVal[nIndex], static_cast(nIndex << 1)); + //EXPECT_EQ(sStruct.tptrssFixVal.size(), 5); + //EXPECT_EQ(sStruct.tptrssFixVal[0], "hihi"); + //EXPECT_EQ(sStruct.tptrssFixVal[1], "hoihoi"); + //EXPECT_EQ(sStruct.tptrssFixVal[2], "hellohello"); + //EXPECT_EQ(sStruct.tptrssFixVal[3], "hallohallo"); + //EXPECT_EQ(sStruct.tptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS_Tunnel, DISABLED_MarshallMegaStructSequenceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + //sStruct.tseq8Val.resize(100); + //for (size_t nIndex = 0; nIndex < sStruct.tseq8Val.size(); nIndex++) + // sStruct.tseq8Val[nIndex] = static_cast(nIndex); + //sStruct.tseqssVal.resize(3); + //sStruct.tseqssVal[0] = "hello"; + //sStruct.tseqssVal[1] = "huhu"; + //sStruct.tseqssVal[2] = "hi"; + //sStruct.tseq8FixVal.resize(sStruct.tseq8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < sStruct.tseq8FixVal.size(); nIndex++) + // sStruct.tseq8FixVal[nIndex] = static_cast(nIndex); + //sStruct.tseqssFixVal.resize(sStruct.tseqssFixVal.capacity()); + //sStruct.tseqssFixVal[0] = "hi"; + //sStruct.tseqssFixVal[1] = "hoi"; + //sStruct.tseqssFixVal[2] = "hello"; + //sStruct.tseqssFixVal[3] = "hallo"; + //sStruct.tseqssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + //EXPECT_EQ(sStruct.tseq8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < sStruct.tseq8Val.size() / 2; nIndex++) + // EXPECT_EQ(sStruct.tseq8Val[nIndex], sStruct.tseq8Val[nIndex + sStruct.tseq8Val.size() / 2]); + //EXPECT_EQ(sStruct.tseqssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < sStruct.tseqssVal.size() / 2; nIndex++) + // EXPECT_EQ(sStruct.tseqssVal[nIndex], sStruct.tseqssVal[nIndex + sStruct.tseqssVal.size() / 2]); + //EXPECT_EQ(sStruct.tseq8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < sStruct.tseq8FixVal.size(); nIndex++) + // EXPECT_EQ(sStruct.tseq8FixVal[nIndex], static_cast(nIndex << 1)); + //EXPECT_EQ(sStruct.tseqssFixVal.size(), 5); + //EXPECT_EQ(sStruct.tseqssFixVal[0], "hihi"); + //EXPECT_EQ(sStruct.tseqssFixVal[1], "hoihoi"); + //EXPECT_EQ(sStruct.tseqssFixVal[2], "hellohello"); + //EXPECT_EQ(sStruct.tseqssFixVal[3], "hallohallo"); + //EXPECT_EQ(sStruct.tseqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructInterfaceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + + sStruct.tidVal = sdv::GetInterfaceId(); + sStruct.tifcVal = static_cast(&sMultiply); + // Typedef of interfaces current not possible. BUG #399464 + //sStruct.ptMultiplyValue = &sMultiply; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tidVal, sdv::GetInterfaceId()); + EXPECT_EQ(sStruct.tifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(sStruct.tifcVal, nullptr); + ASSERT_NE(sStruct.tifcVal.get(), nullptr); + EXPECT_EQ(sStruct.tifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + // Typedef of interfaces current not possible. BUG #399464 + //ASSERT_NE(sStruct.tpMultiplyValue, nullptr); + //EXPECT_EQ(sStruct.tpMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaStructSubCompoundTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaStruct sStruct{}; + sStruct.tsSubVal.i = 1; + sStruct.tsIndVal.i = 10; + // Incompatible serdes generation: BUG #398509 + //sStruct.tsUnnamedVal.i = 100; + EXPECT_NO_THROW(pMegaTest->ProcessMegaStruct(sStruct)); + EXPECT_EQ(sStruct.tsSubVal.i, 2); + EXPECT_EQ(sStruct.tsIndVal.i, 20); + // Incompatible serdes generation: BUG #398509 + //EXPECT_EQ(sStruct.tsUnnamedVal.i, 120); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionBoolean) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(1); + uUnion.bVal = false; + EXPECT_FALSE(uUnion.bVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_TRUE(uUnion.bVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionIntegral) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(2); + uUnion.sVal = -10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.sVal, -11); + uUnion.switch_to(3); + uUnion.usVal = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.usVal, 11u); + uUnion.switch_to(4); + uUnion.lVal = -20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.lVal, -21); + uUnion.switch_to(5); + uUnion.ulVal = 20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ulVal, 21u); + uUnion.switch_to(6); + uUnion.llVal = -30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.llVal, -31); + uUnion.switch_to(7); + uUnion.ullVal = 30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ullVal, 31u); + uUnion.switch_to(8); + uUnion.i8Val = -40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(static_cast(uUnion.i8Val), static_cast(-41)); // Cast needed for ARM compatibility + uUnion.switch_to(9); + uUnion.ui8Val = 40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ui8Val, 41u); + uUnion.switch_to(10); + uUnion.i16Val = -50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.i16Val, -51); + uUnion.switch_to(11); + uUnion.ui16Val = 50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ui16Val, 51u); + uUnion.switch_to(12); + uUnion.i32Val = -60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.i32Val, -61); + uUnion.switch_to(13); + uUnion.ui32Val = 60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ui32Val, 61u); + uUnion.switch_to(14); + uUnion.i64Val = -70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.i64Val, -71); + uUnion.switch_to(15); + uUnion.ui64Val = 70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ui64Val, 71u); + uUnion.switch_to(23); + uUnion.nVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.nVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionCharacter) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(16); + uUnion.cVal = 'A'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.cVal, 'B'); + uUnion.switch_to(17); + uUnion.c16Val = u'D'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.c16Val, u'E'); + uUnion.switch_to(18); + uUnion.c32Val = U'G'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.c32Val, U'H'); + uUnion.switch_to(19); + uUnion.wcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.wcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionFloatingPoint) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(20); + float fVal = -1234.5678f; + uUnion.fVal = fVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.fVal, fVal - 1000.000f); + uUnion.switch_to(21); + double dVal = 8765.4321; + uUnion.dVal = dVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.dVal, dVal + 1000.000); + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + //uUnion.switch_to(22); + //long double ldVal = -1234.4321; + //uUnion.ldVal = ldVal; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.ldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionEnum) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(24); + uUnion.eHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.eHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionString) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(26); + uUnion.ssVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ssVal, "hij"); + uUnion.switch_to(27); + uUnion.ss8Val = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss8Val, "huhuv"); + uUnion.switch_to(28); + uUnion.ss16Val = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss16Val, u"heyz"); + uUnion.switch_to(29); + uUnion.ss32Val = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss32Val, U"hellop"); + uUnion.switch_to(30); + uUnion.wssVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.wssVal, L"servust"); + uUnion.switch_to(31); + uUnion.ssFixVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ssFixVal, "hij"); + uUnion.switch_to(32); + uUnion.ss8FixVal = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss8FixVal, "huhuv"); + uUnion.switch_to(33); + uUnion.ss16FixVal = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss16FixVal, u"heyz"); + uUnion.switch_to(34); + uUnion.ss32FixVal = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ss32FixVal, U"hellop"); + uUnion.switch_to(35); + uUnion.wssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.wssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionPointer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(36); + uUnion.ptr8Val.resize(100); + for (size_t nIndex = 0; nIndex < uUnion.ptr8Val.size(); nIndex++) + uUnion.ptr8Val[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ptr8Val.size(), 200); + for (size_t nIndex = 0; nIndex < uUnion.ptr8Val.size() / 2; nIndex++) + EXPECT_EQ(uUnion.ptr8Val[nIndex], uUnion.ptr8Val[nIndex + uUnion.ptr8Val.size() / 2]); + uUnion.switch_to(37); + uUnion.ptrssVal.resize(3); + uUnion.ptrssVal[0] = "hello"; + uUnion.ptrssVal[1] = "huhu"; + uUnion.ptrssVal[2] = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ptrssVal.size(), 6); + for (size_t nIndex = 0; nIndex < uUnion.ptrssVal.size() / 2; nIndex++) + EXPECT_EQ(uUnion.ptrssVal[nIndex], uUnion.ptrssVal[nIndex + uUnion.ptrssVal.size() / 2]); + uUnion.switch_to(38); + uUnion.ptr8FixVal.resize(uUnion.ptr8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < uUnion.ptr8FixVal.size(); nIndex++) + uUnion.ptr8FixVal[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ptr8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < uUnion.ptr8FixVal.size(); nIndex++) + EXPECT_EQ(uUnion.ptr8FixVal[nIndex], static_cast(nIndex << 1)); + uUnion.switch_to(39); + uUnion.ptrssFixVal.resize(uUnion.ptrssFixVal.capacity()); + uUnion.ptrssFixVal[0] = "hi"; + uUnion.ptrssFixVal[1] = "hoi"; + uUnion.ptrssFixVal[2] = "hello"; + uUnion.ptrssFixVal[3] = "hallo"; + uUnion.ptrssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ptrssFixVal.size(), 5); + EXPECT_EQ(uUnion.ptrssFixVal[0], "hihi"); + EXPECT_EQ(uUnion.ptrssFixVal[1], "hoihoi"); + EXPECT_EQ(uUnion.ptrssFixVal[2], "hellohello"); + EXPECT_EQ(uUnion.ptrssFixVal[3], "hallohallo"); + EXPECT_EQ(uUnion.ptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionSequence) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(36); + uUnion.seq8Val.resize(100); + for (size_t nIndex = 0; nIndex < uUnion.seq8Val.size(); nIndex++) + uUnion.seq8Val[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.seq8Val.size(), 200); + for (size_t nIndex = 0; nIndex < uUnion.seq8Val.size() / 2; nIndex++) + EXPECT_EQ(uUnion.seq8Val[nIndex], uUnion.seq8Val[nIndex + uUnion.seq8Val.size() / 2]); + uUnion.switch_to(37); + uUnion.seqssVal.resize(3); + uUnion.seqssVal[0] = "hello"; + uUnion.seqssVal[1] = "huhu"; + uUnion.seqssVal[2] = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.seqssVal.size(), 6); + for (size_t nIndex = 0; nIndex < uUnion.seqssVal.size() / 2; nIndex++) + EXPECT_EQ(uUnion.seqssVal[nIndex], uUnion.seqssVal[nIndex + uUnion.seqssVal.size() / 2]); + uUnion.switch_to(38); + uUnion.seq8FixVal.resize(uUnion.seq8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < uUnion.seq8FixVal.size(); nIndex++) + uUnion.seq8FixVal[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.seq8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < uUnion.seq8FixVal.size(); nIndex++) + EXPECT_EQ(uUnion.seq8FixVal[nIndex], static_cast(nIndex << 1)); + uUnion.switch_to(39); + uUnion.seqssFixVal.resize(uUnion.seqssFixVal.capacity()); + uUnion.seqssFixVal[0] = "hi"; + uUnion.seqssFixVal[1] = "hoi"; + uUnion.seqssFixVal[2] = "hello"; + uUnion.seqssFixVal[3] = "hallo"; + uUnion.seqssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.seqssFixVal.size(), 5); + EXPECT_EQ(uUnion.seqssFixVal[0], "hihi"); + EXPECT_EQ(uUnion.seqssFixVal[1], "hoihoi"); + EXPECT_EQ(uUnion.seqssFixVal[2], "hellohello"); + EXPECT_EQ(uUnion.seqssFixVal[3], "hallohallo"); + EXPECT_EQ(uUnion.seqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionInterface) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(25); + + uUnion.idVal = sdv::GetInterfaceId(); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.idVal, sdv::GetInterfaceId()); + uUnion.switch_to(44); + uUnion.ifcVal = static_cast(&sMultiply); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(uUnion.ifcVal, nullptr); + ASSERT_NE(uUnion.ifcVal.get(), nullptr); + EXPECT_EQ(uUnion.ifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + uUnion.switch_to(45); + uUnion.pMultiplyValue = &sMultiply; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + ASSERT_NE(uUnion.pMultiplyValue, nullptr); + EXPECT_EQ(uUnion.pMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionSubCompound) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(46); + uUnion.sSubVal.i = 1; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.sSubVal.i, 2); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionBooleanTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(201); + uUnion.tbVal = false; + EXPECT_FALSE(uUnion.tbVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_TRUE(uUnion.tbVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionIntegralTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(202); + uUnion.tsVal = -10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tsVal, -11); + uUnion.switch_to(203); + uUnion.tusVal = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tusVal, 11u); + uUnion.switch_to(204); + uUnion.tlVal = -20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tlVal, -21); + uUnion.switch_to(205); + uUnion.tulVal = 20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tulVal, 21u); + uUnion.switch_to(206); + uUnion.tllVal = -30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tllVal, -31); + uUnion.switch_to(207); + uUnion.tullVal = 30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tullVal, 31u); + uUnion.switch_to(208); + uUnion.ti8Val = -40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(static_cast(uUnion.ti8Val), static_cast(-41)); // Cast needed for ARM compatibility + uUnion.switch_to(209); + uUnion.tui8Val = 40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tui8Val, 41u); + uUnion.switch_to(210); + uUnion.ti16Val = -50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ti16Val, -51); + uUnion.switch_to(211); + uUnion.tui16Val = 50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tui16Val, 51u); + uUnion.switch_to(212); + uUnion.ti32Val = -60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ti32Val, -61); + uUnion.switch_to(213); + uUnion.tui32Val = 60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tui32Val, 61u); + uUnion.switch_to(214); + uUnion.ti64Val = -70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.ti64Val, -71); + uUnion.switch_to(215); + uUnion.tui64Val = 70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tui64Val, 71u); + uUnion.switch_to(223); + uUnion.tnVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tnVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionCharacterTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(216); + uUnion.tcVal = 'A'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tcVal, 'B'); + uUnion.switch_to(217); + uUnion.tc16Val = u'D'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tc16Val, u'E'); + uUnion.switch_to(218); + uUnion.tc32Val = U'G'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tc32Val, U'H'); + uUnion.switch_to(219); + uUnion.twcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.twcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionFloatingPointTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(220); + float fVal = -1234.5678f; + uUnion.tfVal = fVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tfVal, fVal - 1000.000f); + uUnion.switch_to(221); + double dVal = 8765.4321; + uUnion.tdVal = dVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tdVal, dVal + 1000.000); + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + //uUnion.switch_to(222); + //long double ldVal = -1234.4321; + //uUnion.tldVal = ldVal; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionEnumTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(224); + uUnion.teHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.teHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionStringTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(226); + uUnion.tssVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tssVal, "hij"); + uUnion.switch_to(227); + uUnion.tss8Val = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss8Val, "huhuv"); + uUnion.switch_to(228); + uUnion.tss16Val = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss16Val, u"heyz"); + uUnion.switch_to(229); + uUnion.tss32Val = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss32Val, U"hellop"); + uUnion.switch_to(230); + uUnion.twssVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.twssVal, L"servust"); + uUnion.switch_to(231); + uUnion.tssFixVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tssFixVal, "hij"); + uUnion.switch_to(232); + uUnion.tss8FixVal = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss8FixVal, "huhuv"); + uUnion.switch_to(233); + uUnion.tss16FixVal = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss16FixVal, u"heyz"); + uUnion.switch_to(234); + uUnion.tss32FixVal = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tss32FixVal, U"hellop"); + uUnion.switch_to(235); + uUnion.twssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.twssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS_Tunnel, DISABLED_MarshallMegaTypeBasedUnionPointerTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(236); + //uUnion.tptr8Val.resize(100); + //for (size_t nIndex = 0; nIndex < uUnion.tptr8Val.size(); nIndex++) + // uUnion.tptr8Val[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tptr8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < uUnion.tptr8Val.size() / 2; nIndex++) + // EXPECT_EQ(uUnion.tptr8Val[nIndex], uUnion.tptr8Val[nIndex + uUnion.tptr8Val.size() / 2]); + //uUnion.switch_to(237); + //uUnion.tptrssVal.resize(3); + //uUnion.tptrssVal[0] = "hello"; + //uUnion.tptrssVal[1] = "huhu"; + //uUnion.tptrssVal[2] = "hi"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tptrssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < uUnion.tptrssVal.size() / 2; nIndex++) + // EXPECT_EQ(uUnion.tptrssVal[nIndex], uUnion.tptrssVal[nIndex + uUnion.tptrssVal.size() / 2]); + //uUnion.switch_to(238); + //uUnion.tptr8FixVal.resize(uUnion.tptr8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < uUnion.tptr8FixVal.size(); nIndex++) + // uUnion.tptr8FixVal[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tptr8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < uUnion.tptr8FixVal.size(); nIndex++) + // EXPECT_EQ(uUnion.tptr8FixVal[nIndex], static_cast(nIndex << 1)); + //uUnion.switch_to(239); + //uUnion.tptrssFixVal.resize(uUnion.tptrssFixVal.capacity()); + //uUnion.tptrssFixVal[0] = "hi"; + //uUnion.tptrssFixVal[1] = "hoi"; + //uUnion.tptrssFixVal[2] = "hello"; + //uUnion.tptrssFixVal[3] = "hallo"; + //uUnion.tptrssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tptrssFixVal.size(), 5); + //EXPECT_EQ(uUnion.tptrssFixVal[0], "hihi"); + //EXPECT_EQ(uUnion.tptrssFixVal[1], "hoihoi"); + //EXPECT_EQ(uUnion.tptrssFixVal[2], "hellohello"); + //EXPECT_EQ(uUnion.tptrssFixVal[3], "hallohallo"); + //EXPECT_EQ(uUnion.tptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS_Tunnel, DISABLED_MarshallMegaTypeBasedUnionSequenceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(236); + //uUnion.tseq8Val.resize(100); + //for (size_t nIndex = 0; nIndex < uUnion.tseq8Val.size(); nIndex++) + // uUnion.tseq8Val[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tseq8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < uUnion.tseq8Val.size() / 2; nIndex++) + // EXPECT_EQ(uUnion.tseq8Val[nIndex], uUnion.tseq8Val[nIndex + uUnion.tseq8Val.size() / 2]); + //uUnion.switch_to(237); + //uUnion.tseqssVal.resize(3); + //uUnion.tseqssVal[0] = "hello"; + //uUnion.tseqssVal[1] = "huhu"; + //uUnion.tseqssVal[2] = "hi"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tseqssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < uUnion.tseqssVal.size() / 2; nIndex++) + // EXPECT_EQ(uUnion.tseqssVal[nIndex], uUnion.tseqssVal[nIndex + uUnion.tseqssVal.size() / 2]); + //uUnion.switch_to(238); + //uUnion.tseq8FixVal.resize(uUnion.tseq8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < uUnion.tseq8FixVal.size(); nIndex++) + // uUnion.tseq8FixVal[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tseq8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < uUnion.tseq8FixVal.size(); nIndex++) + // EXPECT_EQ(uUnion.tseq8FixVal[nIndex], static_cast(nIndex << 1)); + //uUnion.switch_to(239); + //uUnion.tseqssFixVal.resize(uUnion.tseqssFixVal.capacity()); + //uUnion.tseqssFixVal[0] = "hi"; + //uUnion.tseqssFixVal[1] = "hoi"; + //uUnion.tseqssFixVal[2] = "hello"; + //uUnion.tseqssFixVal[3] = "hallo"; + //uUnion.tseqssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //EXPECT_EQ(uUnion.tseqssFixVal.size(), 5); + //EXPECT_EQ(uUnion.tseqssFixVal[0], "hihi"); + //EXPECT_EQ(uUnion.tseqssFixVal[1], "hoihoi"); + //EXPECT_EQ(uUnion.tseqssFixVal[2], "hellohello"); + //EXPECT_EQ(uUnion.tseqssFixVal[3], "hallohallo"); + //EXPECT_EQ(uUnion.tseqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionInterfaceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(225); + + uUnion.tidVal = sdv::GetInterfaceId(); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tidVal, sdv::GetInterfaceId()); + uUnion.switch_to(244); + uUnion.tifcVal = static_cast(&sMultiply); + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(uUnion.tifcVal, nullptr); + ASSERT_NE(uUnion.tifcVal.get(), nullptr); + EXPECT_EQ(uUnion.tifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + // Typedef of interfaces current not possible. BUG #399464 + //uUnion.switch_to(245); + //uUnion.tpMultiplyValue = &sMultiply; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + //ASSERT_NE(uUnion.tpMultiplyValue, nullptr); + //EXPECT_EQ(uUnion.tpMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaTypeBasedUnionSubCompoundTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + UMegaTypeBasedUnion uUnion; + uUnion.switch_to(246); + uUnion.tsSubVal.i = 1; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tsSubVal.i, 2); + uUnion.switch_to(247); + uUnion.tsIndVal.i = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaTypeBasedUnion(uUnion)); + EXPECT_EQ(uUnion.tsIndVal.i, 20); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionBoolean) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(1); + sUnion.uVal.bVal = false; + EXPECT_FALSE(sUnion.uVal.bVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_TRUE(sUnion.uVal.bVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionIntegral) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(2); + sUnion.uVal.sVal = -10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.sVal, -11); + sUnion.switch_to(3); + sUnion.uVal.usVal = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.usVal, 11u); + sUnion.switch_to(4); + sUnion.uVal.lVal = -20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.lVal, -21); + sUnion.switch_to(5); + sUnion.uVal.ulVal = 20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ulVal, 21u); + sUnion.switch_to(6); + sUnion.uVal.llVal = -30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.llVal, -31); + sUnion.switch_to(7); + sUnion.uVal.ullVal = 30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ullVal, 31u); + sUnion.switch_to(8); + sUnion.uVal.i8Val = -40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(static_cast(sUnion.uVal.i8Val), static_cast(-41)); // Cast needed for ARM compatibility + sUnion.switch_to(9); + sUnion.uVal.ui8Val = 40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ui8Val, 41u); + sUnion.switch_to(10); + sUnion.uVal.i16Val = -50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.i16Val, -51); + sUnion.switch_to(11); + sUnion.uVal.ui16Val = 50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ui16Val, 51u); + sUnion.switch_to(12); + sUnion.uVal.i32Val = -60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.i32Val, -61); + sUnion.switch_to(13); + sUnion.uVal.ui32Val = 60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ui32Val, 61u); + sUnion.switch_to(14); + sUnion.uVal.i64Val = -70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.i64Val, -71); + sUnion.switch_to(15); + sUnion.uVal.ui64Val = 70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ui64Val, 71u); + sUnion.switch_to(23); + sUnion.uVal.nVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.nVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionCharacter) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(16); + sUnion.uVal.cVal = 'A'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.cVal, 'B'); + sUnion.switch_to(17); + sUnion.uVal.c16Val = u'D'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.c16Val, u'E'); + sUnion.switch_to(18); + sUnion.uVal.c32Val = U'G'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.c32Val, U'H'); + sUnion.switch_to(19); + sUnion.uVal.wcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.wcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionFloatingPoint) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(20); + float fVal = -1234.5678f; + sUnion.uVal.fVal = fVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.fVal, fVal - 1000.000f); + sUnion.switch_to(21); + double dVal = 8765.4321; + sUnion.uVal.dVal = dVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.dVal, dVal + 1000.000); + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + //sUnion.switch_to(22); + //long double ldVal = -1234.4321; + //sUnion.uVal.ldVal = ldVal; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.ldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionEnum) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(24); + sUnion.uVal.eHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.eHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionString) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(26); + sUnion.uVal.ssVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ssVal, "hij"); + sUnion.switch_to(27); + sUnion.uVal.ss8Val = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss8Val, "huhuv"); + sUnion.switch_to(28); + sUnion.uVal.ss16Val = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss16Val, u"heyz"); + sUnion.switch_to(29); + sUnion.uVal.ss32Val = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss32Val, U"hellop"); + sUnion.switch_to(30); + sUnion.uVal.wssVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.wssVal, L"servust"); + sUnion.switch_to(31); + sUnion.uVal.ssFixVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ssFixVal, "hij"); + sUnion.switch_to(32); + sUnion.uVal.ss8FixVal = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss8FixVal, "huhuv"); + sUnion.switch_to(33); + sUnion.uVal.ss16FixVal = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss16FixVal, u"heyz"); + sUnion.switch_to(34); + sUnion.uVal.ss32FixVal = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ss32FixVal, U"hellop"); + sUnion.switch_to(35); + sUnion.uVal.wssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.wssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionPointer) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(36); + sUnion.uVal.ptr8Val.resize(100); + for (size_t nIndex = 0; nIndex < sUnion.uVal.ptr8Val.size(); nIndex++) + sUnion.uVal.ptr8Val[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ptr8Val.size(), 200); + for (size_t nIndex = 0; nIndex < sUnion.uVal.ptr8Val.size() / 2; nIndex++) + EXPECT_EQ(sUnion.uVal.ptr8Val[nIndex], sUnion.uVal.ptr8Val[nIndex + sUnion.uVal.ptr8Val.size() / 2]); + sUnion.switch_to(37); + sUnion.uVal.ptrssVal.resize(3); + sUnion.uVal.ptrssVal[0] = "hello"; + sUnion.uVal.ptrssVal[1] = "huhu"; + sUnion.uVal.ptrssVal[2] = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ptrssVal.size(), 6); + for (size_t nIndex = 0; nIndex < sUnion.uVal.ptrssVal.size() / 2; nIndex++) + EXPECT_EQ(sUnion.uVal.ptrssVal[nIndex], sUnion.uVal.ptrssVal[nIndex + sUnion.uVal.ptrssVal.size() / 2]); + sUnion.switch_to(38); + sUnion.uVal.ptr8FixVal.resize(sUnion.uVal.ptr8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < sUnion.uVal.ptr8FixVal.size(); nIndex++) + sUnion.uVal.ptr8FixVal[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ptr8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < sUnion.uVal.ptr8FixVal.size(); nIndex++) + EXPECT_EQ(sUnion.uVal.ptr8FixVal[nIndex], static_cast(nIndex << 1)); + sUnion.switch_to(39); + sUnion.uVal.ptrssFixVal.resize(sUnion.uVal.ptrssFixVal.capacity()); + sUnion.uVal.ptrssFixVal[0] = "hi"; + sUnion.uVal.ptrssFixVal[1] = "hoi"; + sUnion.uVal.ptrssFixVal[2] = "hello"; + sUnion.uVal.ptrssFixVal[3] = "hallo"; + sUnion.uVal.ptrssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ptrssFixVal.size(), 5); + EXPECT_EQ(sUnion.uVal.ptrssFixVal[0], "hihi"); + EXPECT_EQ(sUnion.uVal.ptrssFixVal[1], "hoihoi"); + EXPECT_EQ(sUnion.uVal.ptrssFixVal[2], "hellohello"); + EXPECT_EQ(sUnion.uVal.ptrssFixVal[3], "hallohallo"); + EXPECT_EQ(sUnion.uVal.ptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionSequence) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(36); + sUnion.uVal.seq8Val.resize(100); + for (size_t nIndex = 0; nIndex < sUnion.uVal.seq8Val.size(); nIndex++) + sUnion.uVal.seq8Val[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.seq8Val.size(), 200); + for (size_t nIndex = 0; nIndex < sUnion.uVal.seq8Val.size() / 2; nIndex++) + EXPECT_EQ(sUnion.uVal.seq8Val[nIndex], sUnion.uVal.seq8Val[nIndex + sUnion.uVal.seq8Val.size() / 2]); + sUnion.switch_to(37); + sUnion.uVal.seqssVal.resize(3); + sUnion.uVal.seqssVal[0] = "hello"; + sUnion.uVal.seqssVal[1] = "huhu"; + sUnion.uVal.seqssVal[2] = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.seqssVal.size(), 6); + for (size_t nIndex = 0; nIndex < sUnion.uVal.seqssVal.size() / 2; nIndex++) + EXPECT_EQ(sUnion.uVal.seqssVal[nIndex], sUnion.uVal.seqssVal[nIndex + sUnion.uVal.seqssVal.size() / 2]); + sUnion.switch_to(38); + sUnion.uVal.seq8FixVal.resize(sUnion.uVal.seq8FixVal.capacity()); + for (size_t nIndex = 0; nIndex < sUnion.uVal.seq8FixVal.size(); nIndex++) + sUnion.uVal.seq8FixVal[nIndex] = static_cast(nIndex); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.seq8FixVal.size(), 20); + for (size_t nIndex = 0; nIndex < sUnion.uVal.seq8FixVal.size(); nIndex++) + EXPECT_EQ(sUnion.uVal.seq8FixVal[nIndex], static_cast(nIndex << 1)); + sUnion.switch_to(39); + sUnion.uVal.seqssFixVal.resize(sUnion.uVal.seqssFixVal.capacity()); + sUnion.uVal.seqssFixVal[0] = "hi"; + sUnion.uVal.seqssFixVal[1] = "hoi"; + sUnion.uVal.seqssFixVal[2] = "hello"; + sUnion.uVal.seqssFixVal[3] = "hallo"; + sUnion.uVal.seqssFixVal[4] = "servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.seqssFixVal.size(), 5); + EXPECT_EQ(sUnion.uVal.seqssFixVal[0], "hihi"); + EXPECT_EQ(sUnion.uVal.seqssFixVal[1], "hoihoi"); + EXPECT_EQ(sUnion.uVal.seqssFixVal[2], "hellohello"); + EXPECT_EQ(sUnion.uVal.seqssFixVal[3], "hallohallo"); + EXPECT_EQ(sUnion.uVal.seqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionInterface) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(25); + + sUnion.uVal.idVal = sdv::GetInterfaceId(); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.idVal, sdv::GetInterfaceId()); + sUnion.switch_to(44); + sUnion.uVal.ifcVal = static_cast(&sMultiply); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(sUnion.uVal.ifcVal, nullptr); + ASSERT_NE(sUnion.uVal.ifcVal.get(), nullptr); + EXPECT_EQ(sUnion.uVal.ifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + sUnion.switch_to(45); + sUnion.uVal.pMultiplyValue = &sMultiply; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + ASSERT_NE(sUnion.uVal.pMultiplyValue, nullptr); + EXPECT_EQ(sUnion.uVal.pMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionSubCompound) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(46); + sUnion.uVal.sSubVal.i = 1; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.sSubVal.i, 2); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionBooleanTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(201); + sUnion.uVal.tbVal = false; + EXPECT_FALSE(sUnion.uVal.tbVal); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_TRUE(sUnion.uVal.tbVal); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionIntegralTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(202); + sUnion.uVal.tsVal = -10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tsVal, -11); + sUnion.switch_to(203); + sUnion.uVal.tusVal = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tusVal, 11u); + sUnion.switch_to(204); + sUnion.uVal.tlVal = -20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tlVal, -21); + sUnion.switch_to(205); + sUnion.uVal.tulVal = 20; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tulVal, 21u); + sUnion.switch_to(206); + sUnion.uVal.tllVal = -30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tllVal, -31); + sUnion.switch_to(207); + sUnion.uVal.tullVal = 30; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tullVal, 31u); + sUnion.switch_to(208); + sUnion.uVal.ti8Val = -40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(static_cast(sUnion.uVal.ti8Val), static_cast(-41)); // Cast needed for ARM compatibility + sUnion.switch_to(209); + sUnion.uVal.tui8Val = 40; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tui8Val, 41u); + sUnion.switch_to(210); + sUnion.uVal.ti16Val = -50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ti16Val, -51); + sUnion.switch_to(211); + sUnion.uVal.tui16Val = 50; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tui16Val, 51u); + sUnion.switch_to(212); + sUnion.uVal.ti32Val = -60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ti32Val, -61); + sUnion.switch_to(213); + sUnion.uVal.tui32Val = 60; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tui32Val, 61u); + sUnion.switch_to(214); + sUnion.uVal.ti64Val = -70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.ti64Val, -71); + sUnion.switch_to(215); + sUnion.uVal.tui64Val = 70; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tui64Val, 71u); + sUnion.switch_to(223); + sUnion.uVal.tnVal = 80; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tnVal, 81); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionCharacterTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(216); + sUnion.uVal.tcVal = 'A'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tcVal, 'B'); + sUnion.switch_to(217); + sUnion.uVal.tc16Val = u'D'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tc16Val, u'E'); + sUnion.switch_to(218); + sUnion.uVal.tc32Val = U'G'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tc32Val, U'H'); + sUnion.switch_to(219); + sUnion.uVal.twcVal = L'J'; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.twcVal, L'K'); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionFloatingPointTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(220); + float fVal = -1234.5678f; + sUnion.uVal.tfVal = fVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tfVal, fVal - 1000.000f); + sUnion.switch_to(221); + double dVal = 8765.4321; + sUnion.uVal.tdVal = dVal; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tdVal, dVal + 1000.000); + // GCC issue with generated initialization in the consructor for the "long double" type: BUG #3982727 + //sUnion.switch_to(222); + //long double ldVal = -1234.4321; + //sUnion.uVal.tldVal = ldVal; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tldVal, ldVal - 1000.000); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionEnumTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(224); + sUnion.uVal.teHelloVal = EHello::hello; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.teHelloVal, EHello::hallo); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionStringTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(226); + sUnion.uVal.tssVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tssVal, "hij"); + sUnion.switch_to(227); + sUnion.uVal.tss8Val = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss8Val, "huhuv"); + sUnion.switch_to(228); + sUnion.uVal.tss16Val = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss16Val, u"heyz"); + sUnion.switch_to(229); + sUnion.uVal.tss32Val = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss32Val, U"hellop"); + sUnion.switch_to(230); + sUnion.uVal.twssVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.twssVal, L"servust"); + sUnion.switch_to(231); + sUnion.uVal.tssFixVal = "hi"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tssFixVal, "hij"); + sUnion.switch_to(232); + sUnion.uVal.tss8FixVal = "huhu"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss8FixVal, "huhuv"); + sUnion.switch_to(233); + sUnion.uVal.tss16FixVal = u"hey"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss16FixVal, u"heyz"); + sUnion.switch_to(234); + sUnion.uVal.tss32FixVal = U"hello"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tss32FixVal, U"hellop"); + sUnion.switch_to(235); + sUnion.uVal.twssFixVal = L"servus"; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.twssFixVal, L"servust"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS_Tunnel, DISABLED_MarshallMegaVarBasedUnionPointerTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(236); + //sUnion.uVal.tptr8Val.resize(100); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tptr8Val.size(); nIndex++) + // sUnion.uVal.tptr8Val[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tptr8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tptr8Val.size() / 2; nIndex++) + // EXPECT_EQ(sUnion.uVal.tptr8Val[nIndex], sUnion.uVal.tptr8Val[nIndex + sUnion.uVal.tptr8Val.size() / 2]); + //sUnion.switch_to(237); + //sUnion.uVal.tptrssVal.resize(3); + //sUnion.uVal.tptrssVal[0] = "hello"; + //sUnion.uVal.tptrssVal[1] = "huhu"; + //sUnion.uVal.tptrssVal[2] = "hi"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tptrssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tptrssVal.size() / 2; nIndex++) + // EXPECT_EQ(sUnion.uVal.tptrssVal[nIndex], sUnion.uVal.tptrssVal[nIndex + sUnion.uVal.tptrssVal.size() / 2]); + //sUnion.switch_to(238); + //sUnion.uVal.tptr8FixVal.resize(sUnion.uVal.tptr8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tptr8FixVal.size(); nIndex++) + // sUnion.uVal.tptr8FixVal[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tptr8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tptr8FixVal.size(); nIndex++) + // EXPECT_EQ(sUnion.uVal.tptr8FixVal[nIndex], static_cast(nIndex << 1)); + //sUnion.switch_to(239); + //sUnion.uVal.tptrssFixVal.resize(sUnion.uVal.tptrssFixVal.capacity()); + //sUnion.uVal.tptrssFixVal[0] = "hi"; + //sUnion.uVal.tptrssFixVal[1] = "hoi"; + //sUnion.uVal.tptrssFixVal[2] = "hello"; + //sUnion.uVal.tptrssFixVal[3] = "hallo"; + //sUnion.uVal.tptrssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal.size(), 5); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal[0], "hihi"); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal[1], "hoihoi"); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal[2], "hellohello"); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal[3], "hallohallo"); + //EXPECT_EQ(sUnion.uVal.tptrssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +// Using typedefs of template based types produced an error: BUG #398266 +TEST(IPC_Communication_Test_UDS_Tunnel, DISABLED_MarshallMegaVarBasedUnionSequenceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(236); + //sUnion.uVal.tseq8Val.resize(100); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tseq8Val.size(); nIndex++) + // sUnion.uVal.tseq8Val[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tseq8Val.size(), 200); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tseq8Val.size() / 2; nIndex++) + // EXPECT_EQ(sUnion.uVal.tseq8Val[nIndex], sUnion.uVal.tseq8Val[nIndex + sUnion.uVal.tseq8Val.size() / 2]); + //sUnion.switch_to(237); + //sUnion.uVal.tseqssVal.resize(3); + //sUnion.uVal.tseqssVal[0] = "hello"; + //sUnion.uVal.tseqssVal[1] = "huhu"; + //sUnion.uVal.tseqssVal[2] = "hi"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tseqssVal.size(), 6); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tseqssVal.size() / 2; nIndex++) + // EXPECT_EQ(sUnion.uVal.tseqssVal[nIndex], sUnion.uVal.tseqssVal[nIndex + sUnion.uVal.tseqssVal.size() / 2]); + //sUnion.switch_to(238); + //sUnion.uVal.tseq8FixVal.resize(sUnion.uVal.tseq8FixVal.capacity()); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tseq8FixVal.size(); nIndex++) + // sUnion.uVal.tseq8FixVal[nIndex] = static_cast(nIndex); + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tseq8FixVal.size(), 20); + //for (size_t nIndex = 0; nIndex < sUnion.uVal.tseq8FixVal.size(); nIndex++) + // EXPECT_EQ(sUnion.uVal.tseq8FixVal[nIndex], static_cast(nIndex << 1)); + //sUnion.switch_to(239); + //sUnion.uVal.tseqssFixVal.resize(sUnion.uVal.tseqssFixVal.capacity()); + //sUnion.uVal.tseqssFixVal[0] = "hi"; + //sUnion.uVal.tseqssFixVal[1] = "hoi"; + //sUnion.uVal.tseqssFixVal[2] = "hello"; + //sUnion.uVal.tseqssFixVal[3] = "hallo"; + //sUnion.uVal.tseqssFixVal[4] = "servus"; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal.size(), 5); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal[0], "hihi"); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal[1], "hoihoi"); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal[2], "hellohello"); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal[3], "hallohallo"); + //EXPECT_EQ(sUnion.uVal.tseqssFixVal[4], "servusservus"); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionInterfaceTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + struct SMultiply : public IMultiplyValue + { + size_t Multiply(size_t n) + { + return n * n; + } + } sMultiply; + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(225); + + sUnion.uVal.tidVal = sdv::GetInterfaceId(); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tidVal, sdv::GetInterfaceId()); + sUnion.switch_to(244); + sUnion.uVal.tifcVal = static_cast(&sMultiply); + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tifcVal.id(), sdv::GetInterfaceId()); + ASSERT_NE(sUnion.uVal.tifcVal, nullptr); + ASSERT_NE(sUnion.uVal.tifcVal.get(), nullptr); + EXPECT_EQ(sUnion.uVal.tifcVal.get()->Add(99), 19602); // 99 * 99 + 99 * 99 + // Typedef of interfaces current not possible. BUG #399464 + //sUnion.switch_to(245); + //sUnion.uVal.tpMultiplyValue = &sMultiply; + //EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + //ASSERT_NE(sUnion.uVal.tpMultiplyValue, nullptr); + //EXPECT_EQ(sUnion.uVal.tpMultiplyValue->Multiply(5), 625); // 5 * 5 * 5 * 5 + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} + +TEST(IPC_Communication_Test_UDS_Tunnel, MarshallMegaVarBasedUnionSubCompoundTypedef) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" + +[Console] +Report = "Silent" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + // Start communication control + CCommunicationControl control; + control.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::initialized); + control.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(control.GetObjectState(), sdv::EObjectState::configuring); + + // Load the shared memory components + LoadIPCModules(control, ELocalIpcBackend::tunnel); + + // Create the server connection + CInterfaceTest test; + sdv::u8string ssConnectionString; + EXPECT_TRUE(control.CreateServerConnection(sdv::com::EChannelType::local_channel, &test, 100, ssConnectionString) != 0u); + + // Create the client connection + sdv::IInterfaceAccess* pObjectProxy = nullptr; + EXPECT_TRUE(control.CreateClientConnection(ssConnectionString, 1000, pObjectProxy) != 0u); + ASSERT_NE(pObjectProxy, nullptr); + + // Get the target interface + IMegaTest* pMegaTest = pObjectProxy->GetInterface(); + + ASSERT_NE(pMegaTest, nullptr); + SMegaVarBasedUnion sUnion; + sUnion.switch_to(246); + sUnion.uVal.tsSubVal.i = 1; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tsSubVal.i, 2); + sUnion.switch_to(247); + sUnion.uVal.tsIndVal.i = 10; + EXPECT_NO_THROW(pMegaTest->ProcessMegaVarBasedUnion(sUnion)); + EXPECT_EQ(sUnion.uVal.tsIndVal.i, 20); + + // Cleanup... + control.Shutdown(); + appcontrol.Shutdown(); +} diff --git a/tests/unit_tests/ipc_com/main.cpp b/tests/unit_tests/ipc_com/main.cpp index 5c630d3..6003015 100644 --- a/tests/unit_tests/ipc_com/main.cpp +++ b/tests/unit_tests/ipc_com/main.cpp @@ -47,7 +47,8 @@ extern "C" int main(int argc, char* argv[]) } else { - CProcessWatchdog watchdog; + // Use extended execution time of 10 minutes + CProcessWatchdog watchdog(600u); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/tests/unit_tests/ipc_connect/CMakeLists.txt b/tests/unit_tests/ipc_connect/CMakeLists.txt index 07125a3..156c45b 100644 --- a/tests/unit_tests/ipc_connect/CMakeLists.txt +++ b/tests/unit_tests/ipc_connect/CMakeLists.txt @@ -17,8 +17,10 @@ project (UnitTest_IPC_Connect VERSION 1.0 LANGUAGES CXX) # Compile the source code add_executable(UnitTest_IPC_Connect "main.cpp" - - "ipc_connect.cpp" + "ipc_connect_shared_mem.cpp" + "ipc_connect_false_positiv.cpp" + "ipc_connect_uds.cpp" + "ipc_connect_uds_tunnel.cpp" ) target_link_libraries(UnitTest_IPC_Connect ${CMAKE_DL_LIBS} GTest::GTest) diff --git a/tests/unit_tests/ipc_connect/ipc_connect.cpp b/tests/unit_tests/ipc_connect/ipc_connect.cpp deleted file mode 100644 index 9a7804e..0000000 --- a/tests/unit_tests/ipc_connect/ipc_connect.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025-2026 ZF Friedrichshafen AG - * - * This program and the accompanying materials are made available under the - * terms of the Apache License Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Erik Verhoeven - initial API and implementation - ********************************************************************************/ - -#include "../../include/gtest_custom.h" -#include -#include -#include -#include "../../../sdv_services/ipc_connect/listener.h" -#include "../../../sdv_services/ipc_connect/client.h" - -TEST(IPC_Connect_Test, InstantiateLocalDefaultListener) -{ - // Initialize system - sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config")); - ASSERT_TRUE(appcontrol.IsRunning()); - sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); - ASSERT_NE(pModuleControl, nullptr); - EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); - EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); - EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); - sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); - ASSERT_NE(pRepositoryControl, nullptr); - EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); - EXPECT_NE(pRepositoryControl->CreateObject("DefaultSharedMemoryChannelControl", {}, {}), 0); - - // Start listener - CListener listener; - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); - listener.Initialize(R"code([Listener] -Type = "Local" -)code"); - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); - - // Shutdown - listener.Shutdown(); - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); - appcontrol.Shutdown(); -} - -TEST(IPC_Connect_Test, InstantiateLocalDedicatedListener) -{ - // Initialize system - sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config")); - ASSERT_TRUE(appcontrol.IsRunning()); - sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); - ASSERT_NE(pModuleControl, nullptr); - EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); - EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); - EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); - sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); - ASSERT_NE(pRepositoryControl, nullptr); - EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); - EXPECT_NE(pRepositoryControl->CreateObject("DefaultSharedMemoryChannelControl", {}, {}), 0); - - // Start listener - CListener listener; - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); - listener.Initialize(R"code([Listener] -Type = "Local" -Instance = 1234 -)code"); - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); - - // Shutdown - listener.Shutdown(); - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); - appcontrol.Shutdown(); -} - -TEST(IPC_Connect_Test, InstantiateLocalDefaultClientNoListener) -{ - // Initialize system - sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config")); - ASSERT_TRUE(appcontrol.IsRunning()); - sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); - ASSERT_NE(pModuleControl, nullptr); - EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); - EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); - EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); - sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); - ASSERT_NE(pRepositoryControl, nullptr); - EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); - EXPECT_NE(pRepositoryControl->CreateObject("DefaultSharedMemoryChannelControl", {}, {}), 0); - - // Start client - CClient client; - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); - client.Initialize(""); - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); - sdv::TObjectPtr ptrClient = client.Connect(R"code([Client] -Type = "local" -)code"); - EXPECT_FALSE(ptrClient); - - // Shutdown - ptrClient.Clear(); - client.Shutdown(); - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); - appcontrol.Shutdown(); -} - -TEST(IPC_Connect_Test, InstantiateLocalSpecificClientNoListener) -{ - // Initialize system - sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config")); - ASSERT_TRUE(appcontrol.IsRunning()); - sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); - ASSERT_NE(pModuleControl, nullptr); - EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); - EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); - EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); - sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); - ASSERT_NE(pRepositoryControl, nullptr); - EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); - EXPECT_NE(pRepositoryControl->CreateObject("DefaultSharedMemoryChannelControl", {}, {}), 0); - - // Start client - CClient client; - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); - client.Initialize(""); - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); - sdv::TObjectPtr ptrClient = client.Connect(R"code([Client] -Type = "local" -Instance = 1234 -)code"); - EXPECT_FALSE(ptrClient); - - // Shutdown - ptrClient.Clear(); - client.Shutdown(); - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); - appcontrol.Shutdown(); -} - -// Disabled the following test due to an unidentified crash/heap corruption occurring with MINGW on the build-server. -// Bug-report #610009 describes this issue: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/610009 -#ifdef _WIN32 -TEST(IPC_Connect_Test, DISABLED_InstantiateLocalDefaultClientAndListener) -#else -TEST(IPC_Connect_Test, InstantiateLocalDefaultClientAndListener) -#endif -{ - // Initialize system - sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config")); - ASSERT_TRUE(appcontrol.IsRunning()); - sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); - ASSERT_NE(pModuleControl, nullptr); - EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); - EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); - EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); - sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); - ASSERT_NE(pRepositoryControl, nullptr); - EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); - EXPECT_NE(pRepositoryControl->CreateObject("DefaultSharedMemoryChannelControl", {}, {}), 0); - - // Start listener - CListener listener; - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); - listener.Initialize(R"code([Listener] -Type = "Local" -)code"); - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); - - // Start client - CClient client; - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); - client.Initialize(""); - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); - sdv::TObjectPtr ptrClient = client.Connect(R"code([Client] -Type = "Local" -)code"); - EXPECT_TRUE(ptrClient); - - // The client is a pointer to the repository. Request the module control service - sdv::core::IObjectAccess* pObjectAccess = ptrClient.GetInterface(); - EXPECT_NE(pObjectAccess, nullptr); - sdv::core::IModuleInfo* pModuleInfo = nullptr; - if (pObjectAccess) - pModuleInfo = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ModuleControlService")).GetInterface(); - EXPECT_NE(pModuleInfo, nullptr); - if (pModuleInfo) - { - EXPECT_FALSE(pModuleInfo->GetModuleList().empty()); - } - - // Shutdown - ptrClient.Clear(); - client.Shutdown(); - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); - listener.Shutdown(); - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); - appcontrol.Shutdown(); -} - -// Disabled the following test due to an unidentified crash/heap corruption occurring with MINGW on the build-server. -// Bug-report #610009 describes this issue: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/610009 -#ifdef _WIN32 -TEST(IPC_Connect_Test, DISABLED_InstantiateLocalSpecificClientAndListener) -#else -TEST(IPC_Connect_Test, InstantiateLocalSpecificClientAndListener) -#endif -{ - // Initialize system - sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"config( -[Application] -Mode = "Essential" - -[LogHandler] -ViewFilter = "Fatal" -)config")); - ASSERT_TRUE(appcontrol.IsRunning()); - sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); - ASSERT_NE(pModuleControl, nullptr); - EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); - EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); - EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); - sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); - ASSERT_NE(pRepositoryControl, nullptr); - EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); - EXPECT_NE(pRepositoryControl->CreateObject("DefaultSharedMemoryChannelControl", {}, {}), 0); - - // Start listener - CListener listener; - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); - listener.Initialize(R"code([Listener] -Type = "Local" -Instance = 1234 -)code"); - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); - - // Start client - CClient client; - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); - client.Initialize(""); - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); - sdv::TObjectPtr ptrClient = client.Connect(R"code([Client] -Type = "Local" -Instance = 1234 -)code"); - EXPECT_TRUE(ptrClient); - - // The client is a pointer to the repository. Request the module control service - sdv::core::IObjectAccess* pObjectAccess = ptrClient.GetInterface(); - EXPECT_NE(pObjectAccess, nullptr); - sdv::core::IModuleInfo* pModuleInfo = nullptr; - if (pObjectAccess) - pModuleInfo = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ModuleControlService")).GetInterface(); - EXPECT_NE(pModuleInfo, nullptr); - if (pModuleInfo) - { - EXPECT_FALSE(pModuleInfo->GetModuleList().empty()); - } - - // Shutdown - ptrClient.Clear(); - client.Shutdown(); - EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); - listener.Shutdown(); - EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); - appcontrol.Shutdown(); -} diff --git a/tests/unit_tests/ipc_connect/ipc_connect_false_positiv.cpp b/tests/unit_tests/ipc_connect/ipc_connect_false_positiv.cpp new file mode 100644 index 0000000..0275985 --- /dev/null +++ b/tests/unit_tests/ipc_connect/ipc_connect_false_positiv.cpp @@ -0,0 +1,86 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include +#include + +#include "../../../sdv_services/ipc_connect/client.h" +#include "../../../sdv_services/ipc_connect/listener.h" +#include "../../include/gtest_custom.h" + +TEST(IPC_Connect_Test, InstantiateListenerWithUnknownProvider) +{ + // Initialize system + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); + ASSERT_NE(pModuleControl, nullptr); + EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); + EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); + EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); + sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); + ASSERT_NE(pRepositoryControl, nullptr); + EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); + EXPECT_NE(pRepositoryControl->CreateObject("DefaultSharedMemory", {}, {}), 0); + + // Start listener + CListener listener; + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); + listener.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_failure); + + // Shutdown + listener.Shutdown(); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); + appcontrol.Shutdown(); +} + +TEST(IPC_Connect_Test_Shared_Mem, InstantiateClientWithoutConfiguration) +{ + // Initialize system + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); + ASSERT_NE(pModuleControl, nullptr); + EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); + EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); + EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); + sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); + ASSERT_NE(pRepositoryControl, nullptr); + EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); + + // Start client without configuration + CClientConnect client; + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); + client.Initialize(sdv::SObjectInfo()); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_failure); + + client.Shutdown(); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); + appcontrol.Shutdown(); +} diff --git a/tests/unit_tests/ipc_connect/ipc_connect_shared_mem.cpp b/tests/unit_tests/ipc_connect/ipc_connect_shared_mem.cpp new file mode 100644 index 0000000..f19d3cc --- /dev/null +++ b/tests/unit_tests/ipc_connect/ipc_connect_shared_mem.cpp @@ -0,0 +1,181 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include "../../include/gtest_custom.h" +#include +#include +#include +#include +#include "../../../sdv_services/ipc_connect/listener.h" +#include "../../../sdv_services/ipc_connect/client.h" + +TEST(IPC_Connect_Test_Shared_Mem, InstantiateListener) +{ + // Initialize system + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" +Instance = 1234 + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); + ASSERT_NE(pModuleControl, nullptr); + EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); + EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); + EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); + sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); + ASSERT_NE(pRepositoryControl, nullptr); + EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); + EXPECT_NE(pRepositoryControl->CreateObject("DefaultSharedMemory", {}, {}), 0); + + sdv::SObjectInfo sListenerInfo{}; + sListenerInfo.ssConfig = R"toml( +[Provider] +Name = "DefaultSharedMemory" + +[IpcChannel] +Name = "MyPersonalChannelName" +)toml"; + + // Start listener + CListener listener; + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); + listener.Initialize(sListenerInfo); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); + + EXPECT_EQ(listener.GetProviderName(), "DefaultSharedMemory"); + + // Shutdown + listener.Shutdown(); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); + appcontrol.Shutdown(); +} + +TEST(IPC_Connect_Test_Shared_Mem, InstantiateClientNoListener) +{ + // Initialize system + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); + ASSERT_NE(pModuleControl, nullptr); + EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); + EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); + EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); + sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); + ASSERT_NE(pRepositoryControl, nullptr); + EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); + + sdv::SObjectInfo sClientInfo{}; + sClientInfo.ssConfig = R"toml( +[Provider] +Name = "DefaultSharedMemory" + +[IpcChannel] +Name = "MyPersonalChannelName" +)toml"; + + // Start client + CClientConnect client; + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); + client.Initialize(sClientInfo); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); + EXPECT_FALSE(client.Connect()); + + // Shutdown + client.Shutdown(); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); + appcontrol.Shutdown(); +} + +TEST(IPC_Connect_Test_Shared_Mem, InstantiateClientAndListener) +{ + // Initialize system + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + sdv::core::IModuleControl* pModuleControl = sdv::core::GetObject("ModuleControlService"); + ASSERT_NE(pModuleControl, nullptr); + EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); + EXPECT_NE(pModuleControl->Load("ipc_shared_mem.sdv"), 0); + EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); + sdv::core::IRepositoryControl* pRepositoryControl = sdv::core::GetObject("RepositoryService"); + ASSERT_NE(pRepositoryControl, nullptr); + EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); + + sdv::SObjectInfo sListenerInfo{}; + sListenerInfo.ssConfig = R"toml([Provider] +Name = "DefaultSharedMemory" +[IpcChannel] +Name = "CHANNEL_1234" +)toml"; + + // Start listener + CListener listener; + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); + listener.Initialize(sListenerInfo); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); + + sdv::SObjectInfo sClientInfo{}; + sClientInfo.ssConfig = R"toml([Provider] +Name = "DefaultSharedMemory" +[IpcChannel] +Name = "CHANNEL_1234" +)toml"; + + // Start client + CClientConnect client; + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); + client.Initialize(sClientInfo); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); + EXPECT_TRUE(client.Connect()); + EXPECT_TRUE(client.IsConnected()); + + // The client is a pointer to the repository. Request the module control service + sdv::core::IObjectAccess* pObjectAccess = + sdv::TInterfaceAccessPtr(client.GetRemoteRepository()).GetInterface(); + EXPECT_NE(pObjectAccess, nullptr); + sdv::core::IModuleInfo* pModuleInfo = nullptr; + if (pObjectAccess) + pModuleInfo = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ModuleControlService")).GetInterface(); + EXPECT_NE(pModuleInfo, nullptr); + if (pModuleInfo) + { + EXPECT_FALSE(pModuleInfo->GetModuleList().empty()); + } + EXPECT_TRUE(client.Disconnect()); + + // Shutdown + client.Shutdown(); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); + listener.Shutdown(); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); + appcontrol.Shutdown(); +} + diff --git a/tests/unit_tests/ipc_connect/ipc_connect_uds.cpp b/tests/unit_tests/ipc_connect/ipc_connect_uds.cpp new file mode 100644 index 0000000..bc2409c --- /dev/null +++ b/tests/unit_tests/ipc_connect/ipc_connect_uds.cpp @@ -0,0 +1,233 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Denisa Ros - initial API and implementation + ********************************************************************************/ + +#include "../../include/gtest_custom.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "../../../sdv_services/ipc_connect/listener.h" +#include "../../../sdv_services/ipc_connect/client.h" + +namespace +{ + +const char* GetUdsModuleName() +{ +#ifdef _WIN32 + return "uds_win_sockets.sdv"; +#else + return "uds_unix_sockets.sdv"; +#endif +} + +const char* GetUdsChannelControlClassName() +{ +#ifdef _WIN32 + return "WinSocketsChannelControl"; +#else + return "UnixSocketsChannelControl"; +#endif +} + +std::string UniqueSuffix() +{ + const auto now = std::chrono::steady_clock::now().time_since_epoch().count(); + return std::to_string(static_cast(now)); +} + +std::string MakeChannelName(const char* base) +{ + return std::string(base) + "_" + UniqueSuffix(); +} + +std::string MakeUdsConfigByName(const std::string& channelName) +{ + return std::string("[Provider]\n") + + "Name = \"unix_domain_sockets\"\n\n" + + "[IpcChannel]\n" + + "Name = \"" + channelName + "\"\n"; +} + +void LoadRequiredModulesAndObjects(bool createUdsChannelControl = true) +{ + auto* pModuleControl = + sdv::core::GetObject("ModuleControlService"); + ASSERT_NE(pModuleControl, nullptr); + + EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); + EXPECT_NE(pModuleControl->Load(GetUdsModuleName()), 0); + EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); + + auto* pRepositoryControl = + sdv::core::GetObject("RepositoryService"); + ASSERT_NE(pRepositoryControl, nullptr); + + EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); + + if (createUdsChannelControl) + { + EXPECT_NE( + pRepositoryControl->CreateObject(GetUdsChannelControlClassName(), {}, {}), + 0); + } +} + +} // anonymous namespace + +TEST(IPC_Connect_Test_UDS, InstantiateListener) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" +Instance = 1234 + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + LoadRequiredModulesAndObjects(true); + + const std::string channelName = MakeChannelName("MyPersonalChannelName"); + + sdv::SObjectInfo sListenerInfo{}; + sListenerInfo.ssConfig = MakeUdsConfigByName(channelName); + + CListener listener; + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); + + listener.Initialize(sListenerInfo); + + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); + EXPECT_EQ(listener.GetProviderName(), "unix_domain_sockets"); + + listener.Shutdown(); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); + + appcontrol.Shutdown(); +} + +TEST(IPC_Connect_Test_UDS, InstantiateClientNoListener) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + LoadRequiredModulesAndObjects(true); + + const std::string channelName = MakeChannelName("MyPersonalChannelName"); + + sdv::SObjectInfo sClientInfo{}; + sClientInfo.ssConfig = MakeUdsConfigByName(channelName); + + CClientConnect client; + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); + client.Initialize(sClientInfo); + + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); + std::cout << "Connecting client..." << std::endl; + EXPECT_FALSE(client.Connect()); + std::cout << "Client failed to connect." << std::endl; + EXPECT_FALSE(client.IsConnected()); + + client.Shutdown(); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); + appcontrol.Shutdown(); +} + +TEST(IPC_Connect_Test_UDS, InstantiateClientAndListener) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + LoadRequiredModulesAndObjects(true); + + const std::string channelName = MakeChannelName("CHANNEL_1234"); + + sdv::SObjectInfo sListenerInfo{}; + sListenerInfo.ssConfig = MakeUdsConfigByName(channelName); + + CListener listener; + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); + + listener.Initialize(sListenerInfo); + + EXPECT_EQ(listener.GetProviderName(), "unix_domain_sockets"); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); + + // Small delay to avoid startup races between listener init and client connect. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + sdv::SObjectInfo sClientInfo{}; + sClientInfo.ssConfig = MakeUdsConfigByName(channelName); + + CClientConnect client; + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); + + client.Initialize(sClientInfo); + + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); + EXPECT_TRUE(client.Connect()); + EXPECT_TRUE(client.IsConnected()); + + // The client should expose the remote repository after successful connect. + sdv::core::IObjectAccess* pObjectAccess = + sdv::TInterfaceAccessPtr(client.GetRemoteRepository()) + .GetInterface(); + EXPECT_NE(pObjectAccess, nullptr); + + sdv::core::IModuleInfo* pModuleInfo = nullptr; + if (pObjectAccess) + { + pModuleInfo = + sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ModuleControlService")) + .GetInterface(); + } + + EXPECT_NE(pModuleInfo, nullptr); + if (pModuleInfo) + { + EXPECT_FALSE(pModuleInfo->GetModuleList().empty()); + } + + EXPECT_TRUE(client.Disconnect()); + std::cout << "Client Shutdown" << std::endl; + client.Shutdown(); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); + std::cout << "Listener Shutdown" << std::endl; + listener.Shutdown(); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); + + appcontrol.Shutdown(); +} \ No newline at end of file diff --git a/tests/unit_tests/ipc_connect/ipc_connect_uds_tunnel.cpp b/tests/unit_tests/ipc_connect/ipc_connect_uds_tunnel.cpp new file mode 100644 index 0000000..47bf524 --- /dev/null +++ b/tests/unit_tests/ipc_connect/ipc_connect_uds_tunnel.cpp @@ -0,0 +1,278 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Denisa Ros - initial API and implementation + ********************************************************************************/ + +#include "../../include/gtest_custom.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "../../../sdv_services/ipc_connect/listener.h" +#include "../../../sdv_services/ipc_connect/client.h" + +namespace +{ + +const char* GetTunnelModuleName() +{ +#ifdef _WIN32 + return "uds_win_tunnel.sdv"; +#else + return "uds_unix_tunnel.sdv"; +#endif +} + +const char* GetTunnelChannelControlClassName() +{ +#ifdef _WIN32 + return "WinTunnelChannelControl"; +#else + return "UnixTunnelChannelControl"; +#endif +} + +std::string UniqueSuffix() +{ + const auto now = std::chrono::steady_clock::now().time_since_epoch().count(); + return std::to_string(static_cast(now)); +} + +std::string MakeChannelName(const char* base) +{ + return std::string(base) + "_" + UniqueSuffix(); +} + +std::string MakeTunnelName(const char* base) +{ + return std::string("t_") + base + "_" + UniqueSuffix(); +} + +std::string MakeTunnelConfigByName(const std::string& channelName, + const std::string& tunnelName) +{ + return std::string("[Provider]\n") + + "Name = \"unix_domain_sockets_tunnel\"\n\n" + + "[IpcChannel]\n" + + "Name = \"" + channelName + "\"\n" + + "Tunnel = \"" + tunnelName + "\"\n"; +} + +void LoadRequiredModulesAndObjects(bool createTunnelChannelControl = true) +{ + auto* pModuleControl = + sdv::core::GetObject("ModuleControlService"); + ASSERT_NE(pModuleControl, nullptr); + + EXPECT_NE(pModuleControl->Load("ipc_com.sdv"), 0); + EXPECT_NE(pModuleControl->Load(GetTunnelModuleName()), 0); + EXPECT_NE(pModuleControl->Load("core_ps.sdv"), 0); + + auto* pRepositoryControl = + sdv::core::GetObject("RepositoryService"); + ASSERT_NE(pRepositoryControl, nullptr); + + EXPECT_NE(pRepositoryControl->CreateObject("CommunicationControl", {}, {}), 0); + + if (createTunnelChannelControl) + { + EXPECT_NE( + pRepositoryControl->CreateObject(GetTunnelChannelControlClassName(), {}, {}), + 0); + } +} + +} // anonymous namespace + +TEST(IPC_Connect_Test_UDS_Tunnel, InstantiateListener) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" +Instance = 1234 + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + LoadRequiredModulesAndObjects(true); + + const std::string channelName = MakeChannelName("MyPersonalChannelName"); + const std::string tunnelName = MakeTunnelName("listener"); + + sdv::SObjectInfo sListenerInfo{}; + sListenerInfo.ssConfig = MakeTunnelConfigByName(channelName, tunnelName); + + CListener listener; + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); + + listener.Initialize(sListenerInfo); + + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); + EXPECT_EQ(listener.GetProviderName(), "unix_domain_sockets_tunnel"); + + listener.Shutdown(); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); + + appcontrol.Shutdown(); +} + +TEST(IPC_Connect_Test_UDS_Tunnel, InstantiateClientNoListener) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + LoadRequiredModulesAndObjects(true); + + const std::string channelName = MakeChannelName("MyPersonalChannelName"); + const std::string tunnelName = MakeTunnelName("no_listener"); + + sdv::SObjectInfo sClientInfo{}; + sClientInfo.ssConfig = MakeTunnelConfigByName(channelName, tunnelName); + + CClientConnect client; + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); + + client.Initialize(sClientInfo); + + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); + EXPECT_FALSE(client.Connect()); + EXPECT_FALSE(client.IsConnected()); + + client.Shutdown(); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); + + appcontrol.Shutdown(); +} + +TEST(IPC_Connect_Test_UDS_Tunnel, InstantiateClientAndListener) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + LoadRequiredModulesAndObjects(true); + + const std::string channelName = MakeChannelName("CHANNEL_1234"); + const std::string tunnelName = MakeTunnelName("client_listener"); + + sdv::SObjectInfo sListenerInfo{}; + sListenerInfo.ssConfig = MakeTunnelConfigByName(channelName, tunnelName); + + CListener listener; + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialization_pending); + + listener.Initialize(sListenerInfo); + + EXPECT_EQ(listener.GetProviderName(), "unix_domain_sockets_tunnel"); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::initialized); + + // Allow listener endpoint publication to complete before client connect. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + sdv::SObjectInfo sClientInfo{}; + sClientInfo.ssConfig = MakeTunnelConfigByName(channelName, tunnelName); + + CClientConnect client; + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialization_pending); + + client.Initialize(sClientInfo); + + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::initialized); + EXPECT_TRUE(client.Connect()); + EXPECT_TRUE(client.IsConnected()); + + // The client should expose the remote repository after successful connect. + sdv::core::IObjectAccess* pObjectAccess = + sdv::TInterfaceAccessPtr(client.GetRemoteRepository()) + .GetInterface(); + EXPECT_NE(pObjectAccess, nullptr); + + sdv::core::IModuleInfo* pModuleInfo = nullptr; + if (pObjectAccess) + { + pModuleInfo = + sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ModuleControlService")) + .GetInterface(); + } + + EXPECT_NE(pModuleInfo, nullptr); + if (pModuleInfo) + { + EXPECT_FALSE(pModuleInfo->GetModuleList().empty()); + } + + EXPECT_TRUE(client.Disconnect()); + + client.Shutdown(); + EXPECT_EQ(client.GetObjectState(), sdv::EObjectState::destruction_pending); + + listener.Shutdown(); + EXPECT_EQ(listener.GetObjectState(), sdv::EObjectState::destruction_pending); + + appcontrol.Shutdown(); +} + +TEST(IPC_Connect_Test_UDS_Tunnel, InvalidProvider_ShouldNotInitializeClient) +{ + sdv::app::CAppControl appcontrol; + ASSERT_TRUE(appcontrol.Startup(R"config( +[Application] +Mode = "Essential" + +[LogHandler] +ViewFilter = "Fatal" +)config")); + ASSERT_TRUE(appcontrol.IsRunning()); + + LoadRequiredModulesAndObjects(true); + + sdv::SObjectInfo sClientInfo{}; + sClientInfo.ssConfig = R"toml( +[Provider] +Name = "invalid_tunnel_provider" + +[IpcChannel] +Name = "CHANNEL_1234" +Tunnel = "t_invalid_provider" +)toml"; + + CClientConnect client; + client.Initialize(sClientInfo); + + EXPECT_NE(client.GetObjectState(), sdv::EObjectState::initialized); + EXPECT_FALSE(client.Connect()); + EXPECT_FALSE(client.IsConnected()); + + client.Shutdown(); + appcontrol.Shutdown(); +} \ No newline at end of file diff --git a/tests/unit_tests/module_control/main.cpp b/tests/unit_tests/module_control/main.cpp index 8a8e354..bd16c43 100644 --- a/tests/unit_tests/module_control/main.cpp +++ b/tests/unit_tests/module_control/main.cpp @@ -21,6 +21,7 @@ #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../../sdv_services/core/toml_parser/miscellaneous.cpp" #include "../../../sdv_services/core/toml_parser/code_snippet.cpp" +#include "../../../sdv_services/core/toml_parser/parser_node_indexer.cpp" #include "../../../sdv_services/core/module_control.cpp" #include "../../../sdv_services/core/module.cpp" #include "../../../sdv_services/core/app_config.cpp" diff --git a/tests/unit_tests/module_control/mock.h b/tests/unit_tests/module_control/mock.h index 9f147fb..b008725 100644 --- a/tests/unit_tests/module_control/mock.h +++ b/tests/unit_tests/module_control/mock.h @@ -46,6 +46,7 @@ public: bool IsConsoleSilent() { return true; } bool IsConsoleVerbose() { return false; } uint32_t GetInstanceID() { return 1234u; } + std::filesystem::path GetFrameworkDir() const { return GetExecDirectory(); } std::filesystem::path GetRootDir() const { return GetExecDirectory(); } std::filesystem::path GetInstallDir() const { return GetExecDirectory(); } std::vector GetSystemConfigPaths() const { return {}; } @@ -117,7 +118,4 @@ inline CRepository& GetRepository() #include "../../../sdv_services/core/app_settings.h" #include "../../../sdv_services/core/app_config.h" -//inline std::filesystem::path GetCoreDirectoryMock() { return "../../bin"; } -//#define GetCoreDirectory GetCoreDirectoryMock - #endif // !defined MOCK_H \ No newline at end of file diff --git a/tests/unit_tests/named_mutex/named_mutex_test.cpp b/tests/unit_tests/named_mutex/named_mutex_test.cpp index c1a62c2..c16e3c6 100644 --- a/tests/unit_tests/named_mutex/named_mutex_test.cpp +++ b/tests/unit_tests/named_mutex/named_mutex_test.cpp @@ -17,6 +17,16 @@ #include #include #include +#include + +namespace +{ +std::string MakeUniqueMutexName(const char* suffix) +{ + return std::string("HELLO_") + suffix + "_" + + std::to_string(std::chrono::high_resolution_clock::now().time_since_epoch().count()); +} +} #if defined(_WIN32) && defined(_UNICODE) extern "C" int wmain(int argc, wchar_t* argv[]) @@ -32,12 +42,14 @@ extern "C" int main(int argc, char* argv[]) TEST(NamedMutexTest, Construction) { - ipc::named_mutex mtx("HELLO"); + ipc::named_mutex mtx(MakeUniqueMutexName("Construction")); EXPECT_NE(mtx.native_handle(), nullptr); } TEST(NamedMutexTest, CritSectSyncManualLock) { + const std::string mutexName = MakeUniqueMutexName("CritSectSyncManualLock"); + // Counter function check for correct counter value. // The checking is manipulated by the bEnable flag. When disabled, no sync will be done and the check will fail. When enabled, // sync will be done and the check will succeed. @@ -46,7 +58,7 @@ TEST(NamedMutexTest, CritSectSyncManualLock) std::atomic_bool bEnable = false; auto fn = [&]() { - ipc::named_mutex mtx("HELLO"); + ipc::named_mutex mtx(mutexName); if (bEnable) mtx.lock(); @@ -81,12 +93,14 @@ TEST(NamedMutexTest, CritSectSyncManualLock) TEST(NamedMutexTest, CritSectSyncAutoLock) { + const std::string mutexName = MakeUniqueMutexName("CritSectSyncAutoLock"); + // Counter function check for correct counter value. int32_t iCnt = 0; std::atomic_bool bSuccess = true; auto fn = [&]() { - ipc::named_mutex mtx("HELLO"); + ipc::named_mutex mtx(mutexName); std::unique_lock lock(mtx); bSuccess = bSuccess && (iCnt == 0); @@ -108,10 +122,12 @@ TEST(NamedMutexTest, CritSectSyncAutoLock) TEST(NamedMutexTest, TryLock) { + const std::string mutexName = MakeUniqueMutexName("TryLock"); + std::atomic_bool bRunning = false; auto fn = [&]() { - ipc::named_mutex mtx("HELLO"); + ipc::named_mutex mtx(mutexName); mtx.lock(); while (bRunning) std::this_thread::sleep_for(std::chrono::milliseconds(1)); @@ -123,7 +139,7 @@ TEST(NamedMutexTest, TryLock) std::this_thread::sleep_for(std::chrono::milliseconds(250)); // Try locking; doesn't work since thread still locks. - ipc::named_mutex mtx2("HELLO"); + ipc::named_mutex mtx2(mutexName); EXPECT_FALSE(mtx2.try_lock()); bRunning = false; @@ -137,7 +153,7 @@ TEST(NamedMutexTest, TryLock) TEST(NamedMutexTest, Naming) { - ipc::named_mutex mtx1("HELLO"); + ipc::named_mutex mtx1(MakeUniqueMutexName("Naming")); EXPECT_FALSE(mtx1.name().empty()); ipc::named_mutex mtx2; diff --git a/tests/unit_tests/parameters/param_access.cpp b/tests/unit_tests/parameters/param_access.cpp index f5ad753..cec4c9f 100644 --- a/tests/unit_tests/parameters/param_access.cpp +++ b/tests/unit_tests/parameters/param_access.cpp @@ -834,22 +834,23 @@ public: InitParamMap(); } + BEGIN_SDV_PARAM_MAP() SDV_PARAM_SET_READONLY() - SDV_PARAM_ENTRY(m_i, "my_integer", 10, "int_unit", "My integer") + SDV_PARAM_ENTRY(m_i, "my_integer", 20, "int_unit", "My integer") SDV_PARAM_RESET_READONLY() SDV_PARAM_ENTRY(m_d, "my_double", 1234.5, "double_unit", "My double") SDV_PARAM_SET_READONLY() - SDV_PARAM_ENTRY(m_ss, "my_string", "string_value", "string_unit", "My string") + SDV_PARAM_ENTRY(m_ss, "my_string", "value_string", "string_unit", "My string") SDV_PARAM_RESET_ATTRIBUTES() SDV_PARAM_ENTRY(m_b, "my_boolean", true, "no_unit", "My boolean") END_SDV_PARAM_MAP() private: - int m_i; - double m_d; - std::string m_ss; - bool m_b; + int m_i = 10; // Read only; will not be overwritten + double m_d = 5432.1; // Writable, will be overwritten + std::string m_ss = "string_value"; // Read only; will not be overwritten + bool m_b = false; // Writable, will be overwritten }; TEST(ParameterTest, ReadOnlyParamMapStaticInfo) @@ -869,7 +870,7 @@ TEST(ParameterTest, ReadOnlyParamMapStaticInfo) EXPECT_EQ(ptrParamInfo->Name(), "my_integer"); EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); EXPECT_EQ(ptrParamInfo->Description(), "My integer"); - EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 20); EXPECT_TRUE(ptrParamInfo->ReadOnly()); // Get parameter #1 global information @@ -889,7 +890,7 @@ TEST(ParameterTest, ReadOnlyParamMapStaticInfo) EXPECT_EQ(ptrParamInfo->Name(), "my_string"); EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); EXPECT_EQ(ptrParamInfo->Description(), "My string"); - EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "value_string"); EXPECT_TRUE(ptrParamInfo->ReadOnly()); // Get parameter #3 global information @@ -922,7 +923,7 @@ TEST(ParameterTest, ReadOnlyParamMapObjectInfo) EXPECT_TRUE(ptrParamInfo->Numeric()); EXPECT_EQ(ptrParamInfo->Unit(), "int_unit"); EXPECT_EQ(ptrParamInfo->Description(), "My integer"); - EXPECT_EQ(ptrParamInfo->DefaultVal(), 10); + EXPECT_EQ(ptrParamInfo->DefaultVal(), 20); EXPECT_TRUE(ptrParamInfo->ReadOnly()); // Get parameter #1 global information @@ -940,7 +941,7 @@ TEST(ParameterTest, ReadOnlyParamMapObjectInfo) EXPECT_TRUE(ptrParamInfo->String()); EXPECT_EQ(ptrParamInfo->Unit(), "string_unit"); EXPECT_EQ(ptrParamInfo->Description(), "My string"); - EXPECT_EQ(ptrParamInfo->DefaultVal(), "string_value"); + EXPECT_EQ(ptrParamInfo->DefaultVal(), "value_string"); EXPECT_TRUE(ptrParamInfo->ReadOnly()); // Get parameter #3 global information @@ -971,7 +972,7 @@ TEST(ParameterTest, ReadOnlyParamMapObjectInfoIndirect) EXPECT_TRUE(info.Numeric()); EXPECT_EQ(info.Unit(), "int_unit"); EXPECT_EQ(info.Description(), "My integer"); - EXPECT_EQ(info.DefaultVal(), 10); + EXPECT_EQ(info.DefaultVal(), 20); EXPECT_TRUE(info.ReadOnly()); // Get parameter #1 global information @@ -987,7 +988,7 @@ TEST(ParameterTest, ReadOnlyParamMapObjectInfoIndirect) EXPECT_TRUE(info.String()); EXPECT_EQ(info.Unit(), "string_unit"); EXPECT_EQ(info.Description(), "My string"); - EXPECT_EQ(info.DefaultVal(), "string_value"); + EXPECT_EQ(info.DefaultVal(), "value_string"); EXPECT_TRUE(info.ReadOnly()); // Get parameter #2 global information diff --git a/tests/unit_tests/permission_control/CMakeLists.txt b/tests/unit_tests/permission_control/CMakeLists.txt new file mode 100644 index 0000000..9bacda3 --- /dev/null +++ b/tests/unit_tests/permission_control/CMakeLists.txt @@ -0,0 +1,33 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 ZF Friedrichshafen AG +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Erik Verhoeven - initial API and implementation +#******************************************************************************* + +# Define project +project (UnitTest_PermissionControl VERSION 1.0 LANGUAGES CXX) + +# Compile the source code +add_executable(UnitTest_PermissionControl + "main.cpp" + "permission_control_tests.cpp") +target_link_libraries(UnitTest_PermissionControl ${CMAKE_DL_LIBS} GTest::GTest) + +# Add the IDL Compiler unittest +add_test(NAME UnitTest_PermissionControl COMMAND UnitTest_PermissionControl) + +# Execute the test +add_custom_command(TARGET UnitTest_PermissionControl POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_PermissionControl.xml + VERBATIM +) + +# Build dependencies +add_dependencies(UnitTest_PermissionControl dependency_sdv_components) diff --git a/tests/unit_tests/app_connect/main.cpp b/tests/unit_tests/permission_control/main.cpp similarity index 93% rename from tests/unit_tests/app_connect/main.cpp rename to tests/unit_tests/permission_control/main.cpp index e988681..6563070 100644 --- a/tests/unit_tests/app_connect/main.cpp +++ b/tests/unit_tests/permission_control/main.cpp @@ -13,7 +13,7 @@ #include #include "../../../global/process_watchdog.h" -#include "../../../global/localmemmgr.h" +#include "../../../sdv_services/core/permission_control.cpp" /** * @brief Main function @@ -26,7 +26,6 @@ extern "C" int main(int argc, char* argv[]) { CProcessWatchdog watchdog; - CLocalMemMgr memmgr; ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/tests/unit_tests/permission_control/permission_control_tests.cpp b/tests/unit_tests/permission_control/permission_control_tests.cpp new file mode 100644 index 0000000..e96fb45 --- /dev/null +++ b/tests/unit_tests/permission_control/permission_control_tests.cpp @@ -0,0 +1,264 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include "../../include/gtest_custom.h" +#include "../../../sdv_services/core/permission_control.h" + +TEST(PermissionControl, DefaultRestriction) +{ + CPermissionControl control; + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); +} + +TEST(PermissionControl, SetRestrictionInternal) +{ + CPermissionControl control; + std::vector vecPermissions; + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Full access + vecPermissions.emplace_back(control.CreatePermissionObject(sdv::core::EAccessPermission::full_access)); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::full_access); + + // Reduce access to local access + vecPermissions.emplace_back(control.CreatePermissionObject(sdv::core::EAccessPermission::local_access)); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::local_access); + + // Reduce access to remote access + vecPermissions.emplace_back(control.CreatePermissionObject(sdv::core::EAccessPermission::remote_access)); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::remote_access); + + // Reduce access to restricted access + vecPermissions.emplace_back(control.CreatePermissionObject(sdv::core::EAccessPermission::restricted_access)); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Try increase access to full access - this should not have any change + vecPermissions.emplace_back(control.CreatePermissionObject(sdv::core::EAccessPermission::full_access)); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Remove the local access index #1 - this should not have any change + EXPECT_EQ(vecPermissions[1].Permission(), sdv::core::EAccessPermission::local_access); + vecPermissions.erase(vecPermissions.begin() + 1); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Remove first full access index #0 - this should not have any change + EXPECT_EQ(vecPermissions[0].Permission(), sdv::core::EAccessPermission::full_access); + vecPermissions.erase(vecPermissions.begin()); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Remove remote access new index #0 - this should not have any change + EXPECT_EQ(vecPermissions[0].Permission(), sdv::core::EAccessPermission::remote_access); + vecPermissions.erase(vecPermissions.begin()); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Remove restricted access new index #0 - access will change to full access + EXPECT_EQ(vecPermissions[0].Permission(), sdv::core::EAccessPermission::restricted_access); + vecPermissions.erase(vecPermissions.begin()); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::full_access); + + // Remove the remaining access - this would change to (default) restricted access again + vecPermissions.clear(); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); +} + +TEST(PermissionControl, SetRestrictionExternal) +{ + CPermissionControl control; + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Try to set external permission to full access; this will not work + EXPECT_EQ(control.RestrictAccessPermission(sdv::core::EAccessPermission::full_access), 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Try to set external permission to local access; this will not work + EXPECT_EQ(control.RestrictAccessPermission(sdv::core::EAccessPermission::local_access), 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Try to set external permission to remote access; this will not work + EXPECT_EQ(control.RestrictAccessPermission(sdv::core::EAccessPermission::remote_access), 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Try to set external permission to restricted access; this will not work + EXPECT_EQ(control.RestrictAccessPermission(sdv::core::EAccessPermission::restricted_access), 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); +} + +TEST(PermissionControl, InitializeRestrictionInternal_SetRestrictionExternal) +{ + CPermissionControl control; + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Set restriction to full access + std::optional optPermission = control.CreatePermissionObject(sdv::core::EAccessPermission::full_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::full_access); + + // Set external permission to local access + sdv::core::TPermissionID permidLocalAccess = control.RestrictAccessPermission(sdv::core::EAccessPermission::local_access); + EXPECT_NE(permidLocalAccess, 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::local_access); + + // Set external permission to remote access + sdv::core::TPermissionID permidRemoteAccess = control.RestrictAccessPermission(sdv::core::EAccessPermission::remote_access); + EXPECT_NE(permidRemoteAccess, 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::remote_access); + + // Set external permission to restricted access + sdv::core::TPermissionID permidRestrictedAccess = + control.RestrictAccessPermission(sdv::core::EAccessPermission::restricted_access); + EXPECT_NE(permidRestrictedAccess, 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Set external permission to full access - this will not change the access + sdv::core::TPermissionID permidFullAccess = control.RestrictAccessPermission(sdv::core::EAccessPermission::full_access); + EXPECT_NE(permidFullAccess, 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Release not existing access ID - should not work + EXPECT_FALSE(control.ReleaseAccessPermission(0)); + EXPECT_FALSE(control.ReleaseAccessPermission(1)); + + // Release local, restricted and full access - this will change access to remote access + EXPECT_TRUE(control.ReleaseAccessPermission(permidLocalAccess)); + EXPECT_TRUE(control.ReleaseAccessPermission(permidRestrictedAccess)); + EXPECT_TRUE(control.ReleaseAccessPermission(permidFullAccess)); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::remote_access); + + // Release local again - should not work + EXPECT_FALSE(control.ReleaseAccessPermission(permidLocalAccess)); + + // Terminate internal full access - access should stay with remote access + optPermission.reset(); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::remote_access); + + // Release remote access - the access permission will change to restricted access again + EXPECT_TRUE(control.ReleaseAccessPermission(permidRemoteAccess)); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); +} + +TEST(PermissionControl, DefaultThreadPermission) +{ + CPermissionControl control; + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Set restriction to full access + CAccessPermission permission = control.CreatePermissionObject(sdv::core::EAccessPermission::full_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::full_access); + + // Start new thread - access should be restricted + std::thread thread([&]() + { + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + }); + thread.join(); +} + +TEST(PermissionControl, InternalSetThreadPermission) +{ + CPermissionControl control; + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Set restriction to local access + CAccessPermission permission = control.CreatePermissionObject(sdv::core::EAccessPermission::local_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::local_access); + + // Start new thread - access should be restricted + std::thread thread([&]() + { + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Set full access permission + CAccessPermission permissionFullAccess = control.CreatePermissionObject(sdv::core::EAccessPermission::full_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::full_access); + + // Set local access permission + CAccessPermission permissionLocalAccess = control.CreatePermissionObject(sdv::core::EAccessPermission::local_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::local_access); + + // Set remote access permission + CAccessPermission permissionRemoteAccess = control.CreatePermissionObject(sdv::core::EAccessPermission::remote_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::remote_access); + + // Set restricted access permission + CAccessPermission permissionRestrictedAccess = + control.CreatePermissionObject(sdv::core::EAccessPermission::restricted_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + }); + thread.join(); + + // Access permissions on main thread should still be local + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::local_access); +} + +TEST(PermissionControl, TransferThreadPermission) +{ + CPermissionControl control; + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Set restriction to full access + CAccessPermission permissionFullAccess = control.CreatePermissionObject(sdv::core::EAccessPermission::full_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::full_access); + sdv::core::TPermissionID transferFullAccess = control.TransferCurrentPermission(); + EXPECT_NE(transferFullAccess, 0u); + + // Set restriction to local access + CAccessPermission permissionLocalAccess = control.CreatePermissionObject(sdv::core::EAccessPermission::local_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::local_access); + sdv::core::TPermissionID transferLocalAccess = control.TransferCurrentPermission(); + EXPECT_NE(transferLocalAccess, 0u); + + // Set restriction to remote access + CAccessPermission permissionRemoteAccess = control.CreatePermissionObject(sdv::core::EAccessPermission::remote_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::remote_access); + sdv::core::TPermissionID transferRemoteAccess = control.TransferCurrentPermission(); + EXPECT_NE(transferRemoteAccess, 0u); + + // Set restriction to restricted access + CAccessPermission permissionRestrictedAccess = control.CreatePermissionObject(sdv::core::EAccessPermission::restricted_access); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + sdv::core::TPermissionID transferRestrictedAccess = control.TransferCurrentPermission(); + EXPECT_NE(transferRestrictedAccess, 0u); + + // Start new thread - access should be restricted + std::thread thread([&]() + { + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // Tets bogus transfer IDs + EXPECT_FALSE(control.SetAccessPermission(0)); + EXPECT_FALSE(control.SetAccessPermission(1)); + + // Set full access permission from transfer ID + sdv::core::TPermissionID permidFullAccess = control.SetAccessPermission(transferFullAccess); + EXPECT_NE(permidFullAccess, 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::full_access); + + // Set local access permission from transfer ID + sdv::core::TPermissionID permidLocalAccess = control.SetAccessPermission(transferLocalAccess); + EXPECT_NE(permidLocalAccess, 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::local_access); + + // Set remote access permission from transfer ID + sdv::core::TPermissionID permidRemoteAccess = control.SetAccessPermission(transferRemoteAccess); + EXPECT_NE(permidRemoteAccess, 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::remote_access); + + // Set restricted access permission from transfer ID + sdv::core::TPermissionID permidRestrictedAccess = control.SetAccessPermission(transferRestrictedAccess); + EXPECT_NE(permidRestrictedAccess, 0u); + EXPECT_EQ(control.GetCurrentPermission(), sdv::core::EAccessPermission::restricted_access); + + // At the end of the thread, the IDs will be cleaned up automatically. + }); + thread.join(); +} \ No newline at end of file diff --git a/tests/unit_tests/process_control/process_control_app.cpp b/tests/unit_tests/process_control/process_control_app.cpp index 2fa1f08..efe57fb 100644 --- a/tests/unit_tests/process_control/process_control_app.cpp +++ b/tests/unit_tests/process_control/process_control_app.cpp @@ -44,8 +44,8 @@ extern "C" int main(int argc, char* argv[]) EOperatingmode eMode = static_cast(std::atoi(sdv::MakeAnsiString(argv[1]).c_str())); sdv::app::CAppControl appcontrol; - if (!appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")) + if (!appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")) { std::cout << GetTimestamp() << "Failed to start app control..." << std::endl; return -1; @@ -89,7 +89,7 @@ Mode="Maintenance")code")) std::cout << GetTimestamp() << "Waiting for process with PID#" << std::dec << tProcessID << std::endl; CProcessMonitorHelper monitor; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation uint32_t uiMon = control.RegisterMonitor(tProcessID, &monitor); if (!uiMon) { @@ -134,7 +134,7 @@ Mode="Maintenance")code")) std::cout << GetTimestamp() << "Terminate process with PID#" << std::dec << tProcessID << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Make certain, that the first process is actually running... CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation nResult = control.Terminate(tProcessID) ? 0 : -20; control.Shutdown(); // Needed to prevent clash with core } diff --git a/tests/unit_tests/process_control/process_control_tests.cpp b/tests/unit_tests/process_control/process_control_tests.cpp index 7ffb029..fe72cd7 100644 --- a/tests/unit_tests/process_control/process_control_tests.cpp +++ b/tests/unit_tests/process_control/process_control_tests.cpp @@ -23,8 +23,8 @@ TEST(ProcessControlTest, Instantiate) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessControl control; EXPECT_EQ(control.GetProcessID(), static_cast(getpid())); @@ -35,11 +35,11 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ParentRightsExecuteProcessNormalShutdown) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -61,11 +61,11 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ParentRightsExecuteProcessEmergencyExit) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -87,13 +87,13 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ParentRightsExecuteProcessNormalShutdownWithMonitor) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -119,13 +119,13 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ParentRightsExecuteProcessEmergencyExitWithMonitor) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -151,14 +151,14 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ParentRightsExecuteMultiProcessNormalShutdown) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor1; CProcessMonitorHelper monitor2; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -198,14 +198,14 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ParentRightsExecuteMultiEmergencyAccess) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor1; CProcessMonitorHelper monitor2; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -244,13 +244,13 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ParentRightsExecuteProcessAndTerminate) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -265,7 +265,7 @@ Mode="Maintenance")code")); EXPECT_NE(uiCookie, 0u); bool bTerminateResult = false; - std::thread thread([&]() + sdv::core::secure_thread thread([&]() { std::this_thread::sleep_for(std::chrono::milliseconds(250)); bTerminateResult = control.Terminate(tProcessID); @@ -285,14 +285,14 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ParentRightsExecuteMultiTerminate) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor1; CProcessMonitorHelper monitor2; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -330,11 +330,11 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ReducedRightsExecuteProcessNormalShutdown) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -356,11 +356,11 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ReducedRightsExecuteProcessEmergencyExit) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -382,13 +382,13 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ReducedRightsExecuteProcessNormalShutdownWithMonitor) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -414,13 +414,13 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ReducedRightsExecuteProcessEmergencyExitWithMonitor) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -446,14 +446,14 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ReducedRightsExecuteMultiProcessNormalShutdown) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor1; CProcessMonitorHelper monitor2; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -491,14 +491,14 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ReducedRightsExecuteMultiEmergencyAccess) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor1; CProcessMonitorHelper monitor2; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -537,13 +537,13 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ReducedRightsExecuteProcessAndTerminate) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -558,7 +558,7 @@ Mode="Maintenance")code")); EXPECT_NE(uiCookie, 0u); bool bTerminateResult = false; - std::thread thread([&]() + sdv::core::secure_thread thread([&]() { std::this_thread::sleep_for(std::chrono::milliseconds(250)); bTerminateResult = control.Terminate(tProcessID); @@ -578,14 +578,14 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ReducedRightsExecuteMultiTerminate) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor1; CProcessMonitorHelper monitor2; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else @@ -623,15 +623,15 @@ Mode="Maintenance")code")); TEST(ProcessControlTest, ExecuteProcessNormalShutdownWithMultipleMonitors) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode="Maintenance")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode="Maintenance")toml")); CProcessMonitorHelper monitor1; CProcessMonitorHelper monitor2; CProcessMonitorHelper monitor3; CProcessControl control; - control.Initialize(""); // Needed since local instantiation + control.Initialize(sdv::SObjectInfo()); // Needed since local instantiation #ifdef _WIN32 const std::string ssModule = "UnitTest_ProcessControlApp.exe"; #else diff --git a/tests/unit_tests/repository/main.cpp b/tests/unit_tests/repository/main.cpp index acb1378..aaf7568 100644 --- a/tests/unit_tests/repository/main.cpp +++ b/tests/unit_tests/repository/main.cpp @@ -21,9 +21,11 @@ #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../../sdv_services/core/toml_parser/miscellaneous.cpp" #include "../../../sdv_services/core/toml_parser/code_snippet.cpp" +#include "../../../sdv_services/core/toml_parser/parser_node_indexer.cpp" #include "../../../sdv_services/core/module_control.cpp" #include "../../../sdv_services/core/module.cpp" #include "../../../sdv_services/core/repository.cpp" +#include "../../../sdv_services/core/permission_control.cpp" #include "../../../sdv_services/core/iso_monitor.cpp" #include "../../../sdv_services/core/object_lifetime_control.cpp" #include "../../../sdv_services/core/app_config.cpp" diff --git a/tests/unit_tests/repository/mock.h b/tests/unit_tests/repository/mock.h index ed08560..7136fcb 100644 --- a/tests/unit_tests/repository/mock.h +++ b/tests/unit_tests/repository/mock.h @@ -15,6 +15,7 @@ #define MOCK_H #include +#include #include #include "../../../global/exec_dir_helper.h" #include "../../../sdv_services/core/installation_manifest.h" @@ -24,6 +25,7 @@ #define APP_CONTROL_H #define LOGGER_H #define LOGGER_CONTROL_H +//#define PERMISSION_CONTROL_H /** * @brief CAppSettings redefined @@ -45,6 +47,7 @@ public: bool IsConsoleSilent() { return true; } bool IsConsoleVerbose() { return false; } uint32_t GetInstanceID() { return 1000u; } + std::filesystem::path GetFrameworkDir() const { return GetExecDirectory(); } std::filesystem::path GetRootDir() const { return GetExecDirectory(); } std::filesystem::path GetInstallDir() const { return GetExecDirectory(); } std::vector GetSystemConfigPaths() const { return {}; } @@ -105,15 +108,36 @@ inline CLoggerControl& GetLoggerControl() return logger_control; } +///** +// * @brief CPermissionControl redefined +// */ +//class CPermissionControl : public sdv::IInterfaceAccess +//{ +//public: +// BEGIN_SDV_INTERFACE_MAP() +// END_SDV_INTERFACE_MAP() +// +// // CPermissionControl mocked functions +// virtual sdv::core::EAccessPermission GetCurrentPermission() const { return sdv::core::EAccessPermission::full_access; } +//}; +// +///** +// * @brief Return the permission control. +// * @return Reference to the permission control. +// */ +//inline CPermissionControl& GetPermissionControl() +//{ +// static CPermissionControl control; +// return control; +//} +// #include "../../../sdv_services/core/toml_parser/parser_toml.h" #include "../../../sdv_services/core/toml_parser/parser_node_toml.h" #include "../../../sdv_services/core/module_control.h" #include "../../../sdv_services/core/repository.h" +#include "../../../sdv_services/core/permission_control.h" #include "../../../sdv_services/core/app_config.h" -//inline std::filesystem::path GetCoreDirectoryMock() { return "../../bin"; } -//#define GetCoreDirectory GetCoreDirectoryMock - class CHelper { public: diff --git a/tests/unit_tests/repository/repository_test.cpp b/tests/unit_tests/repository/repository_test.cpp index c383aef..775411e 100644 --- a/tests/unit_tests/repository/repository_test.cpp +++ b/tests/unit_tests/repository/repository_test.cpp @@ -29,7 +29,7 @@ public: SDV_INTERFACE_ENTRY(sdv::IObjectControl) END_SDV_INTERFACE_MAP() - virtual void Initialize([[maybe_unused]] const sdv::u8string& ssObjectConfig) override + virtual void Initialize([[maybe_unused]] const sdv::SObjectInfo& sObjectInfo) override { FAIL() << "Error: Initialize should not be called by Repo Service!"; //m_eObjectState = sdv::EObjectState::initialization_failure; @@ -91,8 +91,8 @@ TEST(RepositoryTest, CreateNonexistentClass) CModuleControl modulectrl; CHelper helper(modulectrl, repository); ASSERT_TRUE(modulectrl.Load((GetExecDirectory() / "UnitTest_Repository_test_module.sdv").generic_u8string())); - EXPECT_FALSE(repository.CreateObject2("TestFooBar", nullptr, nullptr)); - repository.DestroyObject2("TestFooBar"); + EXPECT_FALSE(repository.CreateObject("TestFooBar", nullptr, nullptr)); + EXPECT_FALSE(repository.DestroyObject("TestFooBar")); } TEST(RepositoryTest, GetNonexistentObject) @@ -100,11 +100,11 @@ TEST(RepositoryTest, GetNonexistentObject) CRepository repository; // Must be created first CModuleControl modulectrl; CHelper helper(modulectrl, repository); + auto permission = GetPermissionControl().CreatePermissionObject(sdv::core::EAccessPermission::full_access); ASSERT_TRUE(modulectrl.Load((GetExecDirectory() / "UnitTest_Repository_test_module.sdv").generic_u8string())); - bool bRes = repository.CreateObject2("Example_Object", nullptr, nullptr); - EXPECT_TRUE(bRes); + EXPECT_TRUE(repository.CreateObject("Example_Object", nullptr, nullptr)); EXPECT_EQ(nullptr, repository.GetObject("TestFooBar")); - repository.DestroyObject2("Example_Object"); + EXPECT_TRUE(repository.DestroyObject("Example_Object")); } TEST(RepositoryTest, InstantiateAndGet) @@ -112,11 +112,12 @@ TEST(RepositoryTest, InstantiateAndGet) CRepository repository; // Must be created first CModuleControl modulectrl; CHelper helper(modulectrl, repository); + auto permission = GetPermissionControl().CreatePermissionObject(sdv::core::EAccessPermission::full_access); ASSERT_TRUE(modulectrl.Load((GetExecDirectory() / "UnitTest_Repository_test_module.sdv").generic_u8string())); - bool bRes = repository.CreateObject2("Example_Object", nullptr, nullptr); + bool bRes = repository.CreateObject("Example_Object", nullptr, nullptr); EXPECT_TRUE(bRes); EXPECT_NE(nullptr, repository.GetObject("Example_Object")); - repository.DestroyObject2("Example_Object"); + EXPECT_TRUE(repository.DestroyObject("Example_Object")); EXPECT_EQ(nullptr, repository.GetObject("Example_Object")); } @@ -126,8 +127,7 @@ TEST(RepositoryTest, InstantiateInitFail) CModuleControl modulectrl; CHelper helper(modulectrl, repository); ASSERT_TRUE(modulectrl.Load((GetExecDirectory() / "UnitTest_Repository_test_module.sdv").generic_u8string())); - bool bRes = repository.CreateObject2("TestObject_IObjectControlFail", nullptr, nullptr); - EXPECT_FALSE(bRes); + EXPECT_FALSE(repository.CreateObject("TestObject_IObjectControlFail", nullptr, nullptr)); EXPECT_EQ(nullptr, repository.GetObject("TestObject_IObjectControlFail")); - repository.DestroyObject2("TestObject_IObjectControlFail"); + EXPECT_FALSE(repository.DestroyObject("TestObject_IObjectControlFail")); } diff --git a/tests/unit_tests/scheduler/scheduler_test.cpp b/tests/unit_tests/scheduler/scheduler_test.cpp index f5bfd1f..0c27d00 100644 --- a/tests/unit_tests/scheduler/scheduler_test.cpp +++ b/tests/unit_tests/scheduler/scheduler_test.cpp @@ -15,7 +15,7 @@ #include #include #include "../../../global/process_watchdog.h" -#include "../../../global/scheduler/scheduler.cpp" +#include "../../../global/scheduler/scheduler.h" #if defined(_WIN32) && defined(_UNICODE) extern "C" int wmain(int argc, wchar_t* argv[]) @@ -31,7 +31,7 @@ extern "C" int main(int argc, char* argv[]) TEST(TaskSchedulerTest, Construction) { - CTaskScheduler scheduler(2); + CTaskScheduler scheduler; EXPECT_EQ(scheduler.GetThreadCount(), 2); EXPECT_EQ(scheduler.GetBusyThreadCount(), 0); EXPECT_EQ(scheduler.GetIdleThreadCount(), 2); @@ -50,7 +50,7 @@ TEST(TaskSchedulerTest, Construction) TEST(TaskSchedulerTest, PreallocatedConcurrencyExecution) { - CTaskScheduler scheduler(4); + CTaskScheduler scheduler; // Schedule tasks onto 4 threads std::atomic_size_t nExecuted = 0; @@ -74,7 +74,7 @@ TEST(TaskSchedulerTest, PreallocatedConcurrencyExecution) TEST(TaskSchedulerTest, AllocateConcurrency) { - CTaskScheduler scheduler(2); + CTaskScheduler scheduler; // Schedule 4 tasks onto two threads pre-allocated and two just-in-time-allocated threads std::atomic_size_t nExecuted = 0; @@ -98,7 +98,7 @@ TEST(TaskSchedulerTest, AllocateConcurrency) TEST(TaskSchedulerTest, ConcurrencyAndTaskQueueing) { - CTaskScheduler scheduler(2, 2); + CTaskScheduler scheduler; // Schedule 4 tasks onto two threads std::atomic_size_t nExecuted = 0; @@ -122,7 +122,7 @@ TEST(TaskSchedulerTest, ConcurrencyAndTaskQueueing) TEST(TaskSchedulerTest, ConcurrencyAndDisallowTaskQueueing) { - CTaskScheduler scheduler(2, 2); + CTaskScheduler scheduler; // Schedule 4 tasks onto two threads.. Disallow the second task to be queued and the fourth task to be queued. std::atomic_size_t nExecuted = 0; @@ -133,7 +133,8 @@ TEST(TaskSchedulerTest, ConcurrencyAndDisallowTaskQueueing) while (bWait) std::this_thread::sleep_for(std::chrono::milliseconds(100)); nExecuted++; - }, (n % 2 ? CTaskScheduler::EScheduleFlags::no_queue: CTaskScheduler::EScheduleFlags::normal)); + }, + (n % 2 ? EScheduleFlags::no_queue : EScheduleFlags::normal)); bWait = false; // Wait until all threads are finalized @@ -146,7 +147,7 @@ TEST(TaskSchedulerTest, ConcurrencyAndDisallowTaskQueueing) TEST(TaskSchedulerTest, ConcurrencyAndPrioritizedTaskQueueing) { - CTaskScheduler scheduler(2, 2); + CTaskScheduler scheduler; // Schedule 5 tasks onto two threads.. Queue the fifth task with high priority. std::atomic_size_t nExecuted = 0; @@ -169,7 +170,7 @@ TEST(TaskSchedulerTest, ConcurrencyAndPrioritizedTaskQueueing) std::this_thread::sleep_for(std::chrono::milliseconds(300)); nExecuted++; - }, (n == 4 ? CTaskScheduler::EScheduleFlags::priority : CTaskScheduler::EScheduleFlags::normal)); + }, (n == 4 ? EScheduleFlags::priority : EScheduleFlags::normal)); bWait = false; // Wait until all threads are finalized diff --git a/tests/unit_tests/sdv_control/includes.h b/tests/unit_tests/sdv_control/includes.h index 6682f15..a5d79c4 100644 --- a/tests/unit_tests/sdv_control/includes.h +++ b/tests/unit_tests/sdv_control/includes.h @@ -36,13 +36,13 @@ public: m_uiInstanceID(uiInstanceID) { // Initialize system - std::string ssStartup = R"code( + std::string ssStartup = R"toml( [LogHandler] ViewFilter = "Fatal" [Application] Mode = "Maintenance" -)code"; +)toml"; if (uiInstanceID) ssStartup += "Instance = " + std::to_string(uiInstanceID) + "\n"; Startup(ssStartup); diff --git a/tests/unit_tests/shared_mem/app_connect.cpp b/tests/unit_tests/shared_mem/app_connect.cpp index ed72b5e..7e2e62d 100644 --- a/tests/unit_tests/shared_mem/app_connect.cpp +++ b/tests/unit_tests/shared_mem/app_connect.cpp @@ -42,7 +42,7 @@ public: */ CReceiver() { - m_threadSender = std::thread(&CReceiver::SendThreadFunc, this); + m_threadSender = sdv::core::secure_thread(&CReceiver::SendThreadFunc, this); } /** @@ -236,7 +236,7 @@ private: std::queue>> m_queueSendData; ///< Queue for sending data. std::condition_variable m_cvDisconnect; ///< Disconnect event. std::condition_variable m_cvReceived; ///< Receive event. - std::thread m_threadSender; ///< Thread to send data. + sdv::core::secure_thread m_threadSender; ///< Thread to send data. std::atomic_bool m_bConnected = false; ///< Set when connected was triggered. std::atomic_bool m_bDisconnect = false; ///< Set when shutdown was triggered. std::atomic_bool m_bShutdown = false; ///< Set when shutdown is processed. @@ -320,9 +320,9 @@ extern "C" int main(int argc, char* argv[]) TRACE("Forced termination of app ", bServer ? "server" : "client", " process is ", bForceTerminate ? "enabled" : "disabled"); TRACE("Long life of app ",bServer ? "server" : "client", " process is ", bLongLife ? "enabled" : "disabled"); - // Create an control management channel (if required). + // Create a control management channel (if required). CSharedMemChannelMgnt mgntControlMgntChannel; - mgntControlMgntChannel.Initialize(""); + mgntControlMgntChannel.Initialize(sdv::SObjectInfo()); if (mgntControlMgntChannel.GetObjectState() != sdv::EObjectState::initialized) return -11; mgntControlMgntChannel.SetOperationMode(sdv::EOperationMode::running); if (mgntControlMgntChannel.GetObjectState() != sdv::EObjectState::running) return -11; @@ -349,7 +349,7 @@ extern "C" int main(int argc, char* argv[]) // Create the data management channel. CSharedMemChannelMgnt mgntDataMgntChannel; - mgntDataMgntChannel.Initialize(""); + mgntDataMgntChannel.Initialize(sdv::SObjectInfo()); if (mgntDataMgntChannel.GetObjectState() != sdv::EObjectState::initialized) return -1; mgntDataMgntChannel.SetOperationMode(sdv::EOperationMode::running); if (mgntDataMgntChannel.GetObjectState() != sdv::EObjectState::running) return -1; @@ -360,10 +360,10 @@ extern "C" int main(int argc, char* argv[]) if (bServer) { TRACE("Server: Create data endpoint..."); - sdv::ipc::SChannelEndpoint sEndpoint = mgntDataMgntChannel.CreateEndpoint(R"code( + sdv::ipc::SChannelEndpoint sEndpoint = mgntDataMgntChannel.CreateEndpoint(R"toml( [IpcChannel] Size = 1024000 -)code"); +)toml"); ptrDataConnection = sEndpoint.pConnection; sdv::pointer ptrConnectInfoData; ptrConnectInfoData.resize(sEndpoint.ssConnectString.size()); diff --git a/tests/unit_tests/shared_mem/in_process_mem_buffer_tests.cpp b/tests/unit_tests/shared_mem/in_process_mem_buffer_tests.cpp index abc282b..aea7350 100644 --- a/tests/unit_tests/shared_mem/in_process_mem_buffer_tests.cpp +++ b/tests/unit_tests/shared_mem/in_process_mem_buffer_tests.cpp @@ -59,7 +59,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestRx) }; std::unique_lock lockStart(mtxStart); - std::thread thread(fnWaitForTrigger); + sdv::core::secure_thread thread(fnWaitForTrigger); cvStart.wait(lockStart); for (size_t n = 0; n < 20; n++) @@ -108,7 +108,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestTx) }; std::unique_lock lockStart(mtxStart); - std::thread thread(fnWaitForTrigger); + sdv::core::secure_thread thread(fnWaitForTrigger); cvStart.wait(lockStart); for (size_t n = 0; n < 20; n++) @@ -170,8 +170,8 @@ TEST(InProcessMemoryBufferTest, TriggerTestRxTx) std::unique_lock lockStartSender(mtxSenderStart); std::unique_lock lockStartReceiver(mtxReceiverStart); - std::thread threadSender(fnWaitForTriggerSender); - std::thread threadReceiver(fnWaitForTriggerReceiver); + sdv::core::secure_thread threadSender(fnWaitForTriggerSender); + sdv::core::secure_thread threadReceiver(fnWaitForTriggerReceiver); cvSenderStart.wait(lockStartSender); lockStartSender.unlock(); cvReceiverStart.wait(lockStartReceiver); @@ -340,8 +340,8 @@ TEST(InProcessMemoryBufferTest, ReserveCommitAccessReleaseNonChronologicalOrder) // Reserve buffers for strings // The buffer header has 16 bytes - // Each allocation is 8 bytes header, 5 bytes data and 3 bytes alignment - CAccessorTxPacket rgTxPackets[32] = {}; + // Each allocation is 8 bytes header, 5 bytes data and 3 bytes alignment + CAccessorTxPacket rgTxPackets[32] = {}; for (int32_t iIndex = 0; iIndex < 15; iIndex++) { auto optTxPacket = sender.Reserve(5); @@ -373,8 +373,8 @@ TEST(InProcessMemoryBufferTest, ReserveCommitAccessReleaseNonChronologicalOrder) { // The text should contain the number 0 EXPECT_TRUE(optRxPacket); - EXPECT_NE(optRxPacket->GetData(), nullptr); - EXPECT_EQ(std::to_string(0), optRxPacket->GetData()); + EXPECT_NE(optRxPacket->GetData(), nullptr); + EXPECT_EQ(std::to_string(0), optRxPacket->GetData()); optRxPacket->Accept(); } else @@ -414,7 +414,7 @@ TEST(InProcessMemoryBufferTest, ReserveCommitAccessReleaseNonChronologicalOrder) EXPECT_NE(rgRxPackets[iIndex].GetData(), nullptr); EXPECT_NE(rgRxPackets[iIndex].GetSize(), 0u); } - if (rgRxPackets[iIndex]) + if (rgRxPackets[iIndex]) { EXPECT_EQ(std::to_string(iIndex), rgRxPackets[iIndex].GetData()); } @@ -442,33 +442,33 @@ TEST(InProcessMemoryBufferTest, SendReceivePattern) ASSERT_TRUE(appcontrol.Startup("")); CInProcMemBufferTx sender; - EXPECT_TRUE(sender.IsValid()); + EXPECT_TRUE(sender.IsValid()); - CInProcMemBufferRx receiver(sender.GetConnectionString()); - EXPECT_TRUE(receiver.IsValid()); + CInProcMemBufferRx receiver(sender.GetConnectionString()); + EXPECT_TRUE(receiver.IsValid()); - CPatternReceiver pattern_inspector(receiver); + CPatternReceiver pattern_inspector(receiver); CPatternSender pattern_generator(sender); // Wait for 2 seconds - std::this_thread::sleep_for(std::chrono::seconds(PATTERN_TEST_TIME_S)); + std::this_thread::sleep_for(std::chrono::seconds(PATTERN_TEST_TIME_S)); // Shutdown pattern_generator.Shutdown(); - pattern_inspector.Shutdown(); + pattern_inspector.Shutdown(); std::cout << "Pattern generator: " << pattern_generator.GetCycleCnt() << " cyles, " << pattern_generator.GetPacketCnt() - << " packets, " << pattern_generator.GetByteCnt() << " bytes" << std::endl; - std::cout << "Pattern inspector: " << pattern_inspector.GetCycleCnt() << " cyles, " << pattern_inspector.GetPacketCnt() + << " packets, " << pattern_generator.GetByteCnt() << " bytes" << std::endl; + std::cout << "Pattern inspector: " << pattern_inspector.GetCycleCnt() << " cyles, " << pattern_inspector.GetPacketCnt() << " packets, " << pattern_inspector.GetByteCnt() << " bytes, " << pattern_inspector.GetErrorCnt() << " errors, " << std::endl; - EXPECT_NE(pattern_generator.GetCycleCnt(), 0u); - EXPECT_NE(pattern_generator.GetPacketCnt(), 0u); - EXPECT_NE(pattern_generator.GetByteCnt(), 0ull); - EXPECT_NE(pattern_inspector.GetCycleCnt(), 0u); - EXPECT_EQ(pattern_inspector.GetErrorCnt(), 0u); - EXPECT_NE(pattern_inspector.GetPacketCnt(), 0u); - EXPECT_NE(pattern_inspector.GetByteCnt(), 0ull); + EXPECT_NE(pattern_generator.GetCycleCnt(), 0u); + EXPECT_NE(pattern_generator.GetPacketCnt(), 0u); + EXPECT_NE(pattern_generator.GetByteCnt(), 0ull); + EXPECT_NE(pattern_inspector.GetCycleCnt(), 0u); + EXPECT_EQ(pattern_inspector.GetErrorCnt(), 0u); + EXPECT_NE(pattern_inspector.GetPacketCnt(), 0u); + EXPECT_NE(pattern_inspector.GetByteCnt(), 0ull); } TEST(InProcessMemoryBufferTest, DelayedSendReceivePattern) @@ -477,33 +477,33 @@ TEST(InProcessMemoryBufferTest, DelayedSendReceivePattern) ASSERT_TRUE(appcontrol.Startup("")); CInProcMemBufferTx sender; - EXPECT_TRUE(sender.IsValid()); + EXPECT_TRUE(sender.IsValid()); - CInProcMemBufferRx receiver(sender.GetConnectionString()); - EXPECT_TRUE(receiver.IsValid()); + CInProcMemBufferRx receiver(sender.GetConnectionString()); + EXPECT_TRUE(receiver.IsValid()); - CPatternReceiver pattern_inspector(receiver); - CPatternSender pattern_generator(sender, 10); + CPatternReceiver pattern_inspector(receiver); + CPatternSender pattern_generator(sender, 10); - // Wait for 2 seconds - std::this_thread::sleep_for(std::chrono::seconds(PATTERN_TEST_TIME_S)); + // Wait for 2 seconds + std::this_thread::sleep_for(std::chrono::seconds(PATTERN_TEST_TIME_S)); - // Shutdown - pattern_generator.Shutdown(); - pattern_inspector.Shutdown(); + // Shutdown + pattern_generator.Shutdown(); + pattern_inspector.Shutdown(); std::cout << "Pattern generator: " << pattern_generator.GetCycleCnt() << " cyles, " << pattern_generator.GetPacketCnt() - << " packets, " << pattern_generator.GetByteCnt() << " bytes" << std::endl; - std::cout << "Pattern inspector: " << pattern_inspector.GetCycleCnt() << " cyles, " << pattern_inspector.GetPacketCnt() + << " packets, " << pattern_generator.GetByteCnt() << " bytes" << std::endl; + std::cout << "Pattern inspector: " << pattern_inspector.GetCycleCnt() << " cyles, " << pattern_inspector.GetPacketCnt() << " packets, " << pattern_inspector.GetByteCnt() << " bytes, " << pattern_inspector.GetErrorCnt() << " errors, " << std::endl; - EXPECT_NE(pattern_generator.GetCycleCnt(), 0u); - EXPECT_NE(pattern_generator.GetPacketCnt(), 0u); - EXPECT_NE(pattern_generator.GetByteCnt(), 0ull); - EXPECT_NE(pattern_inspector.GetCycleCnt(), 0u); - EXPECT_EQ(pattern_inspector.GetErrorCnt(), 0u); - EXPECT_NE(pattern_inspector.GetPacketCnt(), 0u); - EXPECT_NE(pattern_inspector.GetByteCnt(), 0ull); + EXPECT_NE(pattern_generator.GetCycleCnt(), 0u); + EXPECT_NE(pattern_generator.GetPacketCnt(), 0u); + EXPECT_NE(pattern_generator.GetByteCnt(), 0ull); + EXPECT_NE(pattern_inspector.GetCycleCnt(), 0u); + EXPECT_EQ(pattern_inspector.GetErrorCnt(), 0u); + EXPECT_NE(pattern_inspector.GetPacketCnt(), 0u); + EXPECT_NE(pattern_inspector.GetByteCnt(), 0ull); } TEST(InProcessMemoryBufferTest, SendDelayedReceivePattern) @@ -512,33 +512,33 @@ TEST(InProcessMemoryBufferTest, SendDelayedReceivePattern) ASSERT_TRUE(appcontrol.Startup("")); CInProcMemBufferTx sender; - EXPECT_TRUE(sender.IsValid()); + EXPECT_TRUE(sender.IsValid()); - CInProcMemBufferRx receiver(sender.GetConnectionString()); - EXPECT_TRUE(receiver.IsValid()); + CInProcMemBufferRx receiver(sender.GetConnectionString()); + EXPECT_TRUE(receiver.IsValid()); - CPatternReceiver pattern_inspector(receiver, 10); - CPatternSender pattern_generator(sender); + CPatternReceiver pattern_inspector(receiver, 10); + CPatternSender pattern_generator(sender); - // Wait for 2 seconds - std::this_thread::sleep_for(std::chrono::seconds(PATTERN_TEST_TIME_S)); + // Wait for 2 seconds + std::this_thread::sleep_for(std::chrono::seconds(PATTERN_TEST_TIME_S)); - // Shutdown - pattern_generator.Shutdown(); - pattern_inspector.Shutdown(); + // Shutdown + pattern_generator.Shutdown(); + pattern_inspector.Shutdown(); std::cout << "Pattern generator: " << pattern_generator.GetCycleCnt() << " cyles, " << pattern_generator.GetPacketCnt() - << " packets, " << pattern_generator.GetByteCnt() << " bytes" << std::endl; - std::cout << "Pattern inspector: " << pattern_inspector.GetCycleCnt() << " cyles, " << pattern_inspector.GetPacketCnt() + << " packets, " << pattern_generator.GetByteCnt() << " bytes" << std::endl; + std::cout << "Pattern inspector: " << pattern_inspector.GetCycleCnt() << " cyles, " << pattern_inspector.GetPacketCnt() << " packets, " << pattern_inspector.GetByteCnt() << " bytes, " << pattern_inspector.GetErrorCnt() << " errors, " << std::endl; - EXPECT_NE(pattern_generator.GetCycleCnt(), 0u); - EXPECT_NE(pattern_generator.GetPacketCnt(), 0u); - EXPECT_NE(pattern_generator.GetByteCnt(), 0ull); - EXPECT_NE(pattern_inspector.GetCycleCnt(), 0u); - EXPECT_EQ(pattern_inspector.GetErrorCnt(), 0u); - EXPECT_NE(pattern_inspector.GetPacketCnt(), 0u); - EXPECT_NE(pattern_inspector.GetByteCnt(), 0ull); + EXPECT_NE(pattern_generator.GetCycleCnt(), 0u); + EXPECT_NE(pattern_generator.GetPacketCnt(), 0u); + EXPECT_NE(pattern_generator.GetByteCnt(), 0ull); + EXPECT_NE(pattern_inspector.GetCycleCnt(), 0u); + EXPECT_EQ(pattern_inspector.GetErrorCnt(), 0u); + EXPECT_NE(pattern_inspector.GetPacketCnt(), 0u); + EXPECT_NE(pattern_inspector.GetByteCnt(), 0ull); } TEST(InProcessMemoryBufferTest, SendRepeatReceivePattern) @@ -547,50 +547,50 @@ TEST(InProcessMemoryBufferTest, SendRepeatReceivePattern) ASSERT_TRUE(appcontrol.Startup("")); // The first process creates a sender and receiver - CInProcMemBufferTx bufferTX; - EXPECT_TRUE(bufferTX.IsValid()); - CInProcMemBufferRx bufferRX; - EXPECT_TRUE(bufferRX.IsValid()); + CInProcMemBufferTx bufferTX; + EXPECT_TRUE(bufferTX.IsValid()); + CInProcMemBufferRx bufferRX; + EXPECT_TRUE(bufferRX.IsValid()); // The connection string containing the RX and TX strings for the repeater std::string ssConnectionString = bufferTX.GetConnectionString() + "\n" + bufferRX.GetConnectionString(); // The repeater process creates a receiver and sender - CInProcMemBufferRx bufferRepeaterRX(ssConnectionString); - EXPECT_TRUE(bufferRepeaterRX.IsValid()); - CInProcMemBufferTx bufferRepeaterTX(ssConnectionString); - EXPECT_TRUE(bufferRepeaterTX.IsValid()); + CInProcMemBufferRx bufferRepeaterRX(ssConnectionString); + EXPECT_TRUE(bufferRepeaterRX.IsValid()); + CInProcMemBufferTx bufferRepeaterTX(ssConnectionString); + EXPECT_TRUE(bufferRepeaterTX.IsValid()); // Connect the pattern generator and inspector CPatternReceiver pattern_inspector(bufferRX); - CPatternRepeater pattern_repeater(bufferRepeaterRX, bufferRepeaterTX); - CPatternSender pattern_generator(bufferTX); + CPatternRepeater pattern_repeater(bufferRepeaterRX, bufferRepeaterTX); + CPatternSender pattern_generator(bufferTX); - // Wait for 2 seconds - std::this_thread::sleep_for(std::chrono::seconds(PATTERN_TEST_TIME_S)); + // Wait for 2 seconds + std::this_thread::sleep_for(std::chrono::seconds(PATTERN_TEST_TIME_S)); - // Shutdown - pattern_generator.Shutdown(); - pattern_repeater.Shutdown(); - pattern_inspector.Shutdown(); + // Shutdown + pattern_generator.Shutdown(); + pattern_repeater.Shutdown(); + pattern_inspector.Shutdown(); std::cout << "Pattern generator: " << pattern_generator.GetCycleCnt() << " cyles, " << pattern_generator.GetPacketCnt() - << " packets, " << pattern_generator.GetByteCnt() << " bytes" << std::endl; - std::cout << "Pattern inspector: " << pattern_inspector.GetCycleCnt() << " cyles, " << pattern_inspector.GetPacketCnt() + << " packets, " << pattern_generator.GetByteCnt() << " bytes" << std::endl; + std::cout << "Pattern inspector: " << pattern_inspector.GetCycleCnt() << " cyles, " << pattern_inspector.GetPacketCnt() << " packets, " << pattern_inspector.GetByteCnt() << " bytes, " << pattern_inspector.GetErrorCnt() << " errors, " << std::endl; - std::cout << "Pattern repeater: " << pattern_repeater.GetCycleCnt() << " cyles, " << pattern_repeater.GetPacketCnt() + std::cout << "Pattern repeater: " << pattern_repeater.GetCycleCnt() << " cyles, " << pattern_repeater.GetPacketCnt() << " packets, " << pattern_repeater.GetByteCnt() << " bytes, " << pattern_repeater.GetErrorCnt() << " errors, " << std::endl; - EXPECT_NE(pattern_generator.GetCycleCnt(), 0u); - EXPECT_NE(pattern_generator.GetPacketCnt(), 0u); - EXPECT_NE(pattern_generator.GetByteCnt(), 0ull); - EXPECT_NE(pattern_inspector.GetCycleCnt(), 0u); - EXPECT_EQ(pattern_inspector.GetErrorCnt(), 0u); - EXPECT_NE(pattern_inspector.GetPacketCnt(), 0u); - EXPECT_NE(pattern_inspector.GetByteCnt(), 0ull); - EXPECT_NE(pattern_repeater.GetCycleCnt(), 0u); - EXPECT_EQ(pattern_repeater.GetErrorCnt(), 0u); - EXPECT_NE(pattern_repeater.GetPacketCnt(), 0u); - EXPECT_NE(pattern_repeater.GetByteCnt(), 0ull); + EXPECT_NE(pattern_generator.GetCycleCnt(), 0u); + EXPECT_NE(pattern_generator.GetPacketCnt(), 0u); + EXPECT_NE(pattern_generator.GetByteCnt(), 0ull); + EXPECT_NE(pattern_inspector.GetCycleCnt(), 0u); + EXPECT_EQ(pattern_inspector.GetErrorCnt(), 0u); + EXPECT_NE(pattern_inspector.GetPacketCnt(), 0u); + EXPECT_NE(pattern_inspector.GetByteCnt(), 0ull); + EXPECT_NE(pattern_repeater.GetCycleCnt(), 0u); + EXPECT_EQ(pattern_repeater.GetErrorCnt(), 0u); + EXPECT_NE(pattern_repeater.GetPacketCnt(), 0u); + EXPECT_NE(pattern_repeater.GetByteCnt(), 0ull); } diff --git a/tests/unit_tests/shared_mem/pattern_gen.cpp b/tests/unit_tests/shared_mem/pattern_gen.cpp index 7a9aa0a..1258852 100644 --- a/tests/unit_tests/shared_mem/pattern_gen.cpp +++ b/tests/unit_tests/shared_mem/pattern_gen.cpp @@ -24,7 +24,7 @@ CPatternSender::CPatternSender(CMemBufferAccessorTx& raccessorOut, uint32_t uiDe m_raccessorOut(raccessorOut), m_uiDelayMs(uiDelayMs) { // Start the thread - m_thread = std::thread(&CPatternSender::Process, this); + m_thread = sdv::core::secure_thread(&CPatternSender::Process, this); // Wait for the thread to run while (!m_bStarted) std::this_thread::sleep_for(std::chrono::milliseconds(1)); @@ -67,7 +67,7 @@ CPatternReceiver::CPatternReceiver(CMemBufferAccessorRx& raccessorIn, uint32_t u m_raccessorIn(raccessorIn),m_uiDelayMs(uiDelayMs) { // Start the thread - m_thread = std::thread(& CPatternReceiver::Process, this); + m_thread = sdv::core::secure_thread(& CPatternReceiver::Process, this); // Wait for the thread to run while (!m_bStarted) std::this_thread::sleep_for(std::chrono::milliseconds(1)); @@ -123,7 +123,7 @@ CPatternRepeater::CPatternRepeater(CMemBufferAccessorRx& raccessorIn, CMemBuffer m_raccessorIn(raccessorIn), m_raccessorOut(raccessorOut), m_uiDelayMs(uiDelayMs) { // Start the thread - m_thread = std::thread(&CPatternRepeater::Process, this); + m_thread = sdv::core::secure_thread(&CPatternRepeater::Process, this); // Wait for the thread to run while (!m_bStarted) std::this_thread::sleep_for(std::chrono::milliseconds(1)); diff --git a/tests/unit_tests/shared_mem/pattern_gen.h b/tests/unit_tests/shared_mem/pattern_gen.h index 43d1acd..23a6f69 100644 --- a/tests/unit_tests/shared_mem/pattern_gen.h +++ b/tests/unit_tests/shared_mem/pattern_gen.h @@ -16,6 +16,7 @@ #include #include +#include #include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.h" /** @@ -55,14 +56,14 @@ public: */ private: - CMemBufferAccessorTx& m_raccessorOut; //!< Reference to the output accessor - std::thread m_thread; //!< Processing thread - std::atomic_bool m_bStarted = false; //!< Set by the thread when started. - std::atomic_bool m_bShutdown = false; //!< When set, shutdown the thread. - uint32_t m_uiDelayMs = 0u; //!< Delay (in ms) to insert while processing. - uint32_t m_uiCycleCnt = 0u; //!< Amount of packets - uint32_t m_uiPacketCnt = 0u; //!< Amount of packets - uint64_t m_uiByteCnt = 0ull; //!< Amount of bytes + CMemBufferAccessorTx& m_raccessorOut; //!< Reference to the output accessor + sdv::core::secure_thread m_thread; //!< Processing thread + std::atomic_bool m_bStarted = false; //!< Set by the thread when started. + std::atomic_bool m_bShutdown = false; //!< When set, shutdown the thread. + uint32_t m_uiDelayMs = 0u; //!< Delay (in ms) to insert while processing. + uint32_t m_uiCycleCnt = 0u; //!< Amount of packets + uint32_t m_uiPacketCnt = 0u; //!< Amount of packets + uint64_t m_uiByteCnt = 0ull; //!< Amount of bytes }; class CPatternReceiver @@ -98,15 +99,15 @@ public: */ private: - CMemBufferAccessorRx& m_raccessorIn; //!< Reference to the input accessor - std::thread m_thread; //!< Processing thread + CMemBufferAccessorRx& m_raccessorIn; //!< Reference to the input accessor + sdv::core::secure_thread m_thread; //!< Processing thread bool m_bStarted = false; //!< Set by the thread when started. bool m_bShutdown = false; //!< When set, shutdown the thread. - uint32_t m_uiDelayMs = 0u; //!< Delay (in ms) to insert while processing. - uint32_t m_uiCycleCnt = 0u; //!< Amount of packets - uint32_t m_uiErrorCnt = 0u; //!< Amount of counter errors - uint32_t m_uiPacketCnt = 0u; //!< Amount of packets - uint64_t m_uiByteCnt = 0ull; //!< Amount of bytes + uint32_t m_uiDelayMs = 0u; //!< Delay (in ms) to insert while processing. + uint32_t m_uiCycleCnt = 0u; //!< Amount of packets + uint32_t m_uiErrorCnt = 0u; //!< Amount of counter errors + uint32_t m_uiPacketCnt = 0u; //!< Amount of packets + uint64_t m_uiByteCnt = 0ull; //!< Amount of bytes }; class CPatternRepeater @@ -143,16 +144,16 @@ public: */ private: - CMemBufferAccessorRx& m_raccessorIn; //!< Reference to the input accessor - CMemBufferAccessorTx& m_raccessorOut; //!< Reference to the output accessor - std::thread m_thread; //!< Processing thread - bool m_bStarted = false; //!< Set by the thread when started. - bool m_bShutdown = false; //!< When set, shutdown the thread. - uint32_t m_uiDelayMs = 0u; //!< Delay (in ms) to insert while processing. - uint32_t m_uiCycleCnt = 0u; //!< Amount of packets - uint32_t m_uiErrorCnt = 0u; //!< Amount of counter errors - uint32_t m_uiPacketCnt = 0u; //!< Amount of packets - uint64_t m_uiByteCnt = 0ull; //!< Amount of bytes + CMemBufferAccessorRx& m_raccessorIn; //!< Reference to the input accessor + CMemBufferAccessorTx& m_raccessorOut; //!< Reference to the output accessor + sdv::core::secure_thread m_thread; //!< Processing thread + bool m_bStarted = false; //!< Set by the thread when started. + bool m_bShutdown = false; //!< When set, shutdown the thread. + uint32_t m_uiDelayMs = 0u; //!< Delay (in ms) to insert while processing. + uint32_t m_uiCycleCnt = 0u; //!< Amount of packets + uint32_t m_uiErrorCnt = 0u; //!< Amount of counter errors + uint32_t m_uiPacketCnt = 0u; //!< Amount of packets + uint64_t m_uiByteCnt = 0ull; //!< Amount of bytes }; #endif // !defined(PATTERN_GEN_H) \ No newline at end of file diff --git a/tests/unit_tests/shared_mem/shared_mem_buffer_tests.cpp b/tests/unit_tests/shared_mem/shared_mem_buffer_tests.cpp index 5d698f3..58d10bb 100644 --- a/tests/unit_tests/shared_mem/shared_mem_buffer_tests.cpp +++ b/tests/unit_tests/shared_mem/shared_mem_buffer_tests.cpp @@ -88,7 +88,7 @@ TEST(SharedMemoryBufferTest, TriggerTestRx) }; std::unique_lock lockStart(mtxStart); - std::thread thread(fnWaitForTrigger); + sdv::core::secure_thread thread(fnWaitForTrigger); cvStart.wait(lockStart); for (size_t n = 0; n < 20; n++) @@ -141,7 +141,7 @@ TEST(SharedMemoryBufferTest, TriggerTestTx) }; std::unique_lock lockStart(mtxStart); - std::thread thread(fnWaitForTrigger); + sdv::core::secure_thread thread(fnWaitForTrigger); cvStart.wait(lockStart); for (size_t n = 0; n < 20; n++) @@ -208,8 +208,8 @@ TEST(SharedMemoryBufferTest, TriggerTestRxTx) std::unique_lock lockStartSender(mtxSenderStart); std::unique_lock lockStartReceiver(mtxReceiverStart); - std::thread threadSender(fnWaitForTriggerSender); - std::thread threadReceiver(fnWaitForTriggerReceiver); + sdv::core::secure_thread threadSender(fnWaitForTriggerSender); + sdv::core::secure_thread threadReceiver(fnWaitForTriggerReceiver); cvSenderStart.wait(lockStartSender); lockStartSender.unlock(); cvReceiverStart.wait(lockStartReceiver); @@ -693,8 +693,8 @@ TEST(SharedMemoryBufferTest, SendRepeatReceivePattern) TEST(SharedMemoryBufferTest, AppProcessSendRepeatReceivePattern) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode = "Essential")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode = "Essential")toml")); LoadSupportServices(); CSharedMemBufferTx bufferTX; @@ -747,8 +747,8 @@ Mode = "Essential")code")); TEST(SharedMemoryBufferTest, SendRepeatReceivePatternBetweenTwoAppProcesses) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode = "Essential")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode = "Essential")toml")); LoadSupportServices(); // test starts 2 app processes, one should be the sender of the pattern, the other is the repeater diff --git a/tests/unit_tests/shared_mem/shared_mem_connect.cpp b/tests/unit_tests/shared_mem/shared_mem_connect.cpp index 6277f2c..cec9b08 100644 --- a/tests/unit_tests/shared_mem/shared_mem_connect.cpp +++ b/tests/unit_tests/shared_mem/shared_mem_connect.cpp @@ -81,7 +81,7 @@ public: // Start the processing thread if needed if (!m_threadDecoupledSend.joinable()) m_threadDecoupledSend = - std::thread(&CConnectReceiver::DecoupledSendThread, this); + sdv::core::secure_thread(&CConnectReceiver::DecoupledSendThread, this); // Store data into the queue for sending. m_queueDecoupledSend.push(std::move(seqData)); @@ -186,7 +186,7 @@ private: bool m_bConnectError = false; ///< Connection error ocurred. bool m_bCommError = false; ///< Communication error occurred. bool m_bForcedDisconnect = false; ///< Force disconnect. - std::thread m_threadDecoupledSend; ///< Decoupled send thread. + sdv::core::secure_thread m_threadDecoupledSend; ///< Decoupled send thread. std::queue>> m_queueDecoupledSend; ///< Data queue for sending. std::condition_variable m_cvDecoupledSend; ///< Trigger decoupled sending. std::atomic_bool m_bShutdown = false; ///< Shutdown send thread. @@ -203,7 +203,7 @@ TEST(SharedMemChannelService, Instantiate) ASSERT_TRUE(appcontrol.Startup("")); CSharedMemChannelMgnt mgnt; - EXPECT_NO_THROW(mgnt.Initialize("")); + EXPECT_NO_THROW(mgnt.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::initialized); @@ -220,7 +220,7 @@ TEST(SharedMemChannelService, ChannelConfigString) ASSERT_TRUE(appcontrol.Startup("")); CSharedMemChannelMgnt mgnt; - EXPECT_NO_THROW(mgnt.Initialize("")); + EXPECT_NO_THROW(mgnt.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::initialized); @@ -237,7 +237,7 @@ TEST(SharedMemChannelService, CreateRandomEndpoint) CSharedMemChannelMgnt mgnt; // Create an endpoint. - EXPECT_NO_THROW(mgnt.Initialize("")); + EXPECT_NO_THROW(mgnt.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgnt.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -256,12 +256,12 @@ TEST(SharedMemChannelService, CreateExplicitEndpoint) CSharedMemChannelMgnt mgnt; // Create an endpoint. - EXPECT_NO_THROW(mgnt.Initialize("")); + EXPECT_NO_THROW(mgnt.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::initialized); - sdv::ipc::SChannelEndpoint sChannelEndpoint = mgnt.CreateEndpoint(R"code([IpcChannel] + sdv::ipc::SChannelEndpoint sChannelEndpoint = mgnt.CreateEndpoint(R"toml([IpcChannel] Name = "CHANNEL_1234" Size = 10240 -)code"); +)toml"); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); if (sChannelEndpoint.pConnection) sdv::TObjectPtr(sChannelEndpoint.pConnection); @@ -278,9 +278,9 @@ TEST(SharedMemChannelService, GetRandomEndpointAccess) CSharedMemChannelMgnt mgntServer, mgntClient; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); - EXPECT_NO_THROW(mgntClient.Initialize("")); + EXPECT_NO_THROW(mgntClient.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -306,14 +306,14 @@ TEST(SharedMemChannelService, GetExplicitEndpointAccess) CSharedMemChannelMgnt mgntServer, mgntClient; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); - EXPECT_NO_THROW(mgntClient.Initialize("")); + EXPECT_NO_THROW(mgntClient.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); - sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code([IpcChannel] + sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"toml([IpcChannel] Name = "CHANNEL_1234" Size = 10240 -)code"); +)toml"); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -337,7 +337,7 @@ TEST(SharedMemChannelService, WaitForConnection) CSharedMemChannelMgnt mgnt; // Create an endpoint. - EXPECT_NO_THROW(mgnt.Initialize("")); + EXPECT_NO_THROW(mgnt.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgnt.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgnt.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -359,7 +359,7 @@ TEST(SharedMemChannelService, WaitForConnection) pConnection->GetConnectState() == sdv::ipc::EConnectState::connecting); // Wait for connection for infinite period with cancel. - std::thread threadCancelWait([&]() + sdv::core::secure_thread threadCancelWait([&]() { std::this_thread::sleep_for(std::chrono::milliseconds(500)); pConnection->CancelWait(); @@ -384,9 +384,9 @@ TEST(SharedMemChannelService, AsyncConnect) CSharedMemChannelMgnt mgntServer, mgntClient; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); - EXPECT_NO_THROW(mgntClient.Initialize("")); + EXPECT_NO_THROW(mgntClient.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -481,9 +481,9 @@ TEST(SharedMemChannelService, EstablishConnectionEvents) CSharedMemChannelMgnt mgntServer, mgntClient; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); - EXPECT_NO_THROW(mgntClient.Initialize("")); + EXPECT_NO_THROW(mgntClient.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -597,11 +597,11 @@ TEST(SharedMemChannelService, EstablishReconnect) CSharedMemChannelMgnt mgntServer, mgntClient1, mgntClient2; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); - EXPECT_NO_THROW(mgntClient1.Initialize("")); + EXPECT_NO_THROW(mgntClient1.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntClient1.GetObjectState(), sdv::EObjectState::initialized); - EXPECT_NO_THROW(mgntClient2.Initialize("")); + EXPECT_NO_THROW(mgntClient2.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntClient2.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -680,11 +680,11 @@ TEST(SharedMemChannelService, EstablishReconnectEvents) CSharedMemChannelMgnt mgntServer, mgntClient1, mgntClient2; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); - EXPECT_NO_THROW(mgntClient1.Initialize("")); + EXPECT_NO_THROW(mgntClient1.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntClient1.GetObjectState(), sdv::EObjectState::initialized); - EXPECT_NO_THROW(mgntClient2.Initialize("")); + EXPECT_NO_THROW(mgntClient2.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntClient2.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -779,14 +779,14 @@ TEST(SharedMemChannelService, EstablishReconnectEvents) TEST(SharedMemChannelService, AppEstablishConnection) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode = "Essential")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode = "Essential")toml")); LoadSupportServices(); CSharedMemChannelMgnt mgntServer; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -860,14 +860,14 @@ Mode = "Essential")code")); TEST(SharedMemChannelService, AppGracefullyShutdown) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode = "Essential")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode = "Essential")toml")); LoadSupportServices(); CSharedMemChannelMgnt mgntServer; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -937,14 +937,14 @@ Mode = "Essential")code")); TEST(SharedMemChannelService, AppForcedShutdown_Watchdog) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode = "Essential")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode = "Essential")toml")); LoadSupportServices(); CSharedMemChannelMgnt mgntServer; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -1019,8 +1019,8 @@ Mode = "Essential")code")); TEST(SharedMemChannelService, IndirectAppGracefullyShutdown) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode = "Essential")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode = "Essential")toml")); LoadSupportServices(); sdv::process::IProcessControl* pProcessControl = sdv::core::GetObject("ProcessControlService"); @@ -1030,7 +1030,7 @@ Mode = "Essential")code")); // Create the first control endpoint. CSharedMemChannelMgnt mgntControl1; - EXPECT_NO_THROW(mgntControl1.Initialize("")); + EXPECT_NO_THROW(mgntControl1.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntControl1.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint1 = mgntControl1.CreateEndpoint(""); EXPECT_NE(sEndpoint1.pConnection, nullptr); @@ -1066,7 +1066,7 @@ Mode = "Essential")code")); // Create the second control endpoint. CSharedMemChannelMgnt mgntControl2; - EXPECT_NO_THROW(mgntControl2.Initialize("")); + EXPECT_NO_THROW(mgntControl2.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntControl2.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint2 = mgntControl2.CreateEndpoint(""); EXPECT_NE(sEndpoint2.pConnection, nullptr); @@ -1100,8 +1100,8 @@ Mode = "Essential")code")); TEST(SharedMemChannelService, IndirectAppServerForceShutdown_Watchdog) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode = "Essential")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode = "Essential")toml")); LoadSupportServices(); sdv::process::IProcessControl* pProcessControl = sdv::core::GetObject("ProcessControlService"); @@ -1113,7 +1113,7 @@ Mode = "Essential")code")); // Create the first control endpoint. CSharedMemChannelMgnt mgntControl1; - EXPECT_NO_THROW(mgntControl1.Initialize("")); + EXPECT_NO_THROW(mgntControl1.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntControl1.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint1 = mgntControl1.CreateEndpoint(""); EXPECT_NE(sEndpoint1.pConnection, nullptr); @@ -1149,7 +1149,7 @@ Mode = "Essential")code")); // Create the second control endpoint. CSharedMemChannelMgnt mgntControl2; - EXPECT_NO_THROW(mgntControl2.Initialize("")); + EXPECT_NO_THROW(mgntControl2.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntControl2.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint2 = mgntControl2.CreateEndpoint(""); EXPECT_NE(sEndpoint2.pConnection, nullptr); @@ -1183,8 +1183,8 @@ Mode = "Essential")code")); TEST(SharedMemChannelService, IndirectAppClientForceShutdown_Watchdog) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code([Application] -Mode = "Essential")code")); + ASSERT_TRUE(appcontrol.Startup(R"toml([Application] +Mode = "Essential")toml")); LoadSupportServices(); sdv::process::IProcessControl* pProcessControl = sdv::core::GetObject("ProcessControlService"); @@ -1194,7 +1194,7 @@ Mode = "Essential")code")); // Create the first control endpoint. CSharedMemChannelMgnt mgntControl1; - EXPECT_NO_THROW(mgntControl1.Initialize("")); + EXPECT_NO_THROW(mgntControl1.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntControl1.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint1 = mgntControl1.CreateEndpoint(""); EXPECT_NE(sEndpoint1.pConnection, nullptr); @@ -1230,7 +1230,7 @@ Mode = "Essential")code")); // Create the second control endpoint. CSharedMemChannelMgnt mgntControl2; - EXPECT_NO_THROW(mgntControl2.Initialize("")); + EXPECT_NO_THROW(mgntControl2.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntControl2.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sEndpoint2 = mgntControl2.CreateEndpoint(""); EXPECT_NE(sEndpoint2.pConnection, nullptr); diff --git a/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp b/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp index 946eab1..39b999f 100644 --- a/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp +++ b/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp @@ -90,7 +90,7 @@ public: // Start the processing thread if needed if (!m_threadDecoupledSend.joinable()) - m_threadDecoupledSend = std::thread(&CLargeDataReceiver::DecoupledSendThread, this); + m_threadDecoupledSend = sdv::core::secure_thread(&CLargeDataReceiver::DecoupledSendThread, this); // Store data into the queue for sending. m_queueDecoupledSend.push(std::move(seqData)); @@ -262,7 +262,7 @@ private: bool m_bForcedDisconnect = false; ///< Force disconnect. std::atomic_size_t m_nCount = 0; ///< Receive counter. std::condition_variable m_cvReceived; ///< Receive event. - std::thread m_threadDecoupledSend; ///< Decoupled send thread. + sdv::core::secure_thread m_threadDecoupledSend; ///< Decoupled send thread. std::queue>> m_queueDecoupledSend; ///< Data queue for sending. std::condition_variable m_cvDecoupledSend; ///< Trigger decoupled sending. std::atomic_bool m_bShutdown = false; ///< Shutdown send thread. @@ -271,27 +271,29 @@ private: TEST(SharedMemChannelService, CommunicateOneLargeBlock) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code( + ASSERT_TRUE(appcontrol.Startup(R"toml( [Console] Report = "Silent" - )code")); + )toml")); appcontrol.SetConfigMode(); CSharedMemChannelMgnt mgntServer, mgntClient; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); mgntServer.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::running); - EXPECT_NO_THROW(mgntClient.Initialize("service = \"client\"")); + sdv::SObjectInfo sClientInfo{}; + sClientInfo.ssConfig = "service = \"client\""; + EXPECT_NO_THROW(mgntClient.Initialize(sClientInfo)); EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); mgntClient.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::running); - sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code( + sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"toml( [IpcChannel] Size = 1024000 -)code"); +)toml"); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -376,18 +378,20 @@ TEST(SharedMemChannelService, CommunicateMultiLargeBlock) CSharedMemChannelMgnt mgntServer, mgntClient; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); mgntServer.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::running); - EXPECT_NO_THROW(mgntClient.Initialize("service = \"client\"")); + sdv::SObjectInfo sClientInfo{}; + sClientInfo.ssConfig = "service = \"client\""; + EXPECT_NO_THROW(mgntClient.Initialize(sClientInfo)); EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); mgntClient.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::running); - sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code( + sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"toml( [IpcChannel] Size = 1024000 -)code"); +)toml"); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -504,18 +508,20 @@ TEST(SharedMemChannelService, CommunicateFragmentedLargeBlock) CSharedMemChannelMgnt mgntServer, mgntClient; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); mgntServer.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::running); - EXPECT_NO_THROW(mgntClient.Initialize("service = \"client\"")); + sdv::SObjectInfo sClientInfo{}; + sClientInfo.ssConfig = "service = \"client\""; + EXPECT_NO_THROW(mgntClient.Initialize(sClientInfo)); EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::initialized); mgntClient.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(mgntClient.GetObjectState(), sdv::EObjectState::running); - sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code( + sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"toml( [IpcChannel] Size = 1024000 -)code"); +)toml"); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -614,23 +620,23 @@ Size = 1024000 TEST(SharedMemChannelService, AppCommunicateOneLargeBlock) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code( + ASSERT_TRUE(appcontrol.Startup(R"toml( [Application] -Mode="Essential")code")); +Mode="Essential")toml")); LoadSupportServices(); appcontrol.SetConfigMode(); CSharedMemChannelMgnt mgntServer; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); mgntServer.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::running); - sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code( + sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"toml( [IpcChannel] Size = 1024000 -)code"); +)toml"); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); @@ -698,23 +704,23 @@ Size = 1024000 TEST(SharedMemChannelService, AppCommunicateMultiLargeBlock) { sdv::app::CAppControl appcontrol; - ASSERT_TRUE(appcontrol.Startup(R"code( + ASSERT_TRUE(appcontrol.Startup(R"toml( [Application] -Mode="Essential")code")); +Mode="Essential")toml")); LoadSupportServices(); appcontrol.SetConfigMode(); CSharedMemChannelMgnt mgntServer; // Create an endpoint. - EXPECT_NO_THROW(mgntServer.Initialize("")); + EXPECT_NO_THROW(mgntServer.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::initialized); mgntServer.SetOperationMode(sdv::EOperationMode::running); EXPECT_EQ(mgntServer.GetObjectState(), sdv::EObjectState::running); - sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"code( + sdv::ipc::SChannelEndpoint sChannelEndpoint = mgntServer.CreateEndpoint(R"toml( [IpcChannel] Size = 1024000 -)code"); +)toml"); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); EXPECT_FALSE(sChannelEndpoint.ssConnectString.empty()); diff --git a/tests/unit_tests/simple_toml_parser/CMakeLists.txt b/tests/unit_tests/simple_toml_parser/CMakeLists.txt new file mode 100644 index 0000000..3fc9805 --- /dev/null +++ b/tests/unit_tests/simple_toml_parser/CMakeLists.txt @@ -0,0 +1,46 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 ZF Friedrichshafen AG +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Martin Stimpfl - initial API and implementation +# Erik Verhoeven - writing TOML and whitespace preservation +#******************************************************************************* + +# Define project +project (UnitTest_SimpleTOMLParser VERSION 1.0 LANGUAGES CXX) + +# Character Reader executable +add_executable(UnitTest_SimpleTOMLParser + "parser_tests.cpp" "main.cpp") + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_libraries(UnitTest_SimpleTOMLParser GTest::GTest ${CMAKE_THREAD_LIBS_INIT}) + if (WIN32) + target_link_libraries(UnitTest_SimpleTOMLParser Ws2_32 Winmm Rpcrt4.lib) + else() + target_link_libraries(UnitTest_SimpleTOMLParser ${CMAKE_DL_LIBS} rt) + endif() +else() + target_link_libraries(UnitTest_SimpleTOMLParser GTest::GTest Rpcrt4.lib) +endif() + +# Add the Reader unittest +add_test(NAME UnitTest_SimpleTOMLParser COMMAND UnitTest_SimpleTOMLParser) + +# Execute the test +add_custom_command(TARGET UnitTest_SimpleTOMLParser POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_SimpleTOMLParser.xml + VERBATIM +) + +# Build dependencies +#add_dependencies(UnitTest_SimpleTOMLParser dependency_sdv_components) + + + diff --git a/tests/unit_tests/simple_toml_parser/main.cpp b/tests/unit_tests/simple_toml_parser/main.cpp new file mode 100644 index 0000000..ef47e4c --- /dev/null +++ b/tests/unit_tests/simple_toml_parser/main.cpp @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Martin Stimpfl - initial API and implementation + * Erik Verhoeven - writing TOML and whitespace preservation + ********************************************************************************/ + +#include +#include "../../../global/process_watchdog.h" + +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain(int argc, wchar_t* argv[]) +#else +extern "C" int main(int argc, char* argv[]) +#endif +{ + CProcessWatchdog watchdog; + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/unit_tests/simple_toml_parser/parser_tests.cpp b/tests/unit_tests/simple_toml_parser/parser_tests.cpp new file mode 100644 index 0000000..64b197a --- /dev/null +++ b/tests/unit_tests/simple_toml_parser/parser_tests.cpp @@ -0,0 +1,868 @@ +#include +#include +#include + +TEST(RecognizeTypes, Root) +{ + sdv::toml::simple_parser::CParser parser(""); + auto sRoot = parser.Root(); + EXPECT_EQ(sRoot.GetType(), sdv::toml::simple_parser::ENodeType::node_table); +} + +TEST(RecognizeTypes, Table) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + [newTable] + [secondTable.nestedTable] + )toml"); + auto sRoot = parser.Root(); + + auto table1 = sRoot.GetDirect("newTable"); + EXPECT_EQ(table1.GetType(), sdv::toml::simple_parser::ENodeType::node_table); + EXPECT_EQ(table1.GetName(), "newTable"); + EXPECT_EQ(table1.GetValue(), ""); + + auto table2 = sRoot.GetDirect("secondTable"); + EXPECT_EQ(table2.GetType(), sdv::toml::simple_parser::ENodeType::node_table); + EXPECT_EQ(table2.GetName(), "secondTable"); + EXPECT_EQ(table2.GetValue(), ""); + + auto table3 = sRoot.GetDirect("secondTable.nestedTable"); + EXPECT_EQ(table3.GetType(), sdv::toml::simple_parser::ENodeType::node_table); + EXPECT_EQ(table3.GetName(), "nestedTable"); + EXPECT_EQ(table3.GetValue(), ""); +} + +TEST(RecognizeTypes, Key_Value) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + name = "Hammer" + id = 42 + pi = 3.1415926 + boolean = true + array = [] + table = {} + )toml"); + auto sRoot = parser.Root(); + + auto value_name = sRoot.GetDirect("name"); + EXPECT_EQ(value_name.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(value_name.GetValue(), "Hammer"); + + auto value_id = sRoot.GetDirect("id"); + EXPECT_EQ(value_id.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(value_id.GetValue(), 42); + + auto value_pi = sRoot.GetDirect("pi"); + EXPECT_EQ(value_pi.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(value_pi.GetValue(), 3.1415926); + + auto value_boolean = sRoot.GetDirect("boolean"); + EXPECT_EQ(value_boolean.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(value_boolean.GetValue(), true); + + auto value_array = sRoot.GetDirect("array"); + EXPECT_EQ(value_array.GetType(), sdv::toml::simple_parser::ENodeType::node_array); + EXPECT_EQ(value_array.GetValue(), ""); + + auto value_table = sRoot.GetDirect("table"); + EXPECT_EQ(value_table.GetType(), sdv::toml::simple_parser::ENodeType::node_table); + EXPECT_EQ(value_table.GetValue(), ""); +} + +TEST(RecognizeTypes, TableArray) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + [[newTableArray]] + [[newTableArray]] + [[table.nestedTableArray]] + )toml"); + auto sRoot = parser.Root(); + + auto tableArray1 = sRoot.GetDirect("newTableArray"); + EXPECT_EQ(tableArray1.GetType(), sdv::toml::simple_parser::ENodeType::node_array); + EXPECT_EQ(tableArray1.GetName(), "newTableArray"); + + auto table1 = sRoot.GetDirect("newTableArray[0]"); + ASSERT_TRUE(table1); + EXPECT_EQ(table1.GetType(), sdv::toml::simple_parser::ENodeType::node_table); + EXPECT_EQ(table1.GetName(), "newTableArray"); + + auto table2 = sRoot.GetDirect("newTableArray[1]"); + ASSERT_TRUE(table2); + EXPECT_EQ(table2.GetType(), sdv::toml::simple_parser::ENodeType::node_table); + EXPECT_EQ(table2.GetName(), "newTableArray"); +} + +TEST(NestedContent, Array) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + arr_mixed = [ 1.0, 2, "test string", [ 1, 2 ], { pi = 3.14, e = 2.71828 }, true] + arr_ints = [ 1, 2, 3, 4] + arr_ints_trailing_comma = [ 1, 2, 3, 4, ] + arr_multiline = [ + "first line", + "second line", + "third_line", + ] + )toml"); + auto sRoot = parser.Root(); + + { + auto array_ints = sRoot.GetDirect("arr_ints"); + EXPECT_EQ(array_ints.GetType(), sdv::toml::simple_parser::ENodeType::node_array); + auto array_ints_0 = sRoot.GetDirect("arr_ints[0]"); + ASSERT_TRUE(array_ints_0); + EXPECT_EQ(array_ints_0.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_ints_0.GetValue(), 1); + auto array_ints_1 = sRoot.GetDirect("arr_ints[1]"); + ASSERT_TRUE(array_ints_1); + EXPECT_EQ(array_ints_1.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_ints_1.GetValue(), 2); + auto array_ints_2 = sRoot.GetDirect("arr_ints[2]"); + ASSERT_TRUE(array_ints_2); + EXPECT_EQ(array_ints_2.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_ints_2.GetValue(), 3); + auto array_ints_3 = sRoot.GetDirect("arr_ints[3]"); + ASSERT_TRUE(array_ints_3); + EXPECT_EQ(array_ints_3.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_ints_3.GetValue(), 4); + auto array_ints_4 = sRoot.GetDirect("arr_ints[4]"); + EXPECT_FALSE(array_ints_4); + } + + { + auto array_ints_trailing_comma = sRoot.GetDirect("arr_ints_trailing_comma"); + auto array_ints_trailing_comma_0 = sRoot.GetDirect("arr_ints_trailing_comma[0]"); + auto array_ints_trailing_comma_1 = sRoot.GetDirect("arr_ints_trailing_comma[1]"); + auto array_ints_trailing_comma_2 = sRoot.GetDirect("arr_ints_trailing_comma[2]"); + auto array_ints_trailing_comma_3 = sRoot.GetDirect("arr_ints_trailing_comma[3]"); + auto array_ints_trailing_comma_4 = sRoot.GetDirect("arr_ints_trailing_comma[4]"); + + EXPECT_EQ(array_ints_trailing_comma.GetType(), sdv::toml::simple_parser::ENodeType::node_array); + ASSERT_TRUE(array_ints_trailing_comma_0); + EXPECT_EQ(array_ints_trailing_comma_0.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_ints_trailing_comma_0.GetValue(), 1); + ASSERT_TRUE(array_ints_trailing_comma_1); + EXPECT_EQ(array_ints_trailing_comma_1.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_ints_trailing_comma_1.GetValue(), 2); + ASSERT_TRUE(array_ints_trailing_comma_2); + EXPECT_EQ(array_ints_trailing_comma_2.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_ints_trailing_comma_2.GetValue(), 3); + ASSERT_TRUE(array_ints_trailing_comma_3); + EXPECT_EQ(array_ints_trailing_comma_3.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_ints_trailing_comma_3.GetValue(), 4); + EXPECT_FALSE(array_ints_trailing_comma_4); + } + + { + auto array_mixed = sRoot.GetDirect("arr_mixed"); + auto array_mixed_0 = sRoot.GetDirect("arr_mixed[0]"); + auto array_mixed_1 = sRoot.GetDirect("arr_mixed[1]"); + auto array_mixed_2 = sRoot.GetDirect("arr_mixed[2]"); + auto array_mixed_3 = sRoot.GetDirect("arr_mixed[3]"); + auto array_mixed_3_1 = sRoot.GetDirect("arr_mixed[3][0]"); + auto array_mixed_3_2 = sRoot.GetDirect("arr_mixed[3][1]"); + auto array_mixed_4 = sRoot.GetDirect("arr_mixed[4]"); + auto array_mixed_4_pi = sRoot.GetDirect("arr_mixed[4].pi"); + auto array_mixed_4_e = sRoot.GetDirect("arr_mixed[4].e"); + auto array_mixed_5 = sRoot.GetDirect("arr_mixed[5]"); + auto array_mixed_6 = sRoot.GetDirect("arr_mixed[6]"); + + EXPECT_EQ(array_mixed.GetType(), sdv::toml::simple_parser::ENodeType::node_array); + ASSERT_TRUE(array_mixed_0); + EXPECT_EQ(array_mixed_0.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_mixed_0.GetValue(), 1.0); + ASSERT_TRUE(array_mixed_1); + EXPECT_EQ(array_mixed_1.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_mixed_1.GetValue(), 2); + ASSERT_TRUE(array_mixed_2); + EXPECT_EQ(array_mixed_2.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_mixed_2.GetValue(), "test string"); + ASSERT_TRUE(array_mixed_3); + EXPECT_EQ(array_mixed_3.GetType(), sdv::toml::simple_parser::ENodeType::node_array); + EXPECT_EQ(array_mixed_3_1.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_mixed_3_1.GetValue(), 1); + EXPECT_EQ(array_mixed_3_2.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_mixed_3_2.GetValue(), 2); + ASSERT_TRUE(array_mixed_4); + EXPECT_EQ(array_mixed_4.GetType(), sdv::toml::simple_parser::ENodeType::node_table); + EXPECT_EQ(array_mixed_4_pi.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_mixed_4_pi.GetValue(), 3.14); + EXPECT_EQ(array_mixed_4_e.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_mixed_4_e.GetValue(), 2.71828); + ASSERT_TRUE(array_mixed_5); + EXPECT_EQ(array_mixed_5.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_mixed_5.GetValue(), true); + EXPECT_FALSE(array_mixed_6); + } + + { + auto array_multiline = sRoot.GetDirect("arr_multiline"); + auto array_multiline_0 = sRoot.GetDirect("arr_multiline[0]"); + auto array_multiline_1 = sRoot.GetDirect("arr_multiline[1]"); + auto array_multiline_2 = sRoot.GetDirect("arr_multiline[2]"); + auto array_multiline_3 = sRoot.GetDirect("arr_multiline[3]"); + + EXPECT_EQ(array_multiline.GetType(), sdv::toml::simple_parser::ENodeType::node_array); + ASSERT_TRUE(array_multiline_0); + EXPECT_EQ(array_multiline_0.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_multiline_0.GetValue(), "first line"); + ASSERT_TRUE(array_multiline_1); + EXPECT_EQ(array_multiline_1.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_multiline_1.GetValue(), "second line"); + ASSERT_TRUE(array_multiline_2); + EXPECT_EQ(array_multiline_2.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(array_multiline_2.GetValue(), "third_line"); + EXPECT_FALSE(array_multiline_3); + } +} + +TEST(NestedContent, Table) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + [table] + a = 2 + b = 1.2 + [anotherTable] + a = 4 + c = false + [thirdTable.fourthTable] + a = "five" + d = [] + )toml"); + auto sRoot = parser.Root(); + + auto table_a = sRoot.GetDirect("table.a"); + auto table_b = sRoot.GetDirect("table.b"); + auto anotherTable_a = sRoot.GetDirect("anotherTable.a"); + auto anotherTable_c = sRoot.GetDirect("anotherTable.c"); + auto fourthTable_a = sRoot.GetDirect("thirdTable.fourthTable.a"); + auto fourthTable_d = sRoot.GetDirect("thirdTable.fourthTable.d"); + + ASSERT_TRUE(table_a); + EXPECT_EQ(table_a.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table_a.GetValue(), 2); + ASSERT_TRUE(table_b); + EXPECT_EQ(table_b.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table_b.GetValue(), 1.2); + ASSERT_TRUE(anotherTable_a); + EXPECT_EQ(anotherTable_a.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(anotherTable_a.GetValue(), 4); + ASSERT_TRUE(anotherTable_c); + EXPECT_EQ(anotherTable_c.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(anotherTable_c.GetValue(), false); + ASSERT_TRUE(fourthTable_a); + EXPECT_EQ(fourthTable_a.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(fourthTable_a.GetValue(), "five"); + ASSERT_TRUE(fourthTable_d); + EXPECT_EQ(fourthTable_d.GetType(), sdv::toml::simple_parser::ENodeType::node_array); +} + +TEST(NestedContent, TableArray) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + [[table.test]] + a = 2 + b = 1.2 + [[table.test]] + a = 4 + c = false + [[table.test]] + a = "five" + d = [] + )toml"); + auto sRoot = parser.Root(); + + auto table_test_1_a = sRoot.GetDirect("table.test[0].a"); + auto table_test_1_b = sRoot.GetDirect("table.test[0].b"); + auto table_test_2_a = sRoot.GetDirect("table.test[1].a"); + auto table_test_2_c = sRoot.GetDirect("table.test[1].c"); + auto table_test_3_a = sRoot.GetDirect("table.test[2].a"); + auto table_test_3_d = sRoot.GetDirect("table.test[2].d"); + + ASSERT_TRUE(table_test_1_a); + EXPECT_EQ(table_test_1_a.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table_test_1_a.GetValue(), 2); + ASSERT_TRUE(table_test_1_b); + EXPECT_EQ(table_test_1_b.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table_test_1_b.GetValue(), 1.2); + ASSERT_TRUE(table_test_2_a); + EXPECT_EQ(table_test_2_a.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table_test_2_a.GetValue(), 4); + ASSERT_TRUE(table_test_2_c); + EXPECT_EQ(table_test_2_c.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table_test_2_c.GetValue(), false); + ASSERT_TRUE(table_test_3_a); + EXPECT_EQ(table_test_3_a.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table_test_3_a.GetValue(), "five"); + ASSERT_TRUE(table_test_3_d); + EXPECT_EQ(table_test_3_d.GetType(), sdv::toml::simple_parser::ENodeType::node_array); +} + +TEST(NestedContent, InlineTable) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + table1 = { a = 0, b = 1.2, c = "string" } + table2 = { a = [], b = true, e = 2.71828 } + table3 = { a = { a = "a", b = "A" }, b = {a = "b", b = "B"}, e = {a = "e", b = "E"} } + )toml"); + auto sRoot = parser.Root(); + + auto table1_a = sRoot.GetDirect("table1.a"); + auto table1_b = sRoot.GetDirect("table1.b"); + auto table1_c = sRoot.GetDirect("table1.c"); + auto table2_a = sRoot.GetDirect("table2.a"); + auto table2_b = sRoot.GetDirect("table2.b"); + auto table2_e = sRoot.GetDirect("table2.e"); + auto table3_a_a = sRoot.GetDirect("table3.a.a"); + auto table3_a_b = sRoot.GetDirect("table3.a.b"); + auto table3_b_a = sRoot.GetDirect("table3.b.a"); + auto table3_b_b = sRoot.GetDirect("table3.b.b"); + auto table3_e_a = sRoot.GetDirect("table3.e.a"); + auto table3_e_b = sRoot.GetDirect("table3.e.b"); + + ASSERT_TRUE(table1_a); + EXPECT_EQ(table1_a.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table1_a.GetValue(), 0); + ASSERT_TRUE(table1_b); + EXPECT_EQ(table1_b.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table1_b.GetValue(), 1.2); + ASSERT_TRUE(table1_c); + EXPECT_EQ(table1_c.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table1_c.GetValue(), "string"); + ASSERT_TRUE(table2_a); + EXPECT_EQ(table2_a.GetType(), sdv::toml::simple_parser::ENodeType::node_array); + ASSERT_TRUE(table2_b); + EXPECT_EQ(table2_b.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table2_b.GetValue(), true); + ASSERT_TRUE(table2_e); + EXPECT_EQ(table2_e.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table2_e.GetValue(), 2.71828); + ASSERT_TRUE(table3_a_a); + EXPECT_EQ(table3_a_a.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table3_a_a.GetValue(), "a"); + ASSERT_TRUE(table3_a_b); + EXPECT_EQ(table3_a_b.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table3_a_b.GetValue(), "A"); + ASSERT_TRUE(table3_b_a); + EXPECT_EQ(table3_b_a.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table3_b_a.GetValue(), "b"); + ASSERT_TRUE(table3_b_b); + EXPECT_EQ(table3_b_b.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table3_b_b.GetValue(), "B"); + ASSERT_TRUE(table3_e_a); + EXPECT_EQ(table3_e_a.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table3_e_a.GetValue(), "e"); + ASSERT_TRUE(table3_e_b); + EXPECT_EQ(table3_e_b.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(table3_e_b.GetValue(), "E"); +} + +TEST(NestedContent, InlineTableBreakLine) +{ + // The following is not allowed in version 1.0, but is allowed in version 1.1 + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( +table = { a = 1, b = 2, + c = 3, d = 4 } +)toml")); + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( +table = { a = 1, b = 2 + ,c = 3, d = 4 } +)toml")); + + // Line breaks are allowed when part of an array or have multi-line strings + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( +array = [{ a = 1, b = 2}, + {c = 3, d = 4}] +)toml")); + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( +table = { a = 1, b = [2, 3, + 4, 5], c = 6, d = 7} +)toml")); + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( +table = { x = "abc", y = """def- +ghi""", z = "jkl" } +)toml")); + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( +table = { x = 'abc', y = '''def- +ghi''', z = 'jkl' } +)toml")); +} + +TEST(SpecialCases, Keys) +{ + std::string ssUTF8String = u8R"toml( + "127.0.0.1" = "value" + "character encoding" = "value" + "ʎǝʞ" = "value" + 'key2' = "value" + 'quoted "value"' = "value" + )toml"; + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(std::string_view(ssUTF8String))); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + key = "value" + bare_key = "value" + bare-key = "value" + 1234 = "value" + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + "" = "blank" # VALID but discouraged + )toml")); + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + '' = 'blank' # VALID but discouraged + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + name = "Orange" + physical.color = "orange" + physical.shape = "round" + site."google.com" = true + )toml")); + + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + fruit.name = "banana" # this is best practice + fruit. color = "yellow" # same as fruit.color + fruit . flavor = "banana" # same as fruit.flavor + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + # This makes the key "fruit" into a table. + fruit.apple.smooth = true + # So then you can add to the table "fruit" like so: + fruit.orange = 2 + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + # VALID BUT DISCOURAGED + apple.type = "fruit" + orange.type = "fruit" + apple.skin = "thin" + orange.skin = "thick" + apple.color = "red" + orange.color = "orange" + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + 3.1415 = 3.1415 + )toml")); + { + sdv::toml::simple_parser::CParser parser(R"toml( + 3.1415 = 3.1415 + )toml"); + auto sRoot = parser.Root(); + + auto table = sRoot.GetDirect("3"); + auto pi = sRoot.GetDirect("3.1415"); + ASSERT_TRUE(table); + EXPECT_EQ(table.GetType(), sdv::toml::simple_parser::ENodeType::node_table); + ASSERT_TRUE(pi); + EXPECT_EQ(pi.GetType(), sdv::toml::simple_parser::ENodeType::node_value); + EXPECT_EQ(pi.GetValue(), 3.1415); + } +} + +TEST(SpecialCases, Arrays) +{ + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + integers = [ 1, 2, 3 ] + colors = [ "red", "yellow", "green" ] + nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] + nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] + string_array = [ "all", 'strings', """are the same""", '''type''' ] + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] + contributors = [ + "Foo Bar ", + { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } + ] + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + integers3 = [ + 1, + 2, # this is ok + ] + )toml")); +} + +TEST(SpecialCases, Tables) +{ + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + [table-1] + key1 = "some string" + key2 = 123 + + [table-2] + key1 = "another string" + key2 = 456 + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + [dog."tater.man"] + type.name = "pug" + )toml")); + + std::string ssUTF8String = u8R"toml( + [a.b.c] # this is best practice + [ d.e.f ] # same as [d.e.f] + [ g . h . i ] # same as [g.h.i] + [ j . "ʞ" . 'l' ] # same as [j."ʞ".'l'] + )toml"; + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(std::string_view(ssUTF8String))); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + # [x] you + # [x.y] don't + # [x.y.z] need these + [x.y.z.w] # for this to work + [x] # defining a super-table afterward is ok + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + # VALID BUT DISCOURAGED + [fruit.apple] + [animal] + [fruit.orange] + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + [fruit] + apple.color = "red" + apple.taste.sweet = true + [fruit.apple.texture] # you can add sub-tables + )toml")); +} + +TEST(SpecialCases, TableArrays) +{ + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + [[products]] + name = "Hammer" + sku = 738594937 + [[products]] # empty table within the array + [[products]] + name = "Nail" + sku = 284758393 + color = "gray" + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + [[fruits]] + name = "apple" + [fruits.physical] # subtable + color = "red" + shape = "round" + [[fruits.varieties]] # nested array of tables + name = "red delicious" + [[fruits.varieties]] + name = "granny smith" + [[fruits]] + name = "banana" + [[fruits.varieties]] + name = "plantain" + )toml")); + + EXPECT_NO_THROW(sdv::toml::simple_parser::CParser(R"toml( + points = [ { x = 1, y = 2, z = 3 }, + { x = 7, y = 8, z = 9 }, + { x = 2, y = 4, z = 8 } ] + )toml")); +} + +TEST(ErrorCases, KeyValue) +{ + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml(key = # node_invalid)toml"), std::exception); + + EXPECT_THROW( + sdv::toml::simple_parser::CParser(R"toml(first = "Tom" last = "Preston-Werner" # node_invalid)toml"), std::exception); + + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml(= "no key name" # node_invalid)toml"), std::exception); + + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + name = "Tom" + name = "Pradyun" + )toml"), + std::exception); + + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + fruit . flavor = "banana" # same as fruit.flavor + fruit.flavor = "banana" + )toml"), + std::exception); + + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + spelling = "favorite" + "spelling" = "favourite" + )toml"), + std::exception); + + + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + # This defines the value of fruit.apple to be an integer. + fruit.apple = 1 + # But then this treats fruit.apple like it's a table. + # You can't turn an integer into a table. + fruit.apple.smooth = true + )toml"), + std::exception); +} + +TEST(ErrorCases, Tables) +{ + std::string ssUTF8String(u8R"toml( + [ j . "ʞ" . 'l' ] + [j."ʞ".'l'] + )toml"); + EXPECT_THROW(sdv::toml::simple_parser::CParser(std::string_view(ssUTF8String)), std::exception); + + ssUTF8String = u8R"toml( + [ j . "ʞ" . 'l' ] + ["j".'ʞ'."l"] + )toml"; + EXPECT_THROW(sdv::toml::simple_parser::CParser(std::string_view(ssUTF8String)), std::exception); + + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + [fruit] + apple = "red" + [fruit] + orange = "orange" + )toml"), + std::exception); + + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + [fruit] + apple = "red" + [fruit.apple] + texture = "smooth" + )toml"), + std::exception); + + // These two tests are not covered with current implementation. + //EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + // [fruit] + // apple.color = "red" + // apple.taste.sweet = true + // [fruit.apple] # INVALID + // )toml"), + // std::exception); + //EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + // [fruit] + // apple.color = "red" + // apple.taste.sweet = true + // [fruit.apple.taste] # INVALID + // )toml"), + // std::exception); +} + +TEST(ErrorCases, InlineTables) +{ + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + type = { name = "Nail" } + type.edible = false # INVALID + )toml"), + std::exception); + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + [product] + type.name = "Nail" + type = { edible = false } # INVALID + )toml"), + std::exception); +} + +TEST(ErrorCases, TableArrays) +{ + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + [fruit.physical] # subtable, but to which parent element should it belong? + color = "red" + shape = "round" + [[fruit]] # parser must throw an error upon discovering that "fruit" is + # an array rather than a table + name = "apple" + )toml"), + std::exception); + + // The following test is not covered with the current implementation + //EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + // fruits = [] + // [[fruits]] # Not allowed + // )toml"), + // std::exception); + + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + [[fruits]] + name = "apple" + [[fruits.varieties]] + name = "red delicious" + # INVALID: This table conflicts with the previous array of tables + [fruits.varieties] + name = "granny smith" + )toml"), + std::exception); + EXPECT_THROW(sdv::toml::simple_parser::CParser(R"toml( + [[fruits]] + name = "apple" + [fruits.physical] + color = "red" + shape = "round" + # INVALID: This array of tables conflicts with the previous table + [[fruits.physical]] + color = "green" + )toml"), + std::exception); +} + +TEST(Ordering, Array) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + )toml"); + auto sRoot = parser.Root(); + + auto two = sRoot.GetDirect("array[2]"); + auto eleven = sRoot.GetDirect("array[11]"); + const auto arr = sRoot.GetDirect("array"); + + // with direct access + ASSERT_TRUE(two); + EXPECT_EQ(two.GetValue(), 2); + ASSERT_TRUE(eleven); + EXPECT_EQ(eleven.GetValue(), 11); + + // with indirect access through iterating + ASSERT_TRUE(arr); + EXPECT_EQ(arr.GetArray().size(), 12u); + size_t nIndex = 0; + for (const auto& rsNode : arr.GetArray()) + EXPECT_EQ(rsNode.GetValue(), nIndex++); +} + +TEST(Ordering, TableAray) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + [[tableArray]] + a = 0 + [[tableArray]] + a = 1 + [[tableArray]] + a = 2 + [[tableArray]] + a = 3 + [[tableArray]] + a = 4 + [[tableArray]] + a = 5 + [[tableArray]] + a = 6 + [[tableArray]] + a = 7 + [[tableArray]] + a = 8 + [[tableArray]] + a = 9 + [[tableArray]] + a = 10 + [[tableArray]] + a = 11 + )toml"); + auto sRoot = parser.Root(); + + auto tableArray = sRoot.GetDirect("tableArray"); + + ASSERT_TRUE(tableArray); + EXPECT_EQ(tableArray.GetArray().size(), 12u); + size_t nIndex = 0; + for (const auto& rsNode : tableArray.GetArray()) + EXPECT_EQ(rsNode.GetDirect("a").GetValue(), nIndex++); +} + +TEST(Ordering, TableArayWithTables) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + [topTable] + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 0 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 1 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 2 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 3 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 4 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 5 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 6 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 7 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 8 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 9 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 10 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 11 + )toml"); + auto sRoot = parser.Root(); + + auto tableArray = sRoot.GetDirect("topTable.tableArray"); + + ASSERT_TRUE(tableArray); + EXPECT_EQ(tableArray.GetArray().size(), 12u); + size_t nIndex = 0; + for (const auto& rsNode : tableArray.GetArray()) + EXPECT_EQ(rsNode.GetDirect("MyTable.a").GetValue(), nIndex++); +} + +TEST(Ordering, NodeGetDirect) +{ + sdv::toml::simple_parser::CParser parser(R"toml( + [[table.test]] + a = 2 + b = 1.2 + [[table.test]] + a = 4 + c = false + [[table.test]] + a = "five" + d = [ { x = 1, y = 2, z = 3 }, + { x = 7, y = 8, z = 9 }, + { x = 2, y = 4, z = 8 }] + )toml"); + auto sRoot = parser.Root(); + + auto table_test_1_a = sRoot.GetDirect("table.test[0].a"); + auto table_test_1_b = sRoot.GetDirect("table.test[0].b"); + auto table_test_2_a = sRoot.GetDirect("table.test[1].a"); + auto table_test_2_c = sRoot.GetDirect("table.test[1].c"); + auto table_test_3_a = sRoot.GetDirect("table.test[2].a"); + auto table_test_3_d = sRoot.GetDirect("table.test[2].d"); + + EXPECT_TRUE(table_test_1_a); + EXPECT_TRUE(table_test_1_b); + EXPECT_EQ(table_test_3_d.GetType(), sdv::toml::simple_parser::ENodeType::node_array); + + auto table_test_3 = sRoot.GetDirect("table.test[2]"); + auto table_test_3_2nd = table_test_3.GetDirect("d[2].x"); + ASSERT_TRUE(table_test_3_2nd); +} + diff --git a/tests/unit_tests/socket_can_com_tests/src/can_com_test_socket.cpp b/tests/unit_tests/socket_can_com_tests/src/can_com_test_socket.cpp index 66dde82..38e9287 100644 --- a/tests/unit_tests/socket_can_com_tests/src/can_com_test_socket.cpp +++ b/tests/unit_tests/socket_can_com_tests/src/can_com_test_socket.cpp @@ -96,15 +96,15 @@ bool CANSocketTest::vcanIsInstalled = false; class CTestCANSocket : public CCANSockets { public: - virtual void Initialize(const sdv::u8string& ssObjectConfig) override - { - CCANSockets::Initialize(ssObjectConfig); - } + // virtual void OnInitialize(const sdv::ObjectInfo& sObjectInfo) override + // { + // CCANSockets::OnInitialize(sObjectInfo); + // } - virtual void Shutdown() override - { - CCANSockets::Shutdown(); - } + // virtual void Shutdown() override + // { + // CCANSockets::Shutdown(); + // } virtual void Send(const sdv::can::SMessage& sMsg, uint32_t uiIfcIndex) override { @@ -199,7 +199,9 @@ bool InitializeAppControl(sdv::app::CAppControl* appcontrol, const std::string& void InitializeCanComObject(CTestCANSocket& canComObj, const std::string config, MockCANReceiver& mockRcv) { - ASSERT_NO_THROW(canComObj.Initialize(config.c_str())); + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = config; + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.SetOperationMode(sdv::EOperationMode::configuring)); ASSERT_NO_THROW(canComObj.RegisterReceiver(&mockRcv)); @@ -228,9 +230,10 @@ TEST_F(CANSocketTest, ValidConfigString) sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string ssObjectConfig = R"(canSockets = "vcan0")"; // vcan0 interface must exist + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"(canSockets = "vcan0")"; // vcan0 interface must exist CTestCANSocket canComObj; - ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); ASSERT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.Send(testMsg, 0)); @@ -244,9 +247,10 @@ TEST_F(CANSocketTest, InvalidConfigString) sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string ssObjectConfig = R"(canSockets = "vcan08")"; + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"(canSockets = "vcan08")"; CTestCANSocket canComObj; - ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialization_failure); ASSERT_NO_THROW(canComObj.Shutdown()); @@ -260,9 +264,10 @@ TEST_F(CANSocketTest, ValidConfigArray) sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string ssObjectConfig = R"(canSockets = ["vcan0", "vcan1"])"; + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"(canSockets = ["vcan0", "vcan1"])"; CTestCANSocket canComObj; - ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.Send(testMsg, 0)); @@ -276,9 +281,10 @@ TEST_F(CANSocketTest, InvalidConfigArray) sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string ssObjectConfig = R"(canSockets = ["vcan08", "vcan09"])"; + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"(canSockets = ["vcan08", "vcan09"])"; CTestCANSocket canComObj; - ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialization_failure); ASSERT_NO_THROW(canComObj.Shutdown()); @@ -292,9 +298,10 @@ TEST_F(CANSocketTest, ValidConfigArrayButUnknownElement) sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string ssObjectConfig = R"(canSockets = ["vcan0", "vcan08"])"; + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"(canSockets = ["vcan0", "vcan08"])"; CTestCANSocket canComObj; - ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); ASSERT_NO_THROW(canComObj.Send(testMsg, 0)); @@ -308,9 +315,10 @@ TEST_F(CANSocketTest, InvalidConfigIdentifier) sdv::app::CAppControl appControl; appControl.Startup(""); - sdv::u8string ssObjectConfig = R"(invalidCanSockets = ["vcan0", "vcan1"])"; // Invalid config identifier + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"(invalidCanSockets = ["vcan0", "vcan1"])"; // Invalid config identifier CTestCANSocket canComObj; - ASSERT_NO_THROW(canComObj.Initialize(ssObjectConfig.c_str())); + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialization_failure); ASSERT_NO_THROW(canComObj.Shutdown()); @@ -478,11 +486,11 @@ TEST_F(CANSocketTest, StressTestWith3Objects) std::atomic_bool stopSendThread = false; std::cout << "Start thread sending messages..." << std::endl; - std::thread thSendThread1(SendThread, std::ref(stopSendThread), std::ref(canComObj1), std::ref(testData1)); + sdv::core::secure_thread thSendThread1(SendThread, std::ref(stopSendThread), std::ref(canComObj1), std::ref(testData1)); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - std::thread thSendThread2(SendThread, std::ref(stopSendThread), std::ref(canComObj2), std::ref(testData2)); + sdv::core::secure_thread thSendThread2(SendThread, std::ref(stopSendThread), std::ref(canComObj2), std::ref(testData2)); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - std::thread thSendThread3(SendThread, std::ref(stopSendThread), std::ref(canComObj3), std::ref(testData3)); + sdv::core::secure_thread thSendThread3(SendThread, std::ref(stopSendThread), std::ref(canComObj3), std::ref(testData3)); std::this_thread::sleep_for(std::chrono::seconds(10)); @@ -702,9 +710,10 @@ TEST_F(CANSocketTest, ManualSendAndReceiveTestOfMulitpleSockets) uint32_t vcan2Index = 6; uint32_t vcan3Index = 1; uint32_t vcan4Index = 5; - sdv::u8string ssConfig = R"(canSockets = ["vcan0", "vcan3", "vcan8", "vcan1", "vcan9", "vcan4", "vcan2"])"; CTestCANSocket canComObj; - ASSERT_NO_THROW(canComObj.Initialize(ssConfig.c_str())); + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"(canSockets = ["vcan0", "vcan3", "vcan8", "vcan1", "vcan9", "vcan4", "vcan2"])"; + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); // Register a receiver. @@ -772,9 +781,10 @@ TEST_F(CANSocketTest, ManualSendAndReceiveTestWithDifferentDataSizes) uint32_t vcan2Index = 6; uint32_t vcan3Index = 1; uint32_t vcan4Index = 5; - sdv::u8string ssConfig = R"(canSockets = ["vcan0", "vcan3", "vcan8", "vcan1", "vcan9", "vcan4", "vcan2"])"; + sdv::SObjectInfo sObjectInfo{}; + sObjectInfo.ssConfig = R"(canSockets = ["vcan0", "vcan3", "vcan8", "vcan1", "vcan9", "vcan4", "vcan2"])"; CTestCANSocket canComObj; - ASSERT_NO_THROW(canComObj.Initialize(ssConfig.c_str())); + ASSERT_NO_THROW(canComObj.Initialize(sObjectInfo)); EXPECT_EQ(canComObj.GetObjectState(), sdv::EObjectState::initialized); // Register a receiver. diff --git a/tests/unit_tests/toml_parser/CMakeLists.txt b/tests/unit_tests/toml_parser/CMakeLists.txt index 656665f..d61c42c 100644 --- a/tests/unit_tests/toml_parser/CMakeLists.txt +++ b/tests/unit_tests/toml_parser/CMakeLists.txt @@ -12,6 +12,9 @@ # Erik Verhoeven - writing TOML and whitespace preservation #******************************************************************************* +# Define project +project (UnitTest_TOMLParser VERSION 1.0 LANGUAGES CXX) + # Character Reader executable add_executable(UnitTest_TOMLParser "character_reader_tests.cpp" @@ -19,13 +22,17 @@ add_executable(UnitTest_TOMLParser "parser_tests.cpp" "main.cpp" "generate_toml_tests.cpp" - "generate_toml_delete_node.cpp" + "delete_node.cpp" "statement_boundary_detection.cpp" - "miscellaneous_tests.cpp" + "miscellaneous.cpp" "generate_toml_with_transfer.cpp" - "generate_toml_switch_inline.cpp" - "generate_toml_getset_comment.cpp" - "generate_toml_insert_node.cpp" "generate_toml_miscellaneous.cpp" "generate_toml_combine_reduce.cpp") + "getset_comment.cpp" + "insert_node.cpp" + "combine_reduce.cpp" + "comparison_tests.cpp" + "make_inline_standard.cpp" + "indexer_tests.cpp" "cascade_insert_node.cpp") + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_link_libraries(UnitTest_TOMLParser GTest::GTest ${CMAKE_THREAD_LIBS_INIT}) if (WIN32) diff --git a/tests/unit_tests/toml_parser/cascade_insert_node.cpp b/tests/unit_tests/toml_parser/cascade_insert_node.cpp new file mode 100644 index 0000000..103e5a3 --- /dev/null +++ b/tests/unit_tests/toml_parser/cascade_insert_node.cpp @@ -0,0 +1,536 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include + +#include "../../../global/localmemmgr.h" +#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" +#include + +TEST(CascadeInsertNode, InsertValueInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + + // Insert the value into the new table + EXPECT_TRUE(root.InsertValue("", "standard_table.value_int", 10)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_int = 10)toml"); + + // Insert the values into the existing table + EXPECT_TRUE(root.InsertValue("", "standard_table.value_str", u8"abc")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_int = 10 +value_str = "abc")toml"); + EXPECT_TRUE(root.InsertValue("standard_table.value_int", "standard_table.value_float", 123.456)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_float = 123.456 +value_int = 10 +value_str = "abc")toml"); +} + +TEST(CascadeInsertNode, InsertTableInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTable("", "standard_table", false)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert a standard table + EXPECT_TRUE(root.InsertTable("", "standard_table.table1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table.table1])toml"); + + // Insert an inline table before + EXPECT_TRUE(root.InsertTable("standard_table.table1", "standard_table.table2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table2 = {} +[standard_table.table1])toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_TRUE(root.InsertTable("", "standard_table.table3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table2 = {} +table3 = {} +[standard_table.table1])toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table relative to the elements of the + // standard_table. + EXPECT_TRUE(root.InsertTable("standard_table.table2", "standard_table.table4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table2 = {} +table3 = {} +[standard_table.table4] +[standard_table.table1])toml"); +} + +TEST(CascadeInsertNode, InsertArrayInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTable("", "standard_table", false)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert arrays + EXPECT_TRUE(root.InsertArray("", "standard_table.value_array1")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_array1 = [])toml"); + EXPECT_TRUE(root.InsertArray("", "standard_table.value_array2")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_array1 = [] +value_array2 = [])toml"); + EXPECT_TRUE(root.InsertArray("standard_table.value_array1", "standard_table.value_array3")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_array3 = [] +value_array1 = [] +value_array2 = [])toml"); +} + +TEST(CascadeInsertNode, InsertTableArrayInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTable("", "standard_table", false)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert standard table array + EXPECT_TRUE(root.InsertTableArray("", "standard_table.table_array1", + false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[standard_table.table_array1]])toml"); + + // Insert an inline table array before + EXPECT_TRUE(root.InsertTableArray("standard_table.table_array1", "standard_table.table_array2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +[[standard_table.table_array1]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_TRUE(root.InsertTableArray("", "standard_table.table_array3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +table_array3 = [{}] +[[standard_table.table_array1]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array relative to the elements + // of the standard_table. + EXPECT_TRUE(root.InsertTableArray("standard_table.table_array2", "standard_table.table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +table_array3 = [{}] +[[standard_table.table_array4]] +[[standard_table.table_array1]])toml"); + + // Add an additional table array entry for table array #4 + EXPECT_TRUE(root.InsertTableArray("", "standard_table.table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +table_array3 = [{}] +[[standard_table.table_array4]] +[[standard_table.table_array1]] +[[standard_table.table_array4]])toml"); +} + +TEST(CascadeInsertNode, InsertValueInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTable("", "inline_table", true)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert the values into the table + EXPECT_TRUE(root.InsertValue("", "inline_table.value_int", 10)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_int = 10})toml"); + EXPECT_TRUE(root.InsertValue("", "inline_table.value_str", u8"abc")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_int = 10, value_str = "abc"})toml"); + EXPECT_TRUE(root.InsertValue("inline_table.value_int", "inline_table.value_float", 123.456)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_float = 123.456, value_int = 10, value_str = "abc"})toml"); +} + +TEST(CascadeInsertNode, InsertTableInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTable("", "inline_table", true)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert a standard table + EXPECT_TRUE(root.InsertTable("", "inline_table.table1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table1 = {}})toml"); + + // Insert an inline table before + EXPECT_TRUE(root.InsertTable("inline_table.table1", "inline_table.table2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table2 = {}, table1 = {}})toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_TRUE(root.InsertTable("", "inline_table.table3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table2 = {}, table1 = {}, table3 = {}})toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_TRUE(root.InsertTable("inline_table.table2", "inline_table.table4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table4 = {}, table2 = {}, table1 = {}, table3 = {}})toml"); +} + +TEST(CascadeInsertNode, InsertArrayInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTable("", "inline_table", + true)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert arrays + EXPECT_TRUE(root.InsertArray("", "inline_table.value_array1")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array1 = []})toml"); + EXPECT_TRUE(root.InsertArray("", "inline_table.value_array2")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array1 = [], value_array2 = []})toml"); + EXPECT_TRUE(root.InsertArray("inline_table.value_array1", "inline_table.value_array3")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array3 = [], value_array1 = [], value_array2 = []})toml"); +} + +TEST(CascadeInsertNode, InsertTableArrayInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTable("", "inline_table", true)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert standard table array + EXPECT_TRUE(root.InsertTableArray("", "inline_table.table_array1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array1 = [{}]})toml"); + + // Insert an inline table array before + EXPECT_TRUE(root.InsertTableArray("inline_table.table_array1", "inline_table.table_array2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array2 = [{}], table_array1 = [{}]})toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_TRUE(root.InsertTableArray("", "inline_table.table_array3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_TRUE(root.InsertTableArray("inline_table.table_array2", "inline_table.table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, + R"toml(inline_table = {table_array4 = [{}], table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); + + // Add an additional table array entry for table array #4 + EXPECT_TRUE(root.InsertTableArray("", "inline_table.table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, + R"toml(inline_table = {table_array4 = [{}, {}], table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); +} + +TEST(CascadeInsertNode, InsertValueInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertArray("", "inline_array")); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert the values into the table (with or without name) + EXPECT_TRUE(root.InsertValue("", "inline_array", 10)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [10])toml"); + EXPECT_TRUE(root.InsertValue("", "inline_array", u8"abc")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [10, "abc"])toml"); + EXPECT_TRUE(root.InsertValue("inline_array[0]", "inline_array", 123.456)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [123.456, 10, "abc"])toml"); +} + +TEST(CascadeInsertNode, InsertTableInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertArray("", "inline_array")); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert a standard table + EXPECT_TRUE(root.InsertTable("", "inline_array", false)); + EXPECT_TRUE(root.InsertValue("", "inline_array[0].a", 10)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{a = 10}])toml"); + + // Insert an inline table before + EXPECT_TRUE(root.InsertTable("inline_array[0]", "inline_array", true)); + EXPECT_TRUE(root.InsertValue("", "inline_array[0].b", 20)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{b = 20}, {a = 10}])toml"); + + // Insert an inline table behind + EXPECT_TRUE(root.InsertTable("", "inline_array", true)); + EXPECT_TRUE(root.InsertValue("", "inline_array.c", 30)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{b = 20}, {a = 10}, {c = 30}])toml"); + + // Insert a standard table in front + EXPECT_TRUE(root.InsertTable("inline_array[0]", "inline_array", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_TRUE(root.InsertValue("", "inline_array[0].d", 40)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{d = 40}, {b = 20}, {a = 10}, {c = 30}])toml"); +} + +TEST(CascadeInsertNode, InsertArrayInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertArray("", "inline_array")); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert arrays + EXPECT_TRUE(root.InsertArray("", "inline_array")); + EXPECT_TRUE(root.InsertValue("", "inline_array[0]", 10)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[10]])toml"); + EXPECT_TRUE(root.InsertArray("", "inline_array")); + EXPECT_TRUE(root.InsertValue("", "inline_array[1]", 20)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[10], [20]])toml"); + EXPECT_TRUE(root.InsertArray("inline_array[0]", "inline_array")); + EXPECT_TRUE(root.InsertValue("", "inline_array[0]", 30)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[30], [10], [20]])toml"); +} + +TEST(CascadeInsertNode, InsertTableArrayInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertArray("", "inline_array")); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert standard table array + EXPECT_TRUE(root.InsertTableArray("", "inline_array", false)); + EXPECT_TRUE(root.InsertValue("", "inline_array[0].a", 10)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{a = 10}]])toml"); + + // Insert an inline table array before + EXPECT_TRUE(root.InsertTableArray("inline_array[0]", "inline_array", true)); + EXPECT_TRUE(root.InsertValue("", "inline_array[0].b", 20)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{b = 20}], [{a = 10}]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_TRUE(root.InsertTableArray("", "inline_array", true)); + EXPECT_TRUE(root.InsertValue("", "inline_array[999].c", 30)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{b = 20}], [{a = 10}], [{c = 30}]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_TRUE(root.InsertTableArray("inline_array[0]", "inline_array", false)); + EXPECT_TRUE(root.InsertValue("", "inline_array[0].d", 40)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{d = 40}], [{b = 20}], [{a = 10}], [{c = 30}]])toml"); + + // Add an additional table array entry for table array #4 + EXPECT_TRUE(root.InsertValue("", "inline_array[0].e", 50)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{d = 40, e = 50}], [{b = 20}], [{a = 10}], [{c = 30}]])toml"); +} + +TEST(CascadeInsertNode, InsertValueInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTableArray("", "table_array", false)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert the values into the table + EXPECT_TRUE(root.InsertValue("", "table_array.value_int", 10)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_int = 10)toml"); + EXPECT_TRUE(root.InsertValue("", "table_array.value_str", u8"abc")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_int = 10 +value_str = "abc")toml"); + EXPECT_TRUE(root.InsertValue("table_array[0]", "table_array.value_float", 123.456)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_float = 123.456 +value_int = 10 +value_str = "abc")toml"); +} + +TEST(CascadeInsertNode, InsertTableInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTableArray("", "table_array", false)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert a standard table + EXPECT_TRUE(root.InsertTable("", "table_array.table1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +[table_array.table1])toml"); + + // Insert an inline table before + EXPECT_TRUE(root.InsertTable("table_array[0]", "table_array.table2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table2 = {} +[table_array.table1])toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_TRUE(root.InsertTable("", "table_array.table3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table2 = {} +table3 = {} +[table_array.table1])toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_TRUE(root.InsertTable("table_array[0]", "table_array.table4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table2 = {} +table3 = {} +[table_array.table4] +[table_array.table1])toml"); +} + +TEST(CascadeInsertNode, InsertArrayInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTableArray("", "table_array", false)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert arrays + EXPECT_TRUE(root.InsertArray("", "table_array.value_array1")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_array1 = [])toml"); + EXPECT_TRUE(root.InsertArray("", "table_array.value_array2")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_array1 = [] +value_array2 = [])toml"); + EXPECT_TRUE(root.InsertArray("table_array[0]", "table_array.value_array3")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_array3 = [] +value_array1 = [] +value_array2 = [])toml"); +} + +TEST(CascadeInsertNode, InsertTableArrayInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTableArray("", "table_array", false)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert standard table array + EXPECT_TRUE(root.InsertTableArray("", "table_array.table_array1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +[[table_array.table_array1]])toml"); + + // Insert an inline table array before + EXPECT_TRUE(root.InsertTableArray("table_array[0]", "table_array.table_array2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +[[table_array.table_array1]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_TRUE(root.InsertTableArray("", "table_array.table_array3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +table_array3 = [{}] +[[table_array.table_array1]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_TRUE(root.InsertTableArray("table_array[0]", "table_array.table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +table_array3 = [{}] +[[table_array.table_array4]] +[[table_array.table_array1]])toml"); + + // Add an additional table array entry for table array #4 + EXPECT_TRUE(root.InsertTableArray("", "table_array.table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +table_array3 = [{}] +[[table_array.table_array4]] +[[table_array.table_array1]] +[[table_array.table_array4]])toml"); +} diff --git a/tests/unit_tests/toml_parser/character_reader_tests.cpp b/tests/unit_tests/toml_parser/character_reader_tests.cpp index a5791c3..328b116 100644 --- a/tests/unit_tests/toml_parser/character_reader_tests.cpp +++ b/tests/unit_tests/toml_parser/character_reader_tests.cpp @@ -13,6 +13,7 @@ ********************************************************************************/ #include +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.h" #include "../../../sdv_services/core/toml_parser/exception.h" diff --git a/tests/unit_tests/toml_parser/generate_toml_combine_reduce.cpp b/tests/unit_tests/toml_parser/combine_reduce.cpp similarity index 74% rename from tests/unit_tests/toml_parser/generate_toml_combine_reduce.cpp rename to tests/unit_tests/toml_parser/combine_reduce.cpp index cfa13e4..e979677 100644 --- a/tests/unit_tests/toml_parser/generate_toml_combine_reduce.cpp +++ b/tests/unit_tests/toml_parser/combine_reduce.cpp @@ -12,11 +12,11 @@ ********************************************************************************/ #include - +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/parser_node_toml.h" #include "../../../sdv_services/core/toml_parser/parser_toml.h" -TEST(GenerateTOML, CombineRoot) +TEST(CombineReduse, CombineRoot) { toml_parser::CParser parser1(R"toml( val1 = 10 @@ -27,10 +27,10 @@ val1 = 10)toml"); EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code( + std::string ssCombinedTOML = R"toml( val1 = 10 val2 = "20" -val3 = 30.0)code"; +val3 = 30.0)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -38,7 +38,7 @@ val3 = 30.0)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineRootComments) +TEST(CombineReduse, CombineRootComments) { toml_parser::CParser parser1(R"toml( val1 = 10 # This is value 1 @@ -49,10 +49,10 @@ val1 = 10 # This is again value 1)toml"); EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code( + std::string ssCombinedTOML = R"toml( val1 = 10 # This is value 1 val2 = "20" # This is value 2 -val3 = 30.0 # This is value 3)code"; +val3 = 30.0 # This is value 3)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -60,7 +60,7 @@ val3 = 30.0 # This is value 3)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineRootwithTable) +TEST(CombineReduse, CombineRootwithTableValues) { toml_parser::CParser parser1(R"toml( val1 = 10 @@ -73,10 +73,10 @@ val1 = 10)toml"); ASSERT_TRUE(ptrTable); EXPECT_TRUE(parser1.Root().Combine(ptrTable->Cast())); - std::string ssCombinedTOML = R"code( + std::string ssCombinedTOML = R"toml( val1 = 10 val2 = "20" -val3 = 30.0)code"; +val3 = 30.0)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -84,7 +84,55 @@ val3 = 30.0)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineTablewithRoot) +TEST(CombineReduse, CombineRootwithStandardTable) +{ + toml_parser::CParser parser1(R"toml( +val1 = 10 +val2 = "20")toml"); + toml_parser::CParser parser2(R"toml([MyTable] +val3 = 30.0 +val1 = 10)toml"); + + auto ptrTable = &parser2.Root(); + ASSERT_TRUE(ptrTable); + EXPECT_TRUE(parser1.Root().Combine(ptrTable->Cast())); + + std::string ssCombinedTOML = R"toml( +val1 = 10 +val2 = "20" +[MyTable] +val3 = 30.0 +val1 = 10)toml"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(CombineReduse, CombineRootwithInlineTable) +{ + toml_parser::CParser parser1(R"toml( +val1 = 10 +val2 = "20")toml"); + toml_parser::CParser parser2(R"toml(MyTable = {val3 = 30.0, val1 = 10})toml"); + + auto ptrTable = &parser2.Root(); + ASSERT_TRUE(ptrTable); + EXPECT_TRUE(parser1.Root().Combine(ptrTable->Cast())); + + std::string ssCombinedTOML = R"toml( +val1 = 10 +val2 = "20" +MyTable = {val3 = 30.0, val1 = 10})toml"; + + std::string ssGenerated; + EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); + + EXPECT_EQ(ssGenerated, ssCombinedTOML); +} + +TEST(CombineReduse, CombineTablewithRoot) { toml_parser::CParser parser1(R"toml([MyTable] val1 = 10 @@ -99,10 +147,10 @@ val1 = 10)toml"); ASSERT_TRUE(ptrTable); EXPECT_TRUE(ptrTable->Combine(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code([MyTable] + std::string ssCombinedTOML = R"toml([MyTable] val1 = 10 val2 = "20" -val3 = 30.0)code"; +val3 = 30.0)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -110,7 +158,7 @@ val3 = 30.0)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineTablewithDifferentTable) +TEST(CombineReduse, CombineTablewithDifferentTable) { toml_parser::CParser parser1(R"toml([MyTable1] val1 = 10 @@ -129,10 +177,10 @@ val1 = 10)toml"); ASSERT_TRUE(ptrTable2); EXPECT_TRUE(ptrTable1->Combine(ptrTable2)); - std::string ssCombinedTOML = R"code([MyTable1] + std::string ssCombinedTOML = R"toml([MyTable1] val1 = 10 val2 = "20" -val3 = 30.0)code"; +val3 = 30.0)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -140,7 +188,7 @@ val3 = 30.0)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineTablewithIdenticalTable) +TEST(CombineReduse, CombineTablewithIdenticalTable) { toml_parser::CParser parser1(R"toml([MyTable] val1 = 10 @@ -151,10 +199,10 @@ val1 = 10)toml"); EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code([MyTable] + std::string ssCombinedTOML = R"toml([MyTable] val1 = 10 val2 = "20" -val3 = 30.0)code"; +val3 = 30.0)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -162,7 +210,7 @@ val3 = 30.0)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineInlineTablewithDifferentStandardTable) +TEST(CombineReduse, CombineInlineTablewithDifferentStandardTable) { toml_parser::CParser parser1(R"toml(MyTable1 = {val1 = 10, val2 = "20"})toml"); toml_parser::CParser parser2(R"toml([MyTable2] @@ -179,7 +227,7 @@ val1 = 10)toml"); ASSERT_TRUE(ptrTable2); EXPECT_TRUE(ptrTable1->Combine(ptrTable2)); - std::string ssCombinedTOML = R"code(MyTable1 = {val1 = 10, val2 = "20", val3 = 30.0})code"; + std::string ssCombinedTOML = R"toml(MyTable1 = {val1 = 10, val2 = "20", val3 = 30.0})toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -187,7 +235,7 @@ val1 = 10)toml"); EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineInlineTablewithIdenticalStandardTable) +TEST(CombineReduse, CombineInlineTablewithIdenticalStandardTable) { toml_parser::CParser parser1(R"toml(MyTable = {val1 = 10, val2 = "20"})toml"); toml_parser::CParser parser2(R"toml([MyTable] @@ -196,7 +244,7 @@ val1 = 10)toml"); EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code(MyTable = {val1 = 10, val2 = "20", val3 = 30.0})code"; + std::string ssCombinedTOML = R"toml(MyTable = {val1 = 10, val2 = "20", val3 = 30.0})toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -204,7 +252,7 @@ val1 = 10)toml"); EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineStandardTablewithDifferentInlineTable) +TEST(CombineReduse, CombineStandardTablewithDifferentInlineTable) { toml_parser::CParser parser1(R"toml([MyTable1] val1 = 10 @@ -221,10 +269,10 @@ val2 = "20")toml"); ASSERT_TRUE(ptrTable2); EXPECT_TRUE(ptrTable1->Combine(ptrTable2)); - std::string ssCombinedTOML = R"code([MyTable1] + std::string ssCombinedTOML = R"toml([MyTable1] val1 = 10 val2 = "20" -val3 = 30.0)code"; +val3 = 30.0)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -232,7 +280,7 @@ val3 = 30.0)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineStandardTablewithIdenticalInlineTable) +TEST(CombineReduse, CombineStandardTablewithIdenticalInlineTable) { toml_parser::CParser parser1(R"toml([MyTable] val1 = 10 @@ -241,10 +289,10 @@ val2 = "20")toml"); EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code([MyTable] + std::string ssCombinedTOML = R"toml([MyTable] val1 = 10 val2 = "20" -val3 = 30.0)code"; +val3 = 30.0)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -252,7 +300,7 @@ val3 = 30.0)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineInlineTablewithDifferentInlineTable) +TEST(CombineReduse, CombineInlineTablewithDifferentInlineTable) { toml_parser::CParser parser1(R"toml(MyTable1 = {val1 = 10, val2 = "20"})toml"); toml_parser::CParser parser2(R"toml(MyTable2 = {val3 = 30.0, val1 = 10})toml"); @@ -267,7 +315,7 @@ TEST(GenerateTOML, CombineInlineTablewithDifferentInlineTable) ASSERT_TRUE(ptrTable2); EXPECT_TRUE(ptrTable1->Combine(ptrTable2)); - std::string ssCombinedTOML = R"code(MyTable1 = {val1 = 10, val2 = "20", val3 = 30.0})code"; + std::string ssCombinedTOML = R"toml(MyTable1 = {val1 = 10, val2 = "20", val3 = 30.0})toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -275,14 +323,14 @@ TEST(GenerateTOML, CombineInlineTablewithDifferentInlineTable) EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineInlineTablewithIdenticalInlineTable) +TEST(CombineReduse, CombineInlineTablewithIdenticalInlineTable) { toml_parser::CParser parser1(R"toml(MyTable = {val1 = 10, val2 = "20"})toml"); toml_parser::CParser parser2(R"toml(MyTable = {val3 = 30.0, val1 = 10})toml"); EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code(MyTable = {val1 = 10, val2 = "20", val3 = 30.0})code"; + std::string ssCombinedTOML = R"toml(MyTable = {val1 = 10, val2 = "20", val3 = 30.0})toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -290,7 +338,7 @@ TEST(GenerateTOML, CombineInlineTablewithIdenticalInlineTable) EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineDifferentArrays) +TEST(CombineReduse, CombineDifferentArrays) { toml_parser::CParser parser1(R"toml( val1 = [10, 20] @@ -301,10 +349,10 @@ val1 = [70, 80])toml"); EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code( + std::string ssCombinedTOML = R"toml( val1 = [70, 80] val2 = ["30", "40"] -val3 = [50.0, 60.0])code"; +val3 = [50.0, 60.0])toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -312,12 +360,13 @@ val3 = [50.0, 60.0])code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, CombineDifferentTableArrays) +TEST(CombineReduse, CombineDifferentTableArrays) { toml_parser::CParser parser1(R"toml([[table_array1]] val1 = [10, 20] [[table_array1]] val2 = ["30", "40"])toml"); + toml_parser::CParser parser2(R"toml( [[table_array2]] val3 = [50.0, 60.0] @@ -326,14 +375,14 @@ val1 = [70, 80])toml"); EXPECT_TRUE(parser1.Root().Combine(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code([[table_array1]] + std::string ssCombinedTOML = R"toml([[table_array1]] val1 = [10, 20] [[table_array1]] val2 = ["30", "40"] [[table_array2]] val3 = [50.0, 60.0] [[table_array2]] -val1 = [70, 80])code"; +val1 = [70, 80])toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -341,7 +390,7 @@ val1 = [70, 80])code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, ReduceRoot) +TEST(CombineReduse, ReduceRoot) { toml_parser::CParser parser1(R"toml( val1 = 10 @@ -353,9 +402,8 @@ val1 = 10)toml"); EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code( -val2 = "20" -val3 = 30.0)code"; + std::string ssCombinedTOML = R"toml(val2 = "20" +val3 = 30.0)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -363,7 +411,7 @@ val3 = 30.0)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, ReduceRootComments) +TEST(CombineReduse, ReduceRootComments) { toml_parser::CParser parser1(R"toml( val1 = 10 # This is value 1 @@ -375,9 +423,8 @@ val1 = 10 # This also is value 1)toml"); EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code( -val2 = "20" # This is value 2 -val3 = 30.0 # This is value 3)code"; + std::string ssCombinedTOML = R"toml(val2 = "20" # This is value 2 +val3 = 30.0 # This is value 3)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -385,7 +432,7 @@ val3 = 30.0 # This is value 3)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, ReduceTable) +TEST(CombineReduse, ReduceTable) { toml_parser::CParser parser1(R"toml([my_table] val1 = 10 @@ -397,9 +444,9 @@ val1 = 10)toml"); EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code([my_table] + std::string ssCombinedTOML = R"toml([my_table] val2 = "20" -val3 = 30.0)code"; +val3 = 30.0)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -407,7 +454,7 @@ val3 = 30.0)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, ReduceStandardTableWithInlineTable) +TEST(CombineReduse, ReduceStandardTableWithInlineTable) { toml_parser::CParser parser1(R"toml([my_table] val1 = 10 @@ -417,9 +464,9 @@ val3 = 30.0)toml"); EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code([my_table] + std::string ssCombinedTOML = R"toml([my_table] val2 = "20" -val3 = 30.0)code"; +val3 = 30.0)toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -427,7 +474,7 @@ val3 = 30.0)code"; EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, ReduceInlineTableWithStandardTable) +TEST(CombineReduse, ReduceInlineTableWithStandardTable) { toml_parser::CParser parser1(R"toml(my_table = {val1 = 10, val2 = "20", val3 = 30.0})toml"); toml_parser::CParser parser2(R"toml([my_table] @@ -436,7 +483,7 @@ val1 = 10)toml"); EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code(my_table = { val2 = "20", val3 = 30.0})code"; + std::string ssCombinedTOML = R"toml(my_table = { val2 = "20", val3 = 30.0})toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -444,14 +491,14 @@ val1 = 10)toml"); EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, ReduceInlineTable) +TEST(CombineReduse, ReduceInlineTable) { toml_parser::CParser parser1(R"toml(my_table = {val1 = 10, val2 = "20", val3 = 30.0})toml"); toml_parser::CParser parser2(R"toml(my_table = {val3 = 35.0, val1 = 10})toml"); EXPECT_TRUE(parser1.Root().Reduce(parser2.Root().Cast())); - std::string ssCombinedTOML = R"code(my_table = { val2 = "20", val3 = 30.0})code"; + std::string ssCombinedTOML = R"toml(my_table = { val2 = "20", val3 = 30.0})toml"; std::string ssGenerated; EXPECT_NO_THROW(ssGenerated = parser1.GenerateTOML()); @@ -459,12 +506,12 @@ TEST(GenerateTOML, ReduceInlineTable) EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, ReduceDifferentArrays) +TEST(CombineReduse, ReduceDifferentArrays) { - toml_parser::CParser parser1(R"code( + toml_parser::CParser parser1(R"toml( val1 = [10, 20] val2 = ["30", "40"] -val3 = [50.0, 60.0])code"); +val3 = [50.0, 60.0])toml"); toml_parser::CParser parser2(R"toml( val3 = [50.0, 60.0] val1 = [70, 80])toml"); @@ -482,16 +529,16 @@ val2 = ["30", "40"] EXPECT_EQ(ssGenerated, ssCombinedTOML); } -TEST(GenerateTOML, ReduceDifferentTableArrays) +TEST(CombineReduse, ReduceDifferentTableArrays) { - toml_parser::CParser parser1(R"code([[table_array1]] + toml_parser::CParser parser1(R"toml([[table_array1]] val1 = [10, 20] [[table_array1]] val2 = ["30", "40"] [[table_array2]] val3 = [50.0, 60.0] [[table_array2]] -val1 = [70, 80])code"); +val1 = [70, 80])toml"); toml_parser::CParser parser2(R"toml( [[table_array1]] val2 = ["30", "40"] diff --git a/tests/unit_tests/toml_parser/comparison_tests.cpp b/tests/unit_tests/toml_parser/comparison_tests.cpp new file mode 100644 index 0000000..bdca9d7 --- /dev/null +++ b/tests/unit_tests/toml_parser/comparison_tests.cpp @@ -0,0 +1,386 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial implementation + ********************************************************************************/ + +#include +#include +#include +#include "../../../global/localmemmgr.h" +#include +#include "../../../sdv_services/core/toml_parser/exception.h" +#include "../../../sdv_services/core/toml_parser/miscellaneous.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" + +bool CompareTest(const std::string& rssToml1, const std::string& rssToml2, + uint32_t uiCompareFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_all)) +{ + try + { + toml_parser::CParser parser1(rssToml1); + toml_parser::CParser parser2(rssToml2); + sdv::toml::CNodeCollection collection1(&parser1.Root()); + sdv::toml::CNodeCollection collection2(&parser2.Root()); + return sdv::toml::internal::CompareNodes(collection1, collection2, uiCompareFlags) == + sdv::toml::ECompareResult::compare_identical; + } + catch (const toml_parser::XTOMLParseException&) + { + return false; + } +} + +TEST(Comparison, CompareInvalid) +{ + EXPECT_FALSE(CompareTest("var = A", "")); + EXPECT_FALSE(CompareTest("[[test]", "")); + EXPECT_FALSE(CompareTest(R"toml([test] +test1 = "this is invalid')toml", "")); +} + +TEST(Comparison, CompareEmpty) +{ + // Identical + uint32_t uiFlags = 0u; + EXPECT_FALSE(CompareTest("var = 1", "", uiFlags)); + EXPECT_FALSE(CompareTest("", "var = 2", uiFlags)); + EXPECT_TRUE(CompareTest("", "", uiFlags)); + EXPECT_FALSE(CompareTest("", R"toml( +)toml", uiFlags)); + EXPECT_FALSE(CompareTest("", R"toml(# This is a comment)toml", uiFlags)); + + // Ignore whitespace + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_whitespace); + EXPECT_FALSE(CompareTest("var = 1", "", uiFlags)); + EXPECT_FALSE(CompareTest("", "var = 2", uiFlags)); + EXPECT_TRUE(CompareTest("", "", uiFlags)); + EXPECT_TRUE(CompareTest("", R"toml( +)toml", uiFlags)); + EXPECT_FALSE(CompareTest("", R"toml(# This is a comment)toml", uiFlags)); + + // Ignore comments + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_comments); + EXPECT_FALSE(CompareTest("var = 1", "", uiFlags)); + EXPECT_FALSE(CompareTest("", "var = 2", uiFlags)); + EXPECT_TRUE(CompareTest("", "", uiFlags)); + EXPECT_TRUE(CompareTest("", R"toml( +)toml", uiFlags)); + EXPECT_TRUE(CompareTest("", R"toml(# This is a comment)toml", uiFlags)); +} + +TEST(Comparison, CompareValues) +{ + std::string ssVal = "val = 1"; + std::string ssVal2 = "val = 2"; + std::string ssValSpace = " val = 1"; + std::string ssValCommentsBefore = R"toml(# comment + +# more comment + val = 1)toml"; + std::string ssValCommentsBehind = R"toml( val = 1 # comment +# more comment + +# lots more comment)toml"; + + // Identical + uint32_t uiFlags = 0u; + EXPECT_TRUE(CompareTest(ssVal, ssVal, uiFlags)); + EXPECT_FALSE(CompareTest(ssVal, ssVal2, uiFlags)); + EXPECT_FALSE(CompareTest(ssVal, ssValSpace, uiFlags)); + EXPECT_FALSE(CompareTest(ssVal, ssValCommentsBefore, uiFlags)); + EXPECT_FALSE(CompareTest(ssVal, ssValCommentsBehind, uiFlags)); + + // Ignore whitespace + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_whitespace); + EXPECT_TRUE(CompareTest(ssVal, ssVal, uiFlags)); + EXPECT_FALSE(CompareTest(ssVal, ssVal2, uiFlags)); + EXPECT_TRUE(CompareTest(ssVal, ssValSpace, uiFlags)); + EXPECT_FALSE(CompareTest(ssVal, ssValCommentsBefore, uiFlags)); + EXPECT_FALSE(CompareTest(ssVal, ssValCommentsBehind, uiFlags)); + + // Ignore comments + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_comments); + EXPECT_TRUE(CompareTest(ssVal, ssVal, uiFlags)); + EXPECT_FALSE(CompareTest(ssVal, ssVal2, uiFlags)); + EXPECT_TRUE(CompareTest(ssVal, ssValSpace, uiFlags)); + EXPECT_TRUE(CompareTest(ssVal, ssValCommentsBefore, uiFlags)); + EXPECT_TRUE(CompareTest(ssVal, ssValCommentsBehind, uiFlags)); + + // Ignore all + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_all); + EXPECT_TRUE(CompareTest(ssVal, ssVal, uiFlags)); + EXPECT_FALSE(CompareTest(ssVal, ssVal2, uiFlags)); + EXPECT_TRUE(CompareTest(ssVal, ssValSpace, uiFlags)); + EXPECT_TRUE(CompareTest(ssVal, ssValCommentsBefore, uiFlags)); + EXPECT_TRUE(CompareTest(ssVal, ssValCommentsBehind, uiFlags)); +} + +TEST(Comparison, CompareTables) +{ + std::string ssStandardTable = R"toml([table1] +val = 1)toml"; + std::string ssStandardTable2 = R"toml([table2] +val = 1)toml"; + std::string ssStandardTable1_3 = R"toml([table1] +val = 3)toml"; + std::string ssInlineTable = R"toml(table1 = {val = 1})toml"; + std::string ssTableSpace = R"toml( [table1] + + val = 1)toml"; + std::string ssTableCommentsBefore = R"toml(# comment + +# more comment + [table1] +val = 1)toml"; + std::string ssTableCommentsBehind = R"toml( [table1] # comment +val = 1 # comment +# more comment + +# lots more comment)toml"; + + // Identical + uint32_t uiFlags = 0u; + EXPECT_TRUE(CompareTest(ssStandardTable, ssStandardTable, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssStandardTable2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssStandardTable1_3, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssInlineTable, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssTableSpace, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssTableCommentsBefore, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssTableCommentsBehind, uiFlags)); + + // Ignore whitespace + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_whitespace); + EXPECT_TRUE(CompareTest(ssStandardTable, ssStandardTable, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssStandardTable2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssStandardTable1_3, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssInlineTable, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTable, ssTableSpace, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssTableCommentsBefore, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssTableCommentsBehind, uiFlags)); + + // Ignore comments + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_comments); + EXPECT_TRUE(CompareTest(ssStandardTable, ssStandardTable, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssStandardTable2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssStandardTable1_3, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssInlineTable, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTable, ssTableSpace, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTable, ssTableCommentsBefore, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTable, ssTableCommentsBehind, uiFlags)); + + // Ignore inline + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_inline); + EXPECT_TRUE(CompareTest(ssStandardTable, ssStandardTable, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssStandardTable2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssStandardTable1_3, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTable, ssInlineTable, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTable, ssTableSpace, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssTableCommentsBefore, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssTableCommentsBehind, uiFlags)); + + // Ignore all + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_all); + EXPECT_TRUE(CompareTest(ssStandardTable, ssStandardTable, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssStandardTable2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTable, ssStandardTable1_3, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTable, ssInlineTable, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTable, ssTableSpace, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTable, ssTableCommentsBefore, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTable, ssTableCommentsBehind, uiFlags)); +} + +TEST(Comparison, CompareArrays) +{ + std::string ssStandardArray = R"toml(array1 = [123, 456])toml"; + std::string ssStandardArray2 = R"toml(array2 = [789, 543])toml"; + std::string ssArraySpace = R"toml( array1 = [ + 123, + 456 ])toml"; + std::string ssArrayCommentsBefore = R"toml(# comment +array1 = +# more comment + [ 123, # xyz +456])toml"; + std::string ssArrayCommentsBehind = R"toml( array1 = [123, 456] # comment +# more comment + +# lots more comment)toml"; + + // Identical + uint32_t uiFlags = 0u; + EXPECT_TRUE(CompareTest(ssStandardArray, ssStandardArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardArray, ssStandardArray2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardArray, ssArraySpace, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardArray, ssArrayCommentsBefore, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardArray, ssArrayCommentsBehind, uiFlags)); + + // Ignore whitespace + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_whitespace); + EXPECT_TRUE(CompareTest(ssStandardArray, ssStandardArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardArray, ssStandardArray2, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardArray, ssArraySpace, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardArray, ssArrayCommentsBefore, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardArray, ssArrayCommentsBehind, uiFlags)); + + // Ignore comments + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_comments); + EXPECT_TRUE(CompareTest(ssStandardArray, ssStandardArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardArray, ssStandardArray2, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardArray, ssArraySpace, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardArray, ssArrayCommentsBefore, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardArray, ssArrayCommentsBehind, uiFlags)); + + // Ignore all + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_all); + EXPECT_TRUE(CompareTest(ssStandardArray, ssStandardArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardArray, ssStandardArray2, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardArray, ssArraySpace, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardArray, ssArrayCommentsBefore, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardArray, ssArrayCommentsBehind, uiFlags)); +} + +TEST(Comparison, CompareTableArrays) +{ + std::string ssStandardTableArray = R"toml([[tableArray1]] +val = 1)toml"; + std::string ssStandardTableArray2 = R"toml([[tableArray2]] +val = 1)toml"; + std::string ssStandardTableArray1_3 = R"toml([[tableArray1]] +val = 3)toml"; + std::string ssInlineTableArray = R"toml(tableArray1 = [{val = 1}])toml"; + std::string ssTableSpaceArray = R"toml( [[tableArray1]] + + val = 1)toml"; + std::string ssTableArrayCommentsBefore = R"toml(# comment + +# more comment + [[tableArray1]] +val = 1)toml"; + std::string ssTableArrayCommentsBehind = R"toml( [[tableArray1]] # comment +val = 1 # comment +# more comment + +# lots more comment)toml"; + + // Identical + uint32_t uiFlags = 0u; + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssStandardTableArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssStandardTableArray2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssStandardTableArray1_3, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssInlineTableArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssTableSpaceArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssTableArrayCommentsBefore, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssTableArrayCommentsBehind, uiFlags)); + + // Ignore whitespace + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_whitespace); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssStandardTableArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssStandardTableArray2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssStandardTableArray1_3, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssInlineTableArray, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssTableSpaceArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssTableArrayCommentsBefore, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssTableArrayCommentsBehind, uiFlags)); + + // Ignore comments + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_comments); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssStandardTableArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssStandardTableArray2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssStandardTableArray1_3, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssInlineTableArray, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssTableSpaceArray, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssTableArrayCommentsBefore, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssTableArrayCommentsBehind, uiFlags)); + + // Ignore inline + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_inline); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssStandardTableArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssStandardTableArray2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssStandardTableArray1_3, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssInlineTableArray, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssTableSpaceArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssTableArrayCommentsBefore, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssTableArrayCommentsBehind, uiFlags)); + + // Ignore all + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_all); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssStandardTableArray, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssStandardTableArray2, uiFlags)); + EXPECT_FALSE(CompareTest(ssStandardTableArray, ssStandardTableArray1_3, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssInlineTableArray, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssTableSpaceArray, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssTableArrayCommentsBefore, uiFlags)); + EXPECT_TRUE(CompareTest(ssStandardTableArray, ssTableArrayCommentsBehind, uiFlags)); +} + +TEST(Comparison, CompareComplex) +{ + std::string ssToml1 = R"toml(# This is a complex TOML file + +# Top value description +[Top] +value1 = 1 # Value 1 +valueA = "A" # Value A +arrayBCD = [ "B", # Array element B + "C", # Array element C + "D" ] # Array element D + +# Table 1 +[Top.table1] +value2 = 2 # Value 2 + +# Table array in table 1 +[[Top.table1.table_array]] # Table array +valueE = "E" + +[[Top.table1.table_array]] # Table array +valueF = "F" + +[Top.table1.other_table] +inline_table = { x = "X", y = "Y", z = "Z" } # Inline table + +[[Top.table1.table_array]] # Table array +valueG = "G" +)toml"; + + std::string ssToml2 = R"toml( +[Top] + value1 = 1 + valueA = "A" + arrayBCD = ["B", "C", "D" ] + table1 = { value2 = 2, table_array = [{valueE = "E"}, {valueF = "F"}, {valueG = "G"}], other_table = { inline_table = {x = "X", y = "Y", z = "Z"}}} # Mega table +)toml"; + + // Identical + uint32_t uiFlags = 0u; + EXPECT_TRUE(CompareTest(ssToml1, ssToml1, uiFlags)); + EXPECT_FALSE(CompareTest(ssToml1, ssToml2, uiFlags)); + + // Ignore whitespace + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_whitespace); + EXPECT_TRUE(CompareTest(ssToml1, ssToml1, uiFlags)); + EXPECT_FALSE(CompareTest(ssToml1, ssToml2, uiFlags)); + + // Ignore comments + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_comments); + EXPECT_TRUE(CompareTest(ssToml1, ssToml1, uiFlags)); + EXPECT_FALSE(CompareTest(ssToml1, ssToml2, uiFlags)); + + // Ignore inline + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_inline); + EXPECT_TRUE(CompareTest(ssToml1, ssToml1, uiFlags)); + EXPECT_FALSE(CompareTest(ssToml1, ssToml2, uiFlags)); + + // Ignore all + uiFlags = static_cast(sdv::toml::ECompareFlags::compare_ignore_all); + EXPECT_TRUE(CompareTest(ssToml1, ssToml1, uiFlags)); + EXPECT_TRUE(CompareTest(ssToml1, ssToml2, uiFlags)); +} \ No newline at end of file diff --git a/tests/unit_tests/toml_parser/generate_toml_delete_node.cpp b/tests/unit_tests/toml_parser/delete_node.cpp similarity index 76% rename from tests/unit_tests/toml_parser/generate_toml_delete_node.cpp rename to tests/unit_tests/toml_parser/delete_node.cpp index 8edccec..3496c70 100644 --- a/tests/unit_tests/toml_parser/generate_toml_delete_node.cpp +++ b/tests/unit_tests/toml_parser/delete_node.cpp @@ -12,7 +12,7 @@ ********************************************************************************/ #include - +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/parser_node_toml.h" #include "../../../sdv_services/core/toml_parser/parser_toml.h" @@ -45,321 +45,315 @@ * @param[in] rssOuput Reference to the expected ouput. * @return Returns 'true' on success. */ -bool TestDelete(const std::string& rssTOMLInput, const std::string& rssKey, const std::string& rssOutput) +std::string DeleteNode(const std::string& rssTOMLInput, const std::string& rssKey) { toml_parser::CParser parser; bool bRes = true; EXPECT_NO_THROW(bRes = parser.Process(rssTOMLInput)); EXPECT_TRUE(bRes); - if (!bRes) return bRes; + if (!bRes) return {}; auto ptrNode = parser.Root().Direct(rssKey); EXPECT_TRUE(ptrNode); - if (!ptrNode) return false; + if (!ptrNode) return {}; EXPECT_TRUE(bRes = ptrNode->DeleteNode()); - if (!bRes) return bRes; - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, rssOutput); - if (ssTOML != rssOutput) return false; - return true; + if (!bRes) return {}; + return parser.GenerateTOML(); }; -TEST(TOMLDeleteNode, DeleteValues) +TEST(DeleteNode, DeleteValues) { // Delete a key from the begin (whitespace reduced) - EXPECT_TRUE(TestDelete(R"toml(key = 10 # value key + EXPECT_EQ(DeleteNode(R"toml(key = 10 # value key bare_key = "value" # value bare_key bare-key = false # value bare-key)toml", - "key", + "key"), R"toml(bare_key = "value" # value bare_key -bare-key = false # value bare-key)toml")); +bare-key = false # value bare-key)toml"); // Delete a key from the begin - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( key = 10 # value key bare_key = "value" # value bare_key bare-key = false # value bare-key )toml", - "key", - R"toml( -bare_key = "value" # value bare_key + "key"), + R"toml(bare_key = "value" # value bare_key bare-key = false # value bare-key -)toml")); +)toml"); // Delete a key from the middle - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( key = 10 # value key bare_key = "value" # value bare_key bare-key = false # value bare-key )toml", - "bare_key", + "bare_key"), R"toml( key = 10 # value key bare-key = false # value bare-key -)toml")); +)toml"); // Delete a key from the end - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( key = 10 # value key bare_key = "value" # value bare_key bare-key = false # value bare-key )toml", - "bare-key", + "bare-key"), R"toml( key = 10 # value key bare_key = "value" # value bare_key -)toml")); +)toml"); } -TEST(TOMLDeleteNode, DeleteInlineTableValues) +TEST(DeleteNode, DeleteInlineTableValues) { // Delete key from the inline table - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc"} )toml", - "1234.y", + "1234.y"), R"toml( key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, z = 2, str = "abc"} -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc"} )toml", - "1234.x", + "1234.x"), R"toml( key = 10 bare_key = "value" bare-key = false 1234 = { y = 1, z = 2, str = "abc"} -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc"} )toml", - "1234.str", + "1234.str"), R"toml( key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2} -)toml")); - +)toml"); } -TEST(TOMLDeleteNode, DeleteInlineSubTableValues) +TEST(DeleteNode, DeleteInlineSubTableValues) { // Delete key from the inline sub-table - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} )toml", - "1234.tbl.b", + "1234.tbl.b"), R"toml( key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, c=3}} -)toml")); - +)toml"); } -TEST(TOMLDeleteNode, DeleteInlineTables) +TEST(DeleteNode, DeleteInlineTables) { // Delete table - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} )toml", - "1234.tbl", + "1234.tbl"), R"toml( key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc"} -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} )toml", - "1234", + "1234"), R"toml( key = 10 bare_key = "value" bare-key = false -)toml")); +)toml"); } -TEST(TOMLDeleteNode, DeleteTableValues) +TEST(DeleteNode, DeleteTableValues) { - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" bare-key = false )toml", - "my_table.key", + "my_table.key"), R"toml( [my_table] bare_key = "value" bare-key = false -)toml")); +)toml"); // Delete a key from the middle - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" bare-key = false )toml", - "my_table.bare_key", + "my_table.bare_key"), R"toml( [my_table] key = 10 bare-key = false -)toml")); +)toml"); // Delete a key from the end - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" bare-key = false )toml", - "my_table.bare-key", + "my_table.bare-key"), R"toml( [my_table] key = 10 bare_key = "value" -)toml")); +)toml"); } -TEST(TOMLDeleteNode, DeleteInlineTableValueInTable) +TEST(DeleteNode, DeleteInlineTableValueInTable) { // Delete key from the inline table in a table - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc"} )toml", - "my_table.1234.y", + "my_table.1234.y"), R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, z = 2, str = "abc"} -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc"} )toml", - "my_table.1234.x", + "my_table.1234.x"), R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = { y = 1, z = 2, str = "abc"} -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc"} )toml", - "my_table.1234.str", + "my_table.1234.str"), R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2} -)toml")); +)toml"); } -TEST(TOMLDeleteNode, DeleteInlineSubTableValueInTable) +TEST(DeleteNode, DeleteInlineSubTableValueInTable) { // Delete key from the inline sub-table - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} )toml", - "my_table.1234.tbl.b", + "my_table.1234.tbl.b"), R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, c=3}} -)toml")); +)toml"); } -TEST(TOMLDeleteNode, DeleteInlineTableInTable) +TEST(DeleteNode, DeleteInlineTableInTable) { // Delete table - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} )toml", - "my_table.1234.tbl", + "my_table.1234.tbl"), R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc"} -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" bare-key = false 1234 = {x = 0, y = 1, z = 2, str = "abc", tbl={a =1, b=2, c=3}} )toml", - "my_table.1234", + "my_table.1234"), R"toml( [my_table] key = 10 bare_key = "value" bare-key = false -)toml")); +)toml"); } -TEST(TOMLDeleteNode, DeleteValuesInChildTable) +TEST(DeleteNode, DeleteValuesInChildTable) { // Delete key from the child-table - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" @@ -370,7 +364,7 @@ y = 1 z = 2 str = "abc" )toml", - "my_table.1234.y", + "my_table.1234.y"), R"toml( [my_table] key = 10 @@ -380,8 +374,8 @@ bare-key = false x = 0 z = 2 str = "abc" -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" @@ -392,7 +386,7 @@ y = 1 z = 2 str = "abc" )toml", - "my_table.1234.x", + "my_table.1234.x"), R"toml( [my_table] key = 10 @@ -402,8 +396,8 @@ bare-key = false y = 1 z = 2 str = "abc" -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" @@ -414,7 +408,7 @@ y = 1 z = 2 str = "abc" )toml", - "my_table.1234.str", + "my_table.1234.str"), R"toml( [my_table] key = 10 @@ -424,14 +418,14 @@ bare-key = false x = 0 y = 1 z = 2 -)toml")); +)toml"); } -TEST(TOMLDeleteNode, DeleteChildTableInTable) +TEST(DeleteNode, DeleteChildTableInTable) { // Delete table - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" @@ -446,7 +440,7 @@ a =1 b=2 c=3 )toml", - "my_table.1234.tbl", + "my_table.1234.tbl"), R"toml( [my_table] key = 10 @@ -457,8 +451,8 @@ x = 0 y = 1 z = 2 str = "abc" -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( [my_table] key = 10 bare_key = "value" @@ -473,176 +467,176 @@ a =1 b=2 c=3 )toml", - "my_table.1234", + "my_table.1234"), R"toml( [my_table] key = 10 bare_key = "value" bare-key = false -)toml")); +)toml"); } -TEST(TOMLDeleteNode, DeleteArrayValues) +TEST(DeleteNode, DeleteArrayValues) { // Delete array values - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] )toml", - "key[0]", + "key[0]"), R"toml( key = [ 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] )toml", - "key[1]", + "key[1]"), R"toml( key = [10, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] )toml", - "key[2]", + "key[2]"), R"toml( key = [10, 20] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] -)toml")); +)toml"); } -TEST(TOMLDeleteNode, DeleteArrayValuesWithSucceedingComma) +TEST(DeleteNode, DeleteArrayValuesWithSucceedingComma) { // Delete array values with succeeding comma - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] )toml", - "bare_key[0]", + "bare_key[0]"), R"toml( key = [10, 20, 30] bare_key = [ "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] )toml", - "bare_key[1]", + "bare_key[1]"), R"toml( key = [10, 20, 30] bare_key = ["value1", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] )toml", - "bare_key[2]", + "bare_key[2]"), R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] -)toml")); +)toml"); } -TEST(TOMLDeleteNode, DeleteValuesWithComments) +TEST(DeleteNode, DeleteValuesWithComments) { // Delete array values with comments - EXPECT_TRUE(TestDelete(R"toml( + EXPECT_EQ(DeleteNode(R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] )toml", - "bare-key[0]", + "bare-key[0]"), R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [ 2020, # value 1 ] -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] )toml", - "bare-key[0].a", + "bare-key[0].a"), R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{ b = true}, # value 0 2020, # value 1 ] -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] )toml", - "bare-key[0].b", + "bare-key[0].b"), R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false}, # value 0 2020, # value 1 ] -)toml")); - EXPECT_TRUE(TestDelete(R"toml( +)toml"); + EXPECT_EQ(DeleteNode(R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 2020, # value 1 ] )toml", - "bare-key[1]", + "bare-key[1]"), R"toml( key = [10, 20, 30] bare_key = ["value1", "value2", 3030, ] bare-key = [{a = false, b = true}, # value 0 ] -)toml")); +)toml"); } \ No newline at end of file diff --git a/tests/unit_tests/toml_parser/generate_toml_insert_node.cpp b/tests/unit_tests/toml_parser/generate_toml_insert_node.cpp deleted file mode 100644 index b817392..0000000 --- a/tests/unit_tests/toml_parser/generate_toml_insert_node.cpp +++ /dev/null @@ -1,1068 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025-2026 ZF Friedrichshafen AG - * - * This program and the accompanying materials are made available under the - * terms of the Apache License Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Erik Verhoeven - initial API and implementation - ********************************************************************************/ - -#include - -#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" -#include "../../../sdv_services/core/toml_parser/parser_toml.h" - -// Test TODO: -// - Inserted and straight away deleted -// - Inserted with false/deleted reference --error -// - Inserted values before (okay) and behind (error) tables -// - Inserted duplicate value -- error -// - Smart insert (comments/whitespace around) -// Insert as TOML, but only partly correct. - -TEST(TOMLInsertNode, TestInsertValuesRoot) -{ - toml_parser::CParser parser; - EXPECT_NE(parser.Root().InsertValue(sdv::toml::npos, "value_int", 10), nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(value_int = 10)toml"); - EXPECT_NE(parser.Root().InsertValue(sdv::toml::npos, "value_str", u8"abc"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(value_int = 10 -value_str = "abc")toml"); - EXPECT_NE(parser.Root().InsertValue(0, "value_float", 123.456), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(value_float = 123.456 -value_int = 10 -value_str = "abc")toml"); -} - -TEST(TOMLInsertNode, TestInsertTableRoot) -{ - // Insert a standard table - toml_parser::CParser parser; - EXPECT_NE(parser.Root().InsertTable(sdv::toml::npos, "table1", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([table1])toml"); - - // Insert an inline table before - EXPECT_NE(parser.Root().InsertTable(0, "table2", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table2 = {} -[table1])toml"); - - // Insert an inline table behind -> this will have to be printed before the standard table - EXPECT_NE(parser.Root().InsertTable(sdv::toml::npos, "table3", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table2 = {} -table3 = {} -[table1])toml"); - - // Insert a standard table in front -> this will have to be printed behind the inline table - EXPECT_NE(parser.Root().InsertTable(0, "table4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table2 = {} -table3 = {} -[table4] -[table1])toml"); -} - -TEST(TOMLInsertNode, TestInsertArrayRoot) -{ - toml_parser::CParser parser; - EXPECT_NE(parser.Root().InsertArray(sdv::toml::npos, "value_array1"), nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(value_array1 = [])toml"); - EXPECT_NE(parser.Root().InsertArray(sdv::toml::npos, "value_array2"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(value_array1 = [] -value_array2 = [])toml"); - EXPECT_NE(parser.Root().InsertArray(0, "value_array3"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(value_array3 = [] -value_array1 = [] -value_array2 = [])toml"); -} - -TEST(TOMLInsertNode, TestInsertTableArrayRoot) -{ - // Insert a standard table array - toml_parser::CParser parser; - EXPECT_NE(parser.Root().InsertTableArray(sdv::toml::npos, "table_array1", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array1]])toml"); - - // Insert an inline table array before - EXPECT_NE(parser.Root().InsertTableArray(0, "table_array2", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] -[[table_array1]])toml"); - - // Insert an inline table array behind -> this will have to be printed before the standard table array - EXPECT_NE(parser.Root().InsertTableArray(sdv::toml::npos, "table_array3", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] -table_array3 = [{}] -[[table_array1]])toml"); - - // Insert a standard table array in front -> this will have to be printed behind the inline table array - EXPECT_NE(parser.Root().InsertTableArray(0, "table_array4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] -table_array3 = [{}] -[[table_array4]] -[[table_array1]])toml"); - - // Add an additional table array entry for table array #4 - EXPECT_NE(parser.Root().InsertTableArray(sdv::toml::npos, "table_array4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] -table_array3 = [{}] -[[table_array4]] -[[table_array1]] -[[table_array4]])toml"); -} - -TEST(TOMLInsertNode, TestInsertValueInStandardTable) -{ - // Insert a standard table - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pStandardTable, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); - - // Insert the values into the table - EXPECT_NE(pStandardTable->InsertValue(sdv::toml::npos, "value_int", 10), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -value_int = 10)toml"); - EXPECT_NE(pStandardTable->InsertValue(sdv::toml::npos, "value_str", u8"abc"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -value_int = 10 -value_str = "abc")toml"); - EXPECT_NE(pStandardTable->InsertValue(0, "value_float", 123.456), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -value_float = 123.456 -value_int = 10 -value_str = "abc")toml"); -} - -TEST(TOMLInsertNode, TestInsertTableInStandardTable) -{ - // Insert a standard table - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pStandardTable, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); - - // Insert a standard table - EXPECT_NE(pStandardTable->InsertTable(sdv::toml::npos, "table1", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table.table1])toml"); - - // Insert an inline table before - EXPECT_NE(pStandardTable->InsertTable(0, "table2", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -table2 = {} -[standard_table.table1])toml"); - - // Insert an inline table behind -> this will have to be printed before the standard table - EXPECT_NE(pStandardTable->InsertTable(sdv::toml::npos, "table3", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -table2 = {} -table3 = {} -[standard_table.table1])toml"); - - // Insert a standard table in front -> this will have to be printed behind the inline table - EXPECT_NE(pStandardTable->InsertTable(0, "table4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -table2 = {} -table3 = {} -[standard_table.table4] -[standard_table.table1])toml"); -} - -TEST(TOMLInsertNode, TestInsertArrayInStandardTable) -{ - // Insert a standard table - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pStandardTable, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); - - // Insert arrays - EXPECT_NE(pStandardTable->InsertArray(sdv::toml::npos, "value_array1"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -value_array1 = [])toml"); - EXPECT_NE(pStandardTable->InsertArray(sdv::toml::npos, "value_array2"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -value_array1 = [] -value_array2 = [])toml"); - EXPECT_NE(pStandardTable->InsertArray(0, "value_array3"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -value_array3 = [] -value_array1 = [] -value_array2 = [])toml"); -} - -TEST(TOMLInsertNode, TestInsertTableArrayInStandardTable) -{ - // Insert a standard table - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pStandardTable, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); - - // Insert standard table array - EXPECT_NE(pStandardTable->InsertTableArray(sdv::toml::npos, "table_array1", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[standard_table.table_array1]])toml"); - - // Insert an inline table array before - EXPECT_NE(pStandardTable->InsertTableArray(0, "table_array2", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -table_array2 = [{}] -[[standard_table.table_array1]])toml"); - - // Insert an inline table array behind -> this will have to be printed before the standard table array - EXPECT_NE(pStandardTable->InsertTableArray(sdv::toml::npos, "table_array3", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -table_array2 = [{}] -table_array3 = [{}] -[[standard_table.table_array1]])toml"); - - // Insert a standard table array in front -> this will have to be printed behind the inline table array - EXPECT_NE(pStandardTable->InsertTableArray(0, "table_array4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -table_array2 = [{}] -table_array3 = [{}] -[[standard_table.table_array4]] -[[standard_table.table_array1]])toml"); - - // Add an additional table array entry for table array #4 - EXPECT_NE(pStandardTable->InsertTableArray(sdv::toml::npos, "table_array4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([standard_table] -table_array2 = [{}] -table_array3 = [{}] -[[standard_table.table_array4]] -[[standard_table.table_array1]] -[[standard_table.table_array4]])toml"); -} - -TEST(TOMLInsertNode, TestInsertValueInInlineTable) -{ - // Insert an inline table - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pInlineTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - sdv::toml::npos, "inline_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). - GetInterface(); - ASSERT_NE(pInlineTable, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); - - // Insert the values into the table - EXPECT_NE(pInlineTable->InsertValue(sdv::toml::npos, "value_int", 10), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {value_int = 10})toml"); - EXPECT_NE(pInlineTable->InsertValue(sdv::toml::npos, "value_str", u8"abc"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {value_int = 10, value_str = "abc"})toml"); - EXPECT_NE(pInlineTable->InsertValue(0, "value_float", 123.456), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {value_float = 123.456, value_int = 10, value_str = "abc"})toml"); -} - -TEST(TOMLInsertNode, TestInsertTableInInlineTable) -{ - // Insert an inline table - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pInlineTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - sdv::toml::npos, "inline_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). - GetInterface(); - ASSERT_NE(pInlineTable, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); - - // Insert a standard table - EXPECT_NE(pInlineTable->InsertTable(sdv::toml::npos, "table1", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {table1 = {}})toml"); - - // Insert an inline table before - EXPECT_NE(pInlineTable->InsertTable(0, "table2", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {table2 = {}, table1 = {}})toml"); - - // Insert an inline table behind -> this will have to be printed before the standard table - EXPECT_NE(pInlineTable->InsertTable(sdv::toml::npos, "table3", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {table2 = {}, table1 = {}, table3 = {}})toml"); - - // Insert a standard table in front -> this will have to be printed behind the inline table - EXPECT_NE(pInlineTable->InsertTable(0, "table4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {table4 = {}, table2 = {}, table1 = {}, table3 = {}})toml"); -} - -TEST(TOMLInsertNode, TestInsertArrayInInlineTable) -{ - // Insert an inline table - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pInlineTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - sdv::toml::npos, "inline_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). - GetInterface(); - ASSERT_NE(pInlineTable, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); - - // Insert arrays - EXPECT_NE(pInlineTable->InsertArray(sdv::toml::npos, "value_array1"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array1 = []})toml"); - EXPECT_NE(pInlineTable->InsertArray(sdv::toml::npos, "value_array2"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array1 = [], value_array2 = []})toml"); - EXPECT_NE(pInlineTable->InsertArray(0, "value_array3"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array3 = [], value_array1 = [], value_array2 = []})toml"); -} - -TEST(TOMLInsertNode, TestInsertTableArrayInInlineTable) -{ - // Insert an inline table - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pInlineTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - sdv::toml::npos, "inline_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). - GetInterface(); - ASSERT_NE(pInlineTable, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); - - // Insert standard table array - EXPECT_NE(pInlineTable->InsertTableArray(sdv::toml::npos, "table_array1", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array1 = [{}]})toml"); - - // Insert an inline table array before - EXPECT_NE(pInlineTable->InsertTableArray(0, "table_array2", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array2 = [{}], table_array1 = [{}]})toml"); - - // Insert an inline table array behind -> this will have to be printed before the standard table array - EXPECT_NE(pInlineTable->InsertTableArray(sdv::toml::npos, "table_array3", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); - - // Insert a standard table array in front -> this will have to be printed behind the inline table array - EXPECT_NE(pInlineTable->InsertTableArray(0, "table_array4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array4 = [{}], table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); - - // Add an additional table array entry for table array #4 - EXPECT_NE(pInlineTable->InsertTableArray(sdv::toml::npos, "table_array4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array4 = [{}, {}], table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); -} - -TEST(TOMLInsertNode, TestInsertValueInArray) -{ - // Insert an array - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pInlineArray = sdv::TInterfaceAccessPtr(parser.Root().InsertArray( - sdv::toml::npos, "inline_array")).GetInterface(); - ASSERT_NE(pInlineArray, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); - - // Insert the values into the table (with or without name) - EXPECT_NE(pInlineArray->InsertValue(sdv::toml::npos, "", 10), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [10])toml"); - EXPECT_NE(pInlineArray->InsertValue(sdv::toml::npos, "", u8"abc"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [10, "abc"])toml"); - EXPECT_NE(pInlineArray->InsertValue(0, "value_float", 123.456), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [123.456, 10, "abc"])toml"); -} - -TEST(TOMLInsertNode, TestInsertTableInArray) -{ - // Insert an array - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pInlineArray = sdv::TInterfaceAccessPtr(parser.Root().InsertArray( - sdv::toml::npos, "inline_array")).GetInterface(); - ASSERT_NE(pInlineArray, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); - - // Insert a standard table - sdv::toml::INodeCollectionInsert* pTableInsert = sdv::TInterfaceAccessPtr( - pInlineArray->InsertTable(sdv::toml::npos, "", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pTableInsert, nullptr); - pTableInsert->InsertValue(0, "a", 10); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [{a = 10}])toml"); - - // Insert an inline table before - pTableInsert = sdv::TInterfaceAccessPtr( - pInlineArray->InsertTable(0, "", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). - GetInterface(); - ASSERT_NE(pTableInsert, nullptr); - pTableInsert->InsertValue(0, "b", 20); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [{b = 20}, {a = 10}])toml"); - - // Insert an inline table behind - pTableInsert = sdv::TInterfaceAccessPtr( - pInlineArray->InsertTable(sdv::toml::npos, "", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)). - GetInterface(); - ASSERT_NE(pTableInsert, nullptr); - pTableInsert->InsertValue(0, "c", 30); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [{b = 20}, {a = 10}, {c = 30}])toml"); - - // Insert a standard table in front - pTableInsert = sdv::TInterfaceAccessPtr( - pInlineArray->InsertTable(0, "", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pTableInsert, nullptr); - pTableInsert->InsertValue(0, "d", 40); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [{d = 40}, {b = 20}, {a = 10}, {c = 30}])toml"); -} - -TEST(TOMLInsertNode, TestInsertArrayInArray) -{ - // Insert an array - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pInlineArray = sdv::TInterfaceAccessPtr(parser.Root().InsertArray( - sdv::toml::npos, "inline_array")).GetInterface(); - ASSERT_NE(pInlineArray, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); - - // Insert arrays - sdv::toml::INodeCollectionInsert* pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertArray(sdv::toml::npos, "")). - GetInterface(); - ASSERT_NE(pArrayInsert, nullptr); - pArrayInsert->InsertValue(0, "", 10); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [[10]])toml"); - pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertArray(sdv::toml::npos, "value_array2")). - GetInterface(); - ASSERT_NE(pArrayInsert, nullptr); - pArrayInsert->InsertValue(0, "", 20); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [[10], [20]])toml"); - pArrayInsert = - sdv::TInterfaceAccessPtr(pInlineArray->InsertArray(0, "value_array3")). - GetInterface(); - ASSERT_NE(pArrayInsert, nullptr); - pArrayInsert->InsertValue(0, "", 30); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [[30], [10], [20]])toml"); -} - -TEST(TOMLInsertNode, TestInsertTableArrayInArray) -{ - // Insert an array - toml_parser::CParser parser; - auto* pArray = parser.Root().InsertArray(sdv::toml::npos, "inline_array"); - ASSERT_NE(pArray, nullptr); - sdv::toml::INodeCollectionInsert* pInlineArray = - sdv::TInterfaceAccessPtr(pArray).GetInterface(); - ASSERT_NE(pInlineArray, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); - - // Insert standard table array - sdv::toml::INodeCollectionInsert* pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertTableArray(sdv::toml::npos, - "table_array1", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pArrayInsert, nullptr); - pArrayInsert->InsertValue(0, "a", 10); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [[{a = 10}]])toml"); - - // Insert an inline table array before - pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertTableArray(0, "table_array2", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)).GetInterface(); - ASSERT_NE(pArrayInsert, nullptr); - pArrayInsert->InsertValue(0, "b", 20); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [[{b = 20}], [{a = 10}]])toml"); - - // Insert an inline table array behind -> this will have to be printed before the standard table array - pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertTableArray(sdv::toml::npos, "table_array3", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline)).GetInterface(); - ASSERT_NE(pArrayInsert, nullptr); - pArrayInsert->InsertValue(0, "c", 30); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [[{b = 20}], [{a = 10}], [{c = 30}]])toml"); - - // Insert a standard table array in front -> this will have to be printed behind the inline table array - pArrayInsert = sdv::TInterfaceAccessPtr(pInlineArray->InsertTableArray(0, "table_array4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)).GetInterface(); - ASSERT_NE(pArrayInsert, nullptr); - pArrayInsert->InsertValue(0, "d", 40); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [[{d = 40}], [{b = 20}], [{a = 10}], [{c = 30}]])toml"); - - // Add an additional table array entry for table array #4 - sdv::toml::INodeCollection* pCollection = sdv::TInterfaceAccessPtr(pArray).GetInterface(); - ASSERT_NE(pCollection, nullptr); - sdv::toml::INodeCollectionInsert* pFirstNode = - sdv::TInterfaceAccessPtr(pCollection->GetNode(0)).GetInterface(); - ASSERT_NE(pFirstNode, nullptr); - sdv::toml::INodeCollectionInsert* pTableInsert = sdv::TInterfaceAccessPtr(pFirstNode->InsertTable(sdv::toml::npos, - "table_array4", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pTableInsert, nullptr); - pTableInsert->InsertValue(0, "e", 50); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(inline_array = [[{d = 40}, {e = 50}], [{b = 20}], [{a = 10}], [{c = 30}]])toml"); -} - -TEST(TOMLInsertNode, TestInsertValueInTableArray) -{ - // Insert a standard table array - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pTableArray = sdv::TInterfaceAccessPtr(parser.Root().InsertTableArray( - sdv::toml::npos, "table_array", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pTableArray, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); - - // Insert the values into the table - EXPECT_NE(pTableArray->InsertValue(sdv::toml::npos, "value_int", 10), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -value_int = 10)toml"); - EXPECT_NE(pTableArray->InsertValue(sdv::toml::npos, "value_str", u8"abc"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -value_int = 10 -value_str = "abc")toml"); - EXPECT_NE(pTableArray->InsertValue(0, "value_float", 123.456), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -value_float = 123.456 -value_int = 10 -value_str = "abc")toml"); -} - -TEST(TOMLInsertNode, TestInsertTableInTableArray) -{ - // Insert a standard table array - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pTableArray = sdv::TInterfaceAccessPtr(parser.Root().InsertTableArray( - sdv::toml::npos, "table_array", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pTableArray, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); - - // Insert a standard table - EXPECT_NE(pTableArray->InsertTable(sdv::toml::npos, "table1", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -[table_array.table1])toml"); - - // Insert an inline table before - EXPECT_NE(pTableArray->InsertTable(0, "table2", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -table2 = {} -[table_array.table1])toml"); - - // Insert an inline table behind -> this will have to be printed before the standard table - EXPECT_NE(pTableArray->InsertTable(sdv::toml::npos, "table3", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -table2 = {} -table3 = {} -[table_array.table1])toml"); - - // Insert a standard table in front -> this will have to be printed behind the inline table - EXPECT_NE(pTableArray->InsertTable(0, "table4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -table2 = {} -table3 = {} -[table_array.table4] -[table_array.table1])toml"); -} - -TEST(TOMLInsertNode, TestInsertArrayInTableArray) -{ - // Insert a standard table array - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pTableArray = sdv::TInterfaceAccessPtr(parser.Root().InsertTableArray( - sdv::toml::npos, "table_array", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pTableArray, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); - - // Insert arrays - EXPECT_NE(pTableArray->InsertArray(sdv::toml::npos, "value_array1"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -value_array1 = [])toml"); - EXPECT_NE(pTableArray->InsertArray(sdv::toml::npos, "value_array2"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -value_array1 = [] -value_array2 = [])toml"); - EXPECT_NE(pTableArray->InsertArray(0, "value_array3"), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -value_array3 = [] -value_array1 = [] -value_array2 = [])toml"); -} - -TEST(TOMLInsertNode, TestInsertTableArrayInTableArray) -{ - // Insert a standard table array - toml_parser::CParser parser; - sdv::toml::INodeCollectionInsert* pTableArray = sdv::TInterfaceAccessPtr(parser.Root().InsertTableArray( - sdv::toml::npos, "table_array", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - GetInterface(); - ASSERT_NE(pTableArray, nullptr); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); - - // Insert standard table array - EXPECT_NE(pTableArray->InsertTableArray(sdv::toml::npos, "table_array1", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -[[table_array.table_array1]])toml"); - - // Insert an inline table array before - EXPECT_NE(pTableArray->InsertTableArray(0, "table_array2", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -table_array2 = [{}] -[[table_array.table_array1]])toml"); - - // Insert an inline table array behind -> this will have to be printed before the standard table array - EXPECT_NE(pTableArray->InsertTableArray(sdv::toml::npos, "table_array3", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_inline), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -table_array2 = [{}] -table_array3 = [{}] -[[table_array.table_array1]])toml"); - - // Insert a standard table array in front -> this will have to be printed behind the inline table array - EXPECT_NE(pTableArray->InsertTableArray(0, "table_array4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -table_array2 = [{}] -table_array3 = [{}] -[[table_array.table_array4]] -[[table_array.table_array1]])toml"); - - // Add an additional table array entry for table array #4 - EXPECT_NE(pTableArray->InsertTableArray(sdv::toml::npos, "table_array4", - sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard), nullptr); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array]] -table_array2 = [{}] -table_array3 = [{}] -[[table_array.table_array4]] -[[table_array.table_array1]] -[[table_array.table_array4]])toml"); -} - -TEST(TOMLInsertNode, TestInsertValuesRootAsTOML) -{ - toml_parser::CParser parser; - EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(# This is the first value -value_int = 10)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(# This is the first value -value_int = 10)toml"); - EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(value_str = "abc" # This is the second value - -# Comment in between - -# And the third value -value_float = 123.456 - -# Some -# Final -# Words :-) -)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(# This is the first value -value_int = 10 -value_str = "abc" # This is the second value - -# Comment in between - -# And the third value -value_float = 123.456 - -# Some -# Final -# Words :-) -)toml"); -} - -TEST(TOMLInsertNode, TestInsertTableRootAsTOML) -{ - // Insert a standard table - toml_parser::CParser parser; - EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml([table1] # This is table 1 -a = 10 -b = 20.30)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([table1] # This is table 1 -a = 10 -b = 20.30)toml"); - - // Insert an inline table before - EXPECT_EQ(parser.Root().InsertTOML(0, R"toml(# This is table 2 -table2 = {c = 40, d = "50"})toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(# This is table 2 -table2 = {c = 40, d = "50"} -[table1] # This is table 1 -a = 10 -b = 20.30)toml"); - - // Insert an inline table behind -> this will have to be printed before the standard table - EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(table3 = {e = '60'} # This is table 3 -)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(# This is table 2 -table2 = {c = 40, d = "50"} -table3 = {e = '60'} # This is table 3 -[table1] # This is table 1 -a = 10 -b = 20.30)toml"); - - // Insert a standard table in front -> this will have to be printed behind the inline table - EXPECT_EQ(parser.Root().InsertTOML(0, R"toml( - -# And this is table 4 -[table4] -f = 70 -g = 80.90)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(# This is table 2 -table2 = {c = 40, d = "50"} -table3 = {e = '60'} # This is table 3 - - -# And this is table 4 -[table4] -f = 70 -g = 80.90 -[table1] # This is table 1 -a = 10 -b = 20.30)toml"); -} - -TEST(TOMLInsertNode, TestInsertArrayRootAsTOML) -{ - toml_parser::CParser parser; - EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(value_array1 = [10, 20, 30] # This is array 1)toml", true), - sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(value_array1 = [10, 20, 30] # This is array 1)toml"); - EXPECT_EQ(parser.Root().InsertTOML(0, R"toml(# This is array 2 -value_array2 = [ - 40, # This is an integer value - 50.60, # This is a float value - "70", # This is a string value - ])toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(# This is array 2 -value_array2 = [ - 40, # This is an integer value - 50.60, # This is a float value - "70", # This is a string value - ] -value_array1 = [10, 20, 30] # This is array 1)toml"); - EXPECT_EQ(parser.Root().InsertTOML(1, R"toml(# And finally array 3 -value_array3 = [])toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(# This is array 2 -value_array2 = [ - 40, # This is an integer value - 50.60, # This is a float value - "70", # This is a string value - ] -# And finally array 3 -value_array3 = [] -value_array1 = [10, 20, 30] # This is array 1)toml"); -} - -TEST(TOMLInsertNode, TestInsertTableArrayRootAsTOML) -{ - // Insert a standard table array - toml_parser::CParser parser; - EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml([[table_array1]] -a = 10 -b = 20)toml", true), - sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - std::string ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml([[table_array1]] -a = 10 -b = 20)toml"); - - // Insert an inline table array before - EXPECT_EQ(parser.Root().InsertTOML(0, R"toml(table_array2 = [{c = 30}, {d = 40}])toml", true), - sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] -[[table_array1]] -a = 10 -b = 20)toml"); - - // Insert an inline table array behind -> this will have to be printed before the standard table array - EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(table_array3 = -[ - { e = 50 } -])toml", true), - sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] -table_array3 = -[ - { e = 50 } -] -[[table_array1]] -a = 10 -b = 20)toml"); - - // Insert a standard table array in front -> this will have to be printed behind the inline table array - EXPECT_EQ(parser.Root().InsertTOML(0, R"toml([[table_array4]] -f = 60 -g = 70)toml", true), - sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] -table_array3 = -[ - { e = 50 } -] -[[table_array4]] -f = 60 -g = 70 -[[table_array1]] -a = 10 -b = 20)toml"); - - // Add an additional table array entry for table array #4 (even provided as inline, must be added as standard). - EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml(table_array4 = [{h = 80}])toml", true), - sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] -table_array3 = -[ - { e = 50 } -] -[[table_array4]] -f = 60 -g = 70 -[[table_array1]] -a = 10 -b = 20 -[[table_array4]] -h = 80)toml"); - - // Add an additional table array entry for table array #4 (even provided as inline, must be added as standard). - EXPECT_EQ(parser.Root().InsertTOML(sdv::toml::npos, R"toml([[table_array3]] -i = 90 -[[table_array3]] -j = 100 -)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - EXPECT_EQ(parser.Root().InsertTOML(0, R"toml([[table_array3]] -k = 110)toml", true), sdv::toml::INodeCollectionInsert::EInsertResult::insert_success); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] -table_array3 = -[{k = 110}, - { e = 50 }, -{i = 90}, {j = 100}] -[[table_array4]] -f = 60 -g = 70 -[[table_array1]] -a = 10 -b = 20 -[[table_array4]] -h = 80)toml"); - - parser.Root().AutomaticFormat(); - ssTOML = parser.GenerateTOML(); - EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] -table_array3 = [{k = 110}, {e = 50}, {i = 90}, {j = 100}] -[[table_array4]] -f = 60 -g = 70 -[[table_array1]] -a = 10 -b = 20 -[[table_array4]] -h = 80)toml"); -} - -TEST(TOMLInsertNode, DISABLED_TestInsertValueInStandardTableAsTOML) -{ - //// Insert a standard table - //toml_parser::CParser parser; - //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - // sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - // GetInterface(); - //ASSERT_NE(pStandardTable, nullptr); - //std::string ssTOML = parser.GenerateTOML(); - //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); -} - -TEST(TOMLInsertNode, DISABLED_TestInsertTableInStandardTableAsTOML) -{ - //// Insert a standard table - //toml_parser::CParser parser; - //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - // sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - // GetInterface(); - //ASSERT_NE(pStandardTable, nullptr); - //std::string ssTOML = parser.GenerateTOML(); - //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); -} - -TEST(TOMLInsertNode, DISABLED_TestInsertArrayInStandardTableAsTOML) -{ - //// Insert a standard table - //toml_parser::CParser parser; - //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - // sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - // GetInterface(); - //ASSERT_NE(pStandardTable, nullptr); - //std::string ssTOML = parser.GenerateTOML(); - //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); -} - -TEST(TOMLInsertNode, DISABLED_TestInsertTableArrayInStandardTableAsTOML) -{ - //// Insert a standard table - //toml_parser::CParser parser; - //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(parser.Root().InsertTable( - // sdv::toml::npos, "standard_table", sdv::toml::INodeCollectionInsert::EInsertPreference::prefer_standard)). - // GetInterface(); - //ASSERT_NE(pStandardTable, nullptr); - //std::string ssTOML = parser.GenerateTOML(); - //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); -} - -TEST(TOMLInsertNode, DISABLED_TestInsertValueInInlineTableAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertTableInInlineTableAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertArrayInInlineTableAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertTableArrayInInlineTableAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertValueInArrayAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertTableInArrayAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertArrayInArrayAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertTableArrayInArrayAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertValueInTableArrayAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertTableInTableArrayAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertArrayInTableArrayAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertTableArrayInTableArrayAsTOML) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertMixed) -{} - -TEST(TOMLInsertNode, DISABLED_TestInsertMixedWithDelete) -{} diff --git a/tests/unit_tests/toml_parser/generate_toml_switch_inline.cpp b/tests/unit_tests/toml_parser/generate_toml_switch_inline.cpp deleted file mode 100644 index 8f9344e..0000000 --- a/tests/unit_tests/toml_parser/generate_toml_switch_inline.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025-2026 ZF Friedrichshafen AG - * - * This program and the accompanying materials are made available under the - * terms of the Apache License Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Erik Verhoeven - initial API and implementation - ********************************************************************************/ - -#include - -#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" -#include "../../../sdv_services/core/toml_parser/parser_toml.h" - -// Test TODO: -// Switch to inline and vice versa - -TEST(GenerateTOML, SwitchTableToInline) -{ - toml_parser::CParser parser; - - std::string ssTOMLInput = R"code([table] -key1 = "some string" -key2 = 123)code"; - std::string ssTOMLOutput = R"code(table = {key1 = "some string", key2 = 123})code"; - - EXPECT_NO_THROW(parser.Process(ssTOMLInput)); - - std::string ssGenerated; - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML()); - EXPECT_EQ(ssGenerated, ssTOMLInput); - - sdv::TInterfaceAccessPtr ptrTable = parser.Root().GetNodeDirect("table"); - EXPECT_TRUE(ptrTable); - sdv::toml::INodeInfo* pInfo = ptrTable.GetInterface(); - sdv::toml::INodeCollectionConvert* pConvert = ptrTable.GetInterface(); - ASSERT_NE(pInfo, nullptr); - ASSERT_NE(pConvert, nullptr); - EXPECT_FALSE(pInfo->IsInline()); - EXPECT_TRUE(pConvert->CanMakeInline()); - EXPECT_TRUE(pConvert->MakeInline()); - EXPECT_TRUE(pInfo->IsInline()); - - EXPECT_NO_THROW(ssGenerated = parser.GenerateTOML()); - EXPECT_EQ(ssGenerated, ssTOMLOutput); -} diff --git a/tests/unit_tests/toml_parser/generate_toml_tests.cpp b/tests/unit_tests/toml_parser/generate_toml_tests.cpp index 55b59d3..c9c4f74 100644 --- a/tests/unit_tests/toml_parser/generate_toml_tests.cpp +++ b/tests/unit_tests/toml_parser/generate_toml_tests.cpp @@ -12,6 +12,7 @@ ********************************************************************************/ #include +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/parser_toml.h" #include "../../../sdv_services/core/toml_parser/parser_node_toml.h" @@ -19,7 +20,7 @@ TEST(GenerateTOML, Comment) { toml_parser::CParser parser; - std::string ssTOML = R"code(# This is a full-line comment)code"; + std::string ssTOML = R"toml(# This is a full-line comment)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -33,9 +34,9 @@ TEST(GenerateTOML, NodeComment) { toml_parser::CParser parser; - std::string ssTOML = R"code(# This is a full-line comment + std::string ssTOML = R"toml(# This is a full-line comment key = "value" # This is a comment at the end of a line -another = "# This is not a comment")code"; +another = "# This is not a comment")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -49,10 +50,10 @@ TEST(GenerateTOML, NodeCommentWithSpaces) { toml_parser::CParser parser; - std::string ssTOML = R"code( + std::string ssTOML = R"toml( # This is a full-line comment key = "value" # This is a comment at the end of a line -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -66,13 +67,13 @@ TEST(GenerateTOML, UnattachedComment) { toml_parser::CParser parser; - std::string ssTOML = R"code(# Comment not belonging to node + std::string ssTOML = R"toml(# Comment not belonging to node # This is a full-line comment key = "value" # This is a comment at the end of a line another = "# This is not a comment" -# Comment not belonging to node)code"; +# Comment not belonging to node)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -86,10 +87,10 @@ TEST(GenerateTOML, ArrayWhitespace) { toml_parser::CParser parser; - std::string ssTOML = R"code( + std::string ssTOML = R"toml( array = [ 1, 2, 3, 4, 5, 6 ] -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -103,7 +104,7 @@ TEST(GenerateTOML, ArrayComment) { toml_parser::CParser parser; - std::string ssTOML = R"code( + std::string ssTOML = R"toml( # Pre-array array = [ 1, # Value #1 2, # Value #2 @@ -112,7 +113,7 @@ array = [ 1, # Value #1 5, # Value #5 6, # Value #6 ] # Post-array -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -126,7 +127,7 @@ TEST(GenerateTOML, ArrayCommentWithSpace) { toml_parser::CParser parser; - std::string ssTOML = R"code( + std::string ssTOML = R"toml( # unattached comment @@ -159,7 +160,7 @@ array = [ 1, # Value #1 # unattached comment -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -174,9 +175,9 @@ TEST(GenerateTOML, InlineTableWhitespace) toml_parser::CParser parser; // Note: line-breaks within an inline table are not allowed. - std::string ssTOML = R"code( + std::string ssTOML = R"toml( table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -191,10 +192,10 @@ TEST(GenerateTOML, InlineTableComment) toml_parser::CParser parser; // Note: line-breaks within an inline table are not allowed. - std::string ssTOML = R"code( + std::string ssTOML = R"toml( # Pre-table table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } # Post-table -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -208,10 +209,10 @@ TEST(GenerateTOML, Keys) { toml_parser::CParser parser; - std::string ssTOML = R"code(key = "value" + std::string ssTOML = R"toml(key = "value" bare_key = "value" bare-key = "value" -1234 = "value")code"; +1234 = "value")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -225,11 +226,11 @@ TEST(GenerateTOML, QuotedKeys) { toml_parser::CParser parser; - std::string ssTOML = u8R"code("127.0.0.1" = "value" + std::string ssTOML = u8R"toml("127.0.0.1" = "value" "character encoding" = "value" "ʎǝʞ" = "value" 'key2' = "value" -'quoted "value"' = "value")code"; +'quoted "value"' = "value")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -241,8 +242,8 @@ TEST(GenerateTOML, QuotedKeys) TEST(GenerateTOML, BlankKeys) { - std::string ssTOML1 = R"code("" = "blank" # VALID but discouraged)code"; - std::string ssTOML2 = R"code('' = 'blank' # VALID but discouraged)code"; + std::string ssTOML1 = R"toml("" = "blank" # VALID but discouraged)toml"; + std::string ssTOML2 = R"toml('' = 'blank' # VALID but discouraged)toml"; toml_parser::CParser parser1, parser2; EXPECT_NO_THROW(parser1.Process(ssTOML1)); @@ -259,10 +260,10 @@ TEST(GenerateTOML, DottedKeys) { toml_parser::CParser parser; - std::string ssTOML = R"code(name = "Orange" + std::string ssTOML = R"toml(name = "Orange" physical.color = "orange" physical.shape = "round" -site."google.com" = true)code"; +site."google.com" = true)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -276,9 +277,9 @@ TEST(GenerateTOML, WhitespaceKeys) { toml_parser::CParser parser; - std::string ssTOML = R"code(fruit.name = "banana" # this is best practice + std::string ssTOML = R"toml(fruit.name = "banana" # this is best practice fruit. color = "yellow" # same as fruit.color -fruit . flavor = "banana" # same as fruit.flavor)code"; +fruit . flavor = "banana" # same as fruit.flavor)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -292,14 +293,14 @@ TEST(GenerateTOML, OutOfOrderKeys) { toml_parser::CParser parser; - std::string ssTOML = R"code(apple.type = "fruit" + std::string ssTOML = R"toml(apple.type = "fruit" orange.type = "fruit" apple.skin = "thin" orange.skin = "thick" apple.color = "red" -orange.color = "orange")code"; +orange.color = "orange")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -313,7 +314,7 @@ TEST(GenerateTOML, FloatLookingAlikeKeys) { toml_parser::CParser parser; - std::string ssTOML = R"code(3.14159 = "pi")code"; + std::string ssTOML = R"toml(3.14159 = "pi")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -327,7 +328,7 @@ TEST(GenerateTOML, BasicStrings) { toml_parser::CParser parser; - std::string ssTOML = R"code(str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.")code"; + std::string ssTOML = R"toml(str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -341,9 +342,9 @@ TEST(GenerateTOML, MultiLineStrings) { toml_parser::CParser parser; - std::string ssTOML = R"code(str1 = """ + std::string ssTOML = R"toml(str1 = """ Roses are red -Violets are blue""")code"; +Violets are blue""")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -357,7 +358,7 @@ TEST(GenerateTOML, LongMultiLineStrings) { toml_parser::CParser parser; - std::string ssTOML = R"code(str1 = "The quick brown fox jumps over the lazy dog." + std::string ssTOML = R"toml(str1 = "The quick brown fox jumps over the lazy dog." str2 = """ The quick brown \ @@ -370,7 +371,7 @@ str3 = """\ The quick brown \ fox jumps over \ the lazy dog.\ - """)code"; + """)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -384,13 +385,13 @@ TEST(GenerateTOML, QuotingStrings) { toml_parser::CParser parser; - std::string ssTOML = R"code(str4 = """Here are two quotation marks: "". Simple enough.""" + std::string ssTOML = R"toml(str4 = """Here are two quotation marks: "". Simple enough.""" # str5 = """Here are three quotation marks: """.""" # INVALID str5 = """Here are three quotation marks: ""\".""" str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" # "This," she said, "is just a pointless statement." -str7 = """"This," she said, "is just a pointless statement."""")code"; +str7 = """"This," she said, "is just a pointless statement."""")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -404,10 +405,10 @@ TEST(GenerateTOML, LiteralStrings) { toml_parser::CParser parser; - std::string ssTOML = R"code(winpath = 'C:\Users\nodejs\templates' + std::string ssTOML = R"toml(winpath = 'C:\Users\nodejs\templates' winpath2 = '\\ServerX\admin$\system32\' quoted = 'Tom "Dubs" Preston-Werner' -regex = '<\i\c*\s*>')code"; +regex = '<\i\c*\s*>')toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -421,13 +422,13 @@ TEST(GenerateTOML, MultiLineLiteralStrings) { toml_parser::CParser parser; - std::string ssTOML = R"code(regex2 = '''I [dw]on't need \d{2} apples''' + std::string ssTOML = R"toml(regex2 = '''I [dw]on't need \d{2} apples''' lines = ''' The first newline is trimmed in raw strings. All other whitespace is preserved. -''')code"; +''')toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -441,13 +442,13 @@ TEST(GenerateTOML, QuotedLiteralStrings) { toml_parser::CParser parser; - std::string ssTOML = R"code(quot15 = '''Here are fifteen quotation marks: """""""""""""""''' + std::string ssTOML = R"toml(quot15 = '''Here are fifteen quotation marks: """""""""""""""''' # apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID apos15 = "Here are fifteen apostrophes: '''''''''''''''" # 'That,' she said, 'is still pointless.' -str = ''''That,' she said, 'is still pointless.'''')code"; +str = ''''That,' she said, 'is still pointless.'''')toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -461,10 +462,10 @@ TEST(GenerateTOML, Integers) { toml_parser::CParser parser; - std::string ssTOML = R"code(int1 = +99 + std::string ssTOML = R"toml(int1 = +99 int2 = 42 int3 = 0 -int4 = -17)code"; +int4 = -17)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -478,10 +479,10 @@ TEST(GenerateTOML, ReadibleIntegers) { toml_parser::CParser parser; - std::string ssTOML = R"code(int5 = 1_000 + std::string ssTOML = R"toml(int5 = 1_000 int6 = 5_349_221 int7 = 53_49_221 # Indian number system grouping -int8 = 1_2_3_4_5 # VALID but discouraged)code"; +int8 = 1_2_3_4_5 # VALID but discouraged)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -495,7 +496,7 @@ TEST(GenerateTOML, OtherBaseIntegers) { toml_parser::CParser parser; - std::string ssTOML = R"code(# hexadecimal with prefix `0x` + std::string ssTOML = R"toml(# hexadecimal with prefix `0x` hex1 = 0xDEADBEEF hex2 = 0xdeadbeef hex3 = 0xdead_beef @@ -505,7 +506,7 @@ oct1 = 0o01234567 oct2 = 0o755 # useful for Unix file permissions # binary with prefix `0b` -bin1 = 0b11010110)code"; +bin1 = 0b11010110)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -519,7 +520,7 @@ TEST(GenerateTOML, FloatingPoints) { toml_parser::CParser parser; - std::string ssTOML = R"code(# fractional + std::string ssTOML = R"toml(# fractional flt1 = +1.0 flt2 = 3.1415 flt3 = -0.01 @@ -530,7 +531,7 @@ flt5 = 1e06 flt6 = -2E-2 # both -flt7 = 6.626e-34)code"; +flt7 = 6.626e-34)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -544,7 +545,7 @@ TEST(GenerateTOML, ReadibleFloatingPoints) { toml_parser::CParser parser; - std::string ssTOML = R"code(flt8 = 224_617.445_991_228)code"; + std::string ssTOML = R"toml(flt8 = 224_617.445_991_228)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -558,7 +559,7 @@ TEST(GenerateTOML, SpecialFloatingPoints) { toml_parser::CParser parser; - std::string ssTOML = R"code(# infinity + std::string ssTOML = R"toml(# infinity sf1 = inf # positive infinity sf2 = +inf # positive infinity sf3 = -inf # negative infinity @@ -566,7 +567,7 @@ sf3 = -inf # negative infinity # not a number sf4 = nan # actual sNaN/qNaN encoding is implementation-specific sf5 = +nan # same as `nan` -sf6 = -nan # valid, actual encoding is implementation-specific)code"; +sf6 = -nan # valid, actual encoding is implementation-specific)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -580,8 +581,8 @@ TEST(GenerateTOML, Booleans) { toml_parser::CParser parser; - std::string ssTOML = R"code(bool1 = true -bool2 = false)code"; + std::string ssTOML = R"toml(bool1 = true +bool2 = false)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -595,9 +596,9 @@ TEST(GenerateTOML, DISABLED_OffsetDateTimes) { toml_parser::CParser parser; - std::string ssTOML = R"code(odt1 = 1979-05-27T07:32:00Z + std::string ssTOML = R"toml(odt1 = 1979-05-27T07:32:00Z odt2 = 1979-05-27T00:32:00-07:00 -odt3 = 1979-05-27T00:32:00.999999-07:00)code"; +odt3 = 1979-05-27T00:32:00.999999-07:00)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -611,7 +612,7 @@ TEST(GenerateTOML, DISABLED_ReadibleOffsetDateTimes) { toml_parser::CParser parser; - std::string ssTOML = R"code(odt4 = 1979-05-27 07:32:00Z)code"; + std::string ssTOML = R"toml(odt4 = 1979-05-27 07:32:00Z)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -625,8 +626,8 @@ TEST(GenerateTOML, DISABLED_LocalDateTimes) { toml_parser::CParser parser; - std::string ssTOML = R"code(ldt1 = 1979-05-27T07:32:00 -ldt2 = 1979-05-27T00:32:00.999999)code"; + std::string ssTOML = R"toml(ldt1 = 1979-05-27T07:32:00 +ldt2 = 1979-05-27T00:32:00.999999)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -640,7 +641,7 @@ TEST(GenerateTOML, DISABLED_LocalDates) { toml_parser::CParser parser; - std::string ssTOML = R"code(ld1 = 1979-05-27)code"; + std::string ssTOML = R"toml(ld1 = 1979-05-27)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -654,8 +655,8 @@ TEST(GenerateTOML, DISABLED_LocalTimes) { toml_parser::CParser parser; - std::string ssTOML = R"code(lt1 = 07:32:00 -lt2 = 00:32:00.999999)code"; + std::string ssTOML = R"toml(lt1 = 07:32:00 +lt2 = 00:32:00.999999)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -669,7 +670,7 @@ TEST(GenerateTOML, Arrays) { toml_parser::CParser parser; - std::string ssTOML = R"code(integers = [ 1, 2, 3 ] + std::string ssTOML = R"toml(integers = [ 1, 2, 3 ] colors = [ "red", "yellow", "green" ] nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] @@ -681,7 +682,7 @@ contributors = [ "Foo Bar ", { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } - ])code"; + ])toml"; parser.Process(ssTOML); EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -696,14 +697,14 @@ TEST(GenerateTOML, MultiLineArrays) { toml_parser::CParser parser; - std::string ssTOML = R"code(integers2 = [ + std::string ssTOML = R"toml(integers2 = [ 1, 2, 3 ] integers3 = [ 1, 2, # this is ok -])code"; +])toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -717,7 +718,7 @@ TEST(GenerateTOML, Tables) { toml_parser::CParser parser; - std::string ssTOML = R"code([table] + std::string ssTOML = R"toml([table] [table-1] key1 = "some string" @@ -725,7 +726,7 @@ key2 = 123 [table-2] key1 = "another string" -key2 = 456)code"; +key2 = 456)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -739,8 +740,8 @@ TEST(GenerateTOML, QuotedKeyTables) { toml_parser::CParser parser; - std::string ssTOML = R"code([dog."tater.man"] -type.name = "pug")code"; + std::string ssTOML = R"toml([dog."tater.man"] +type.name = "pug")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -754,14 +755,14 @@ TEST(GenerateTOML, WhitespaceKeyTables) { toml_parser::CParser parser; - std::string ssTOML = u8R"code([a.b.c] # this is best practice + std::string ssTOML = u8R"toml([a.b.c] # this is best practice x = 1 [ d.e.f ] # same as [d.e.f] y = 1 [ g . h . i ] # same as [g.h.i] z = 1 [ j . "ʞ" . 'l' ] # same as [j."ʞ".'l'] -a = 1)code"; +a = 1)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -775,13 +776,13 @@ TEST(GenerateTOML, MixedOrderTables) { toml_parser::CParser parser; - std::string ssTOML = R"code(# VALID BUT DISCOURAGED + std::string ssTOML = R"toml(# VALID BUT DISCOURAGED [fruit.apple] a = 1 [animal] b = 2 [fruit.orange] -aa = 11)code"; +aa = 11)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -795,14 +796,14 @@ TEST(GenerateTOML, MixedValueAndTables) { toml_parser::CParser parser; - std::string ssTOML = R"code(# Top-level table begins. + std::string ssTOML = R"toml(# Top-level table begins. name = "Fido" breed = "pug" # Top-level table ends. [owner] name = "Regina Dogman" -member_since = 1999)code"; +member_since = 1999)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -816,10 +817,10 @@ TEST(GenerateTOML, AutomaticTables) { toml_parser::CParser parser; - std::string ssTOML = R"code(fruit.apple.color = "red" -fruit.apple.taste.sweet = true)code"; - std::string ssTOMLOutput2 = R"code(apple.color = "red" -apple.taste.sweet = true)code"; + std::string ssTOML = R"toml(fruit.apple.color = "red" +fruit.apple.taste.sweet = true)toml"; + std::string ssTOMLOutput2 = R"toml(apple.color = "red" +apple.taste.sweet = true)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -835,7 +836,7 @@ TEST(GenerateTOML, MixedAutomaticTables) { toml_parser::CParser parser; - std::string ssTOML = R"code([fruit] + std::string ssTOML = R"toml([fruit] apple.color = "red" apple.taste.sweet = true @@ -843,7 +844,7 @@ apple.taste.sweet = true # [fruit.apple.taste] # INVALID [fruit.apple.texture] # you can add sub-tables -smooth = true)code"; +smooth = true)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -857,9 +858,9 @@ TEST(GenerateTOML, InlineTables) { toml_parser::CParser parser; - std::string ssTOML = R"code(name = { first = "Tom", last = "Preston-Werner" } + std::string ssTOML = R"toml(name = { first = "Tom", last = "Preston-Werner" } point = { x = 1, y = 2 } -animal = { type.name = "pug" })code"; +animal = { type.name = "pug" })toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -873,9 +874,9 @@ TEST(GenerateTOML, EmbeddedInlineTables) { toml_parser::CParser parser; - std::string ssTOML = R"code(test=[{ first = "Tom", last = "Preston-Werner" }, + std::string ssTOML = R"toml(test=[{ first = "Tom", last = "Preston-Werner" }, { x = 1, y = 2 }, -{ type.name = "pug" }])code"; +{ type.name = "pug" }])toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -889,7 +890,7 @@ TEST(GenerateTOML, TableArrays) { toml_parser::CParser parser; - std::string ssTOML = R"code([[products]] + std::string ssTOML = R"toml([[products]] name = "Hammer" sku = 738594937 @@ -899,7 +900,7 @@ sku = 738594937 name = "Nail" sku = 284758393 -color = "gray")code"; +color = "gray")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -913,7 +914,7 @@ TEST(GenerateTOML, MixedTableAndTableArrays) { toml_parser::CParser parser; - std::string ssTOML = R"code([[fruits]] + std::string ssTOML = R"toml([[fruits]] name = "apple" [fruits.physical] # subtable @@ -931,29 +932,29 @@ name = "granny smith" name = "banana" [[fruits.varieties]] -name = "plantain")code"; - std::string ssTOMLFruits1Physical = R"code([physical] # subtable +name = "plantain")toml"; + std::string ssTOMLFruits1Physical = R"toml([physical] # subtable color = "red" shape = "round" -)code"; - std::string ssTOMLFruits1Varieties = R"code([[varieties]] # nested array of tables +)toml"; + std::string ssTOMLFruits1Varieties = R"toml([[varieties]] # nested array of tables name = "red delicious" [[varieties]] name = "granny smith" -)code"; - std::string ssTOMLFruits1Variety1 = R"code([variety] # nested array of tables +)toml"; + std::string ssTOMLFruits1Variety1 = R"toml([variety] # nested array of tables name = "red delicious" -)code"; - std::string ssTOMLFruits1Variety2 = R"code([variety] +)toml"; + std::string ssTOMLFruits1Variety2 = R"toml([variety] name = "granny smith" -)code"; - std::string ssTOMLFruits2Variety1 = R"code([variety] -name = "plantain")code"; +)toml"; + std::string ssTOMLFruits2Variety1 = R"toml([variety] +name = "plantain")toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -998,9 +999,9 @@ TEST(GenerateTOML, InlineTableArrays) { toml_parser::CParser parser; - std::string ssTOML = R"code(points = [ { x = 1, y = 2, z = 3 }, + std::string ssTOML = R"toml(points = [ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, - { x = 2, y = 4, z = 8 } ])code"; + { x = 2, y = 4, z = 8 } ])toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); diff --git a/tests/unit_tests/toml_parser/generate_toml_with_transfer.cpp b/tests/unit_tests/toml_parser/generate_toml_with_transfer.cpp index 74ccb0f..6901b36 100644 --- a/tests/unit_tests/toml_parser/generate_toml_with_transfer.cpp +++ b/tests/unit_tests/toml_parser/generate_toml_with_transfer.cpp @@ -12,6 +12,7 @@ ********************************************************************************/ #include +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/parser_toml.h" #include "../../../sdv_services/core/toml_parser/parser_node_toml.h" @@ -19,13 +20,13 @@ TEST(GenerateTOML, TransferNodeComment) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# This is a full-line comment + std::string ssTOMLInput = R"toml(# This is a full-line comment key = "value" # This is a comment at the end of a line -another = "# This is not a comment")code"; - std::string ssTOMLOutput = R"code([tree.branch] +another = "# This is not a comment")toml"; + std::string ssTOMLOutput = R"toml([tree.branch] # This is a full-line comment key = "value" # This is a comment at the end of a line -another = "# This is not a comment")code"; +another = "# This is not a comment")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -39,15 +40,15 @@ TEST(GenerateTOML, TransferNodeCommentWithSpaces) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code( + std::string ssTOMLInput = R"toml( # This is a full-line comment key = "value" # This is a comment at the end of a line -)code"; - std::string ssTOMLOutput = R"code([tree.branch] +)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] # This is a full-line comment key = "value" # This is a comment at the end of a line -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -61,21 +62,21 @@ TEST(GenerateTOML, TransferUnattachedComment) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# Comment not belonging to node + std::string ssTOMLInput = R"toml(# Comment not belonging to node # This is a full-line comment key = "value" # This is a comment at the end of a line another = "# This is not a comment" -# Comment not belonging to node)code"; - std::string ssTOMLOutput = R"code([tree.branch] +# Comment not belonging to node)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] # Comment not belonging to node # This is a full-line comment key = "value" # This is a comment at the end of a line another = "# This is not a comment" -# Comment not belonging to node)code"; +# Comment not belonging to node)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -89,15 +90,15 @@ TEST(GenerateTOML, TransferArrayWhitespace) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code( + std::string ssTOMLInput = R"toml( array = [ 1, 2, 3, 4, 5, 6 ] -)code"; - std::string ssTOMLOutput = R"code([tree.branch] +)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] array = [ 1, 2, 3, 4, 5, 6 ] -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -111,7 +112,7 @@ TEST(GenerateTOML, TransferArrayComment) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code( + std::string ssTOMLInput = R"toml( # Pre-array array = [ 1, # Value #1 2, # Value #2 @@ -120,8 +121,8 @@ array = [ 1, # Value #1 5, # Value #5 6, # Value #6 ] # Post-array -)code"; - std::string ssTOMLOutput = R"code([tree.branch] +)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] # Pre-array array = [ 1, # Value #1 @@ -131,7 +132,7 @@ array = [ 1, # Value #1 5, # Value #5 6, # Value #6 ] # Post-array -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -145,7 +146,7 @@ TEST(GenerateTOML, TransferArrayCommentWithSpace) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code( + std::string ssTOMLInput = R"toml( # unattached comment @@ -178,8 +179,8 @@ array = [ 1, # Value #1 # unattached comment -)code"; - std::string ssTOMLOutput = R"code([tree.branch] +)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] # unattached comment @@ -213,7 +214,7 @@ array = [ 1, # Value #1 # unattached comment -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -228,13 +229,13 @@ TEST(GenerateTOML, TransferInlineTableWhitespace) toml_parser::CParser parser; // NOTE: Line-breaks within inline tables are not allowed. - std::string ssTOMLInput = R"code( + std::string ssTOMLInput = R"toml( table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } -)code"; - std::string ssTOMLOutput = R"code([tree.branch] +)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -249,15 +250,15 @@ TEST(GenerateTOML, TransferInlineTableComment) toml_parser::CParser parser; // NOTE: Line-breaks within inline tables are not allowed. - std::string ssTOMLInput = R"code( + std::string ssTOMLInput = R"toml( # Pre-table table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } # Post-table -)code"; - std::string ssTOMLOutput = R"code([tree.branch] +)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] # Pre-table table = { a = 1, b = 2, d = 3, e = 4, f = 5, g = 6 } # Post-table -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -271,15 +272,15 @@ TEST(GenerateTOML, TransferKeys) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(key = "value" + std::string ssTOMLInput = R"toml(key = "value" bare_key = "value" bare-key = "value" -1234 = "value")code"; - std::string ssTOMLOutput = R"code([tree.branch] +1234 = "value")toml"; + std::string ssTOMLOutput = R"toml([tree.branch] key = "value" bare_key = "value" bare-key = "value" -1234 = "value")code"; +1234 = "value")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -293,17 +294,17 @@ TEST(GenerateTOML, TransferQuotedKeys) { toml_parser::CParser parser; - std::string ssTOMLInput = u8R"code("127.0.0.1" = "value" + std::string ssTOMLInput = u8R"toml("127.0.0.1" = "value" "character encoding" = "value" "ʎǝʞ" = "value" 'key2' = "value" -'quoted "value"' = "value")code"; - std::string ssTOMLOutput = u8R"code([tree.branch] +'quoted "value"' = "value")toml"; + std::string ssTOMLOutput = u8R"toml([tree.branch] "127.0.0.1" = "value" "character encoding" = "value" "ʎǝʞ" = "value" 'key2' = "value" -'quoted "value"' = "value")code"; +'quoted "value"' = "value")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -315,12 +316,12 @@ TEST(GenerateTOML, TransferQuotedKeys) TEST(GenerateTOML, TransferBlankKeys) { - std::string ssTOMLInput1 = R"code("" = "blank" # VALID but discouraged)code"; - std::string ssTOMLInput2 = R"code('' = 'blank' # VALID but discouraged)code"; - std::string ssTOMLOutput1 = R"code([tree.branch] -"" = "blank" # VALID but discouraged)code"; - std::string ssTOMLOutput2 = R"code([tree.branch] -'' = 'blank' # VALID but discouraged)code"; + std::string ssTOMLInput1 = R"toml("" = "blank" # VALID but discouraged)toml"; + std::string ssTOMLInput2 = R"toml('' = 'blank' # VALID but discouraged)toml"; + std::string ssTOMLOutput1 = R"toml([tree.branch] +"" = "blank" # VALID but discouraged)toml"; + std::string ssTOMLOutput2 = R"toml([tree.branch] +'' = 'blank' # VALID but discouraged)toml"; toml_parser::CParser parser1, parser2; EXPECT_NO_THROW(parser1.Process(ssTOMLInput1)); @@ -338,15 +339,15 @@ TEST(GenerateTOML, TransferDottedKeys) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(name = "Orange" + std::string ssTOMLInput = R"toml(name = "Orange" physical.color = "orange" physical.shape = "round" -site."google.com" = true)code"; - std::string ssTOMLOutput = R"code([tree.branch] +site."google.com" = true)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] name = "Orange" physical.color = "orange" physical.shape = "round" -site."google.com" = true)code"; +site."google.com" = true)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -360,13 +361,13 @@ TEST(GenerateTOML, TransferWhitespaceKeys) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(fruit.name = "banana" # this is best practice + std::string ssTOMLInput = R"toml(fruit.name = "banana" # this is best practice fruit. color = "yellow" # same as fruit.color -fruit . flavor = "banana" # same as fruit.flavor)code"; - std::string ssTOMLOutput = R"code([tree.branch] +fruit . flavor = "banana" # same as fruit.flavor)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] fruit.name = "banana" # this is best practice fruit. color = "yellow" # same as fruit.color -fruit . flavor = "banana" # same as fruit.flavor)code"; +fruit . flavor = "banana" # same as fruit.flavor)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -380,15 +381,15 @@ TEST(GenerateTOML, TransferOutOfOrderKeys) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(apple.type = "fruit" + std::string ssTOMLInput = R"toml(apple.type = "fruit" orange.type = "fruit" apple.skin = "thin" orange.skin = "thick" apple.color = "red" -orange.color = "orange")code"; - std::string ssTOMLOutput = R"code([tree.branch] +orange.color = "orange")toml"; + std::string ssTOMLOutput = R"toml([tree.branch] apple.type = "fruit" orange.type = "fruit" @@ -396,7 +397,7 @@ apple.skin = "thin" orange.skin = "thick" apple.color = "red" -orange.color = "orange")code"; +orange.color = "orange")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -410,9 +411,9 @@ TEST(GenerateTOML, TransferFloatLookingAlikeKeys) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(3.14159 = "pi")code"; - std::string ssTOMLOutput = R"code([tree.branch] -3.14159 = "pi")code"; + std::string ssTOMLInput = R"toml(3.14159 = "pi")toml"; + std::string ssTOMLOutput = R"toml([tree.branch] +3.14159 = "pi")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -426,9 +427,9 @@ TEST(GenerateTOML, TransferBasicStrings) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.")code"; - std::string ssTOMLOutput = R"code([tree.branch] -str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.")code"; + std::string ssTOMLInput = R"toml(str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.")toml"; + std::string ssTOMLOutput = R"toml([tree.branch] +str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -442,13 +443,13 @@ TEST(GenerateTOML, TransferMultiLineStrings) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(str1 = """ + std::string ssTOMLInput = R"toml(str1 = """ Roses are red -Violets are blue""")code"; - std::string ssTOMLOutput = R"code([tree.branch] +Violets are blue""")toml"; + std::string ssTOMLOutput = R"toml([tree.branch] str1 = """ Roses are red -Violets are blue""")code"; +Violets are blue""")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -462,7 +463,7 @@ TEST(GenerateTOML, TransferLongMultiLineStrings) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(str1 = "The quick brown fox jumps over the lazy dog." + std::string ssTOMLInput = R"toml(str1 = "The quick brown fox jumps over the lazy dog." str2 = """ The quick brown \ @@ -475,8 +476,8 @@ str3 = """\ The quick brown \ fox jumps over \ the lazy dog.\ - """)code"; - std::string ssTOMLOutput = R"code([tree.branch] + """)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] str1 = "The quick brown fox jumps over the lazy dog." str2 = """ @@ -490,7 +491,7 @@ str3 = """\ The quick brown \ fox jumps over \ the lazy dog.\ - """)code"; + """)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -504,21 +505,21 @@ TEST(GenerateTOML, TransferQuotingStrings) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(str4 = """Here are two quotation marks: "". Simple enough.""" + std::string ssTOMLInput = R"toml(str4 = """Here are two quotation marks: "". Simple enough.""" # str5 = """Here are three quotation marks: """.""" # INVALID str5 = """Here are three quotation marks: ""\".""" str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" # "This," she said, "is just a pointless statement." -str7 = """"This," she said, "is just a pointless statement."""")code"; - std::string ssTOMLOutput = R"code([tree.branch] +str7 = """"This," she said, "is just a pointless statement."""")toml"; + std::string ssTOMLOutput = R"toml([tree.branch] str4 = """Here are two quotation marks: "". Simple enough.""" # str5 = """Here are three quotation marks: """.""" # INVALID str5 = """Here are three quotation marks: ""\".""" str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" # "This," she said, "is just a pointless statement." -str7 = """"This," she said, "is just a pointless statement."""")code"; +str7 = """"This," she said, "is just a pointless statement."""")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -532,15 +533,15 @@ TEST(GenerateTOML, TransferLiteralStrings) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(winpath = 'C:\Users\nodejs\templates' + std::string ssTOMLInput = R"toml(winpath = 'C:\Users\nodejs\templates' winpath2 = '\\ServerX\admin$\system32\' quoted = 'Tom "Dubs" Preston-Werner' -regex = '<\i\c*\s*>')code"; - std::string ssTOMLOutput = R"code([tree.branch] +regex = '<\i\c*\s*>')toml"; + std::string ssTOMLOutput = R"toml([tree.branch] winpath = 'C:\Users\nodejs\templates' winpath2 = '\\ServerX\admin$\system32\' quoted = 'Tom "Dubs" Preston-Werner' -regex = '<\i\c*\s*>')code"; +regex = '<\i\c*\s*>')toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -554,21 +555,21 @@ TEST(GenerateTOML, TransferMultiLineLiteralStrings) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(regex2 = '''I [dw]on't need \d{2} apples''' + std::string ssTOMLInput = R"toml(regex2 = '''I [dw]on't need \d{2} apples''' lines = ''' The first newline is trimmed in raw strings. All other whitespace is preserved. -''')code"; - std::string ssTOMLOutput = R"code([tree.branch] +''')toml"; + std::string ssTOMLOutput = R"toml([tree.branch] regex2 = '''I [dw]on't need \d{2} apples''' lines = ''' The first newline is trimmed in raw strings. All other whitespace is preserved. -''')code"; +''')toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -582,21 +583,21 @@ TEST(GenerateTOML, TransferQuotedLiteralStrings) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(quot15 = '''Here are fifteen quotation marks: """""""""""""""''' + std::string ssTOMLInput = R"toml(quot15 = '''Here are fifteen quotation marks: """""""""""""""''' # apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID apos15 = "Here are fifteen apostrophes: '''''''''''''''" # 'That,' she said, 'is still pointless.' -str = ''''That,' she said, 'is still pointless.'''')code"; - std::string ssTOMLOutput = R"code([tree.branch] +str = ''''That,' she said, 'is still pointless.'''')toml"; + std::string ssTOMLOutput = R"toml([tree.branch] quot15 = '''Here are fifteen quotation marks: """""""""""""""''' # apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID apos15 = "Here are fifteen apostrophes: '''''''''''''''" # 'That,' she said, 'is still pointless.' -str = ''''That,' she said, 'is still pointless.'''')code"; +str = ''''That,' she said, 'is still pointless.'''')toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -610,15 +611,15 @@ TEST(GenerateTOML, TransferIntegers) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(int1 = +99 + std::string ssTOMLInput = R"toml(int1 = +99 int2 = 42 int3 = 0 -int4 = -17)code"; - std::string ssTOMLOutput = R"code([tree.branch] +int4 = -17)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] int1 = +99 int2 = 42 int3 = 0 -int4 = -17)code"; +int4 = -17)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -632,15 +633,15 @@ TEST(GenerateTOML, TransferReadibleIntegers) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(int5 = 1_000 + std::string ssTOMLInput = R"toml(int5 = 1_000 int6 = 5_349_221 int7 = 53_49_221 # Indian number system grouping -int8 = 1_2_3_4_5 # VALID but discouraged)code"; - std::string ssTOMLOutput = R"code([tree.branch] +int8 = 1_2_3_4_5 # VALID but discouraged)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] int5 = 1_000 int6 = 5_349_221 int7 = 53_49_221 # Indian number system grouping -int8 = 1_2_3_4_5 # VALID but discouraged)code"; +int8 = 1_2_3_4_5 # VALID but discouraged)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -654,7 +655,7 @@ TEST(GenerateTOML, TransferOtherBaseIntegers) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# hexadecimal with prefix `0x` + std::string ssTOMLInput = R"toml(# hexadecimal with prefix `0x` hex1 = 0xDEADBEEF hex2 = 0xdeadbeef hex3 = 0xdead_beef @@ -664,8 +665,8 @@ oct1 = 0o01234567 oct2 = 0o755 # useful for Unix file permissions # binary with prefix `0b` -bin1 = 0b11010110)code"; - std::string ssTOMLOutput = R"code([tree.branch] +bin1 = 0b11010110)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] # hexadecimal with prefix `0x` hex1 = 0xDEADBEEF hex2 = 0xdeadbeef @@ -676,7 +677,7 @@ oct1 = 0o01234567 oct2 = 0o755 # useful for Unix file permissions # binary with prefix `0b` -bin1 = 0b11010110)code"; +bin1 = 0b11010110)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -690,7 +691,7 @@ TEST(GenerateTOML, TransferFloatingPoints) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# fractional + std::string ssTOMLInput = R"toml(# fractional flt1 = +1.0 flt2 = 3.1415 flt3 = -0.01 @@ -701,8 +702,8 @@ flt5 = 1e06 flt6 = -2E-2 # both -flt7 = 6.626e-34)code"; - std::string ssTOMLOutput = R"code([tree.branch] +flt7 = 6.626e-34)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] # fractional flt1 = +1.0 flt2 = 3.1415 @@ -714,7 +715,7 @@ flt5 = 1e06 flt6 = -2E-2 # both -flt7 = 6.626e-34)code"; +flt7 = 6.626e-34)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -728,9 +729,9 @@ TEST(GenerateTOML, TransferReadibleFloatingPoints) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(flt8 = 224_617.445_991_228)code"; - std::string ssTOMLOutput = R"code([tree.branch] -flt8 = 224_617.445_991_228)code"; + std::string ssTOMLInput = R"toml(flt8 = 224_617.445_991_228)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] +flt8 = 224_617.445_991_228)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -744,7 +745,7 @@ TEST(GenerateTOML, TransferSpecialFloatingPoints) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# infinity + std::string ssTOMLInput = R"toml(# infinity sf1 = inf # positive infinity sf2 = +inf # positive infinity sf3 = -inf # negative infinity @@ -752,8 +753,8 @@ sf3 = -inf # negative infinity # not a number sf4 = nan # actual sNaN/qNaN encoding is implementation-specific sf5 = +nan # same as `nan` -sf6 = -nan # valid, actual encoding is implementation-specific)code"; - std::string ssTOMLOutput = R"code([tree.branch] +sf6 = -nan # valid, actual encoding is implementation-specific)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] # infinity sf1 = inf # positive infinity sf2 = +inf # positive infinity @@ -762,7 +763,7 @@ sf3 = -inf # negative infinity # not a number sf4 = nan # actual sNaN/qNaN encoding is implementation-specific sf5 = +nan # same as `nan` -sf6 = -nan # valid, actual encoding is implementation-specific)code"; +sf6 = -nan # valid, actual encoding is implementation-specific)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -776,11 +777,11 @@ TEST(GenerateTOML, TransferBooleans) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(bool1 = true -bool2 = false)code"; - std::string ssTOMLOutput = R"code([tree.branch] + std::string ssTOMLInput = R"toml(bool1 = true +bool2 = false)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] bool1 = true -bool2 = false)code"; +bool2 = false)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -794,13 +795,13 @@ TEST(GenerateTOML, DISABLED_TransferOffsetDateTimes) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(odt1 = 1979-05-27T07:32:00Z + std::string ssTOMLInput = R"toml(odt1 = 1979-05-27T07:32:00Z odt2 = 1979-05-27T00:32:00-07:00 -odt3 = 1979-05-27T00:32:00.999999-07:00)code"; - std::string ssTOMLOutput = R"code([tree.branch] +odt3 = 1979-05-27T00:32:00.999999-07:00)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] odt1 = 1979-05-27T07:32:00Z odt2 = 1979-05-27T00:32:00-07:00 -odt3 = 1979-05-27T00:32:00.999999-07:00)code"; +odt3 = 1979-05-27T00:32:00.999999-07:00)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -814,9 +815,9 @@ TEST(GenerateTOML, DISABLED_TransferReadibleOffsetDateTimes) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(odt4 = 1979-05-27 07:32:00Z)code"; - std::string ssTOMLOutput = R"code([tree.branch] -odt4 = 1979-05-27 07:32:00Z)code"; + std::string ssTOMLInput = R"toml(odt4 = 1979-05-27 07:32:00Z)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] +odt4 = 1979-05-27 07:32:00Z)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -830,11 +831,11 @@ TEST(GenerateTOML, DISABLED_TransferLocalDateTimes) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(ldt1 = 1979-05-27T07:32:00 -ldt2 = 1979-05-27T00:32:00.999999)code"; - std::string ssTOMLOutput = R"code([tree.branch] + std::string ssTOMLInput = R"toml(ldt1 = 1979-05-27T07:32:00 +ldt2 = 1979-05-27T00:32:00.999999)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] ldt1 = 1979-05-27T07:32:00 -ldt2 = 1979-05-27T00:32:00.999999)code"; +ldt2 = 1979-05-27T00:32:00.999999)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -848,9 +849,9 @@ TEST(GenerateTOML, DISABLED_TransferLocalDates) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(ld1 = 1979-05-27)code"; - std::string ssTOMLOutput = R"code([tree.branch] -ld1 = 1979-05-27)code"; + std::string ssTOMLInput = R"toml(ld1 = 1979-05-27)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] +ld1 = 1979-05-27)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -864,11 +865,11 @@ TEST(GenerateTOML, DISABLED_TransferLocalTimes) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(lt1 = 07:32:00 -lt2 = 00:32:00.999999)code"; - std::string ssTOMLOutput = R"code([tree.branch] + std::string ssTOMLInput = R"toml(lt1 = 07:32:00 +lt2 = 00:32:00.999999)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] lt1 = 07:32:00 -lt2 = 00:32:00.999999)code"; +lt2 = 00:32:00.999999)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -882,7 +883,7 @@ TEST(GenerateTOML, TransferArrays) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(integers = [ 1, 2, 3 ] + std::string ssTOMLInput = R"toml(integers = [ 1, 2, 3 ] colors = [ "red", "yellow", "green" ] nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] @@ -893,8 +894,8 @@ numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] contributors = [ "Foo Bar ", { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } -])code"; - std::string ssTOMLOutput = R"code([tree.branch] +])toml"; + std::string ssTOMLOutput = R"toml([tree.branch] integers = [ 1, 2, 3 ] colors = [ "red", "yellow", "green" ] nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] @@ -906,7 +907,7 @@ numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] contributors = [ "Foo Bar ", { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } -])code"; +])toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -920,15 +921,15 @@ TEST(GenerateTOML, TransferMultiLineArrays) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(integers2 = [ + std::string ssTOMLInput = R"toml(integers2 = [ 1, 2, 3 ] integers3 = [ 1, 2, # this is ok -])code"; - std::string ssTOMLOutput = R"code([tree.branch] +])toml"; + std::string ssTOMLOutput = R"toml([tree.branch] integers2 = [ 1, 2, 3 ] @@ -936,7 +937,7 @@ integers2 = [ integers3 = [ 1, 2, # this is ok -])code"; +])toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -950,7 +951,7 @@ TEST(GenerateTOML, TransferTables) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code([table] + std::string ssTOMLInput = R"toml([table] [table-1] key1 = "some string" @@ -958,8 +959,8 @@ key2 = 123 [table-2] key1 = "another string" -key2 = 456)code"; - std::string ssTOMLOutput = R"code([tree.branch.table] +key2 = 456)toml"; + std::string ssTOMLOutput = R"toml([tree.branch.table] [tree.branch.table-1] key1 = "some string" @@ -967,7 +968,7 @@ key2 = 123 [tree.branch.table-2] key1 = "another string" -key2 = 456)code"; +key2 = 456)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -981,10 +982,10 @@ TEST(GenerateTOML, TransferQuotedKeyTables) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code([dog."tater.man"] -type.name = "pug")code"; - std::string ssTOMLOutput = R"code([tree.branch.dog."tater.man"] -type.name = "pug")code"; + std::string ssTOMLInput = R"toml([dog."tater.man"] +type.name = "pug")toml"; + std::string ssTOMLOutput = R"toml([tree.branch.dog."tater.man"] +type.name = "pug")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -998,22 +999,22 @@ TEST(GenerateTOML, TransferWhitespaceKeyTables) { toml_parser::CParser parser; - std::string ssTOMLInput = u8R"code([a.b.c] # this is best practice + std::string ssTOMLInput = u8R"toml([a.b.c] # this is best practice x = 1 [ d.e.f ] # same as [d.e.f] y = 1 [ g . h . i ] # same as [g.h.i] z = 1 [ j . "ʞ" . 'l' ] # same as [j."ʞ".'l'] -a = 1)code"; - std::string ssTOMLOutput = u8R"code([tree.branch.a.b.c] # this is best practice +a = 1)toml"; + std::string ssTOMLOutput = u8R"toml([tree.branch.a.b.c] # this is best practice x = 1 [tree.branch. d.e.f ] # same as [d.e.f] y = 1 [tree.branch. g . h . i ] # same as [g.h.i] z = 1 [tree.branch. j . "ʞ" . 'l' ] # same as [j."ʞ".'l'] -a = 1)code"; +a = 1)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -1027,20 +1028,20 @@ TEST(GenerateTOML, TransferMixedOrderTables) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# VALID BUT DISCOURAGED + std::string ssTOMLInput = R"toml(# VALID BUT DISCOURAGED [fruit.apple] a = 1 [animal] b = 2 [fruit.orange] -aa = 11)code"; - std::string ssTOMLOutput = R"code(# VALID BUT DISCOURAGED +aa = 11)toml"; + std::string ssTOMLOutput = R"toml(# VALID BUT DISCOURAGED [tree.branch.fruit.apple] a = 1 [tree.branch.animal] b = 2 [tree.branch.fruit.orange] -aa = 11)code"; +aa = 11)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -1054,15 +1055,15 @@ TEST(GenerateTOML, TransferMixedValueAndTables) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# Top-level table begins. + std::string ssTOMLInput = R"toml(# Top-level table begins. name = "Fido" breed = "pug" # Top-level table ends. [owner] name = "Regina Dogman" -member_since = 1999)code"; - std::string ssTOMLOutput = R"code([tree.branch] +member_since = 1999)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] # Top-level table begins. name = "Fido" breed = "pug" @@ -1070,7 +1071,7 @@ breed = "pug" # Top-level table ends. [tree.branch.owner] name = "Regina Dogman" -member_since = 1999)code"; +member_since = 1999)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -1084,14 +1085,14 @@ TEST(GenerateTOML, TransferAutomaticTables) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(fruit.apple.color = "red" -fruit.apple.taste.sweet = true)code"; - std::string ssTOMLOutput = R"code([tree.branch] + std::string ssTOMLInput = R"toml(fruit.apple.color = "red" +fruit.apple.taste.sweet = true)toml"; + std::string ssTOMLOutput = R"toml([tree.branch] fruit.apple.color = "red" -fruit.apple.taste.sweet = true)code"; - std::string ssTOMLOutput2 = R"code([tree.branch] +fruit.apple.taste.sweet = true)toml"; + std::string ssTOMLOutput2 = R"toml([tree.branch] apple.color = "red" -apple.taste.sweet = true)code"; +apple.taste.sweet = true)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -1107,7 +1108,7 @@ TEST(GenerateTOML, TransferMixedAutomaticTables) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code([fruit] + std::string ssTOMLInput = R"toml([fruit] apple.color = "red" apple.taste.sweet = true @@ -1115,8 +1116,8 @@ apple.taste.sweet = true # [fruit.apple.taste] # INVALID [fruit.apple.texture] # you can add sub-tables -smooth = true)code"; - std::string ssTOMLOutput = R"code([tree.branch.fruit] +smooth = true)toml"; + std::string ssTOMLOutput = R"toml([tree.branch.fruit] apple.color = "red" apple.taste.sweet = true @@ -1124,7 +1125,7 @@ apple.taste.sweet = true # [fruit.apple.taste] # INVALID [tree.branch.fruit.apple.texture] # you can add sub-tables -smooth = true)code"; +smooth = true)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -1138,13 +1139,13 @@ TEST(GenerateTOML, TransferInlineTables) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(name = { first = "Tom", last = "Preston-Werner" } + std::string ssTOMLInput = R"toml(name = { first = "Tom", last = "Preston-Werner" } point = { x = 1, y = 2 } -animal = { type.name = "pug" })code"; - std::string ssTOMLOutput = R"code([tree.branch] +animal = { type.name = "pug" })toml"; + std::string ssTOMLOutput = R"toml([tree.branch] name = { first = "Tom", last = "Preston-Werner" } point = { x = 1, y = 2 } -animal = { type.name = "pug" })code"; +animal = { type.name = "pug" })toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); std::string ssGenerated; @@ -1157,13 +1158,13 @@ TEST(GenerateTOML, TransferEmbeddedInlineTables) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(test=[{ first = "Tom", last = "Preston-Werner" }, + std::string ssTOMLInput = R"toml(test=[{ first = "Tom", last = "Preston-Werner" }, { x = 1, y = 2 }, -{ type.name = "pug" }])code"; - std::string ssTOMLOutput = R"code([tree.branch] +{ type.name = "pug" }])toml"; + std::string ssTOMLOutput = R"toml([tree.branch] test=[{ first = "Tom", last = "Preston-Werner" }, { x = 1, y = 2 }, -{ type.name = "pug" }])code"; +{ type.name = "pug" }])toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -1177,7 +1178,7 @@ TEST(GenerateTOML, TransferTableArrays) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code([[products]] + std::string ssTOMLInput = R"toml([[products]] name = "Hammer" sku = 738594937 @@ -1187,8 +1188,8 @@ sku = 738594937 name = "Nail" sku = 284758393 -color = "gray")code"; - std::string ssTOMLOutput = R"code([[tree.branch.products]] +color = "gray")toml"; + std::string ssTOMLOutput = R"toml([[tree.branch.products]] name = "Hammer" sku = 738594937 @@ -1198,7 +1199,7 @@ sku = 738594937 name = "Nail" sku = 284758393 -color = "gray")code"; +color = "gray")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -1212,7 +1213,7 @@ TEST(GenerateTOML, TransferMixedTableAndTableArrays) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code([[fruits]] + std::string ssTOMLInput = R"toml([[fruits]] name = "apple" [fruits.physical] # subtable @@ -1230,8 +1231,8 @@ name = "granny smith" name = "banana" [[fruits.varieties]] -name = "plantain")code"; - std::string ssTOMLOutput = R"code([[tree.branch.fruits]] +name = "plantain")toml"; + std::string ssTOMLOutput = R"toml([[tree.branch.fruits]] name = "apple" [tree.branch.fruits.physical] # subtable @@ -1249,7 +1250,7 @@ name = "granny smith" name = "banana" [[tree.branch.fruits.varieties]] -name = "plantain")code"; +name = "plantain")toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -1263,13 +1264,13 @@ TEST(GenerateTOML, TransferInlineTableArrays) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(points = [ { x = 1, y = 2, z = 3 }, + std::string ssTOMLInput = R"toml(points = [ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, - { x = 2, y = 4, z = 8 } ])code"; - std::string ssTOMLOutput = R"code([tree.branch] + { x = 2, y = 4, z = 8 } ])toml"; + std::string ssTOMLOutput = R"toml([tree.branch] points = [ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, - { x = 2, y = 4, z = 8 } ])code"; + { x = 2, y = 4, z = 8 } ])toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); std::string ssGenerated; diff --git a/tests/unit_tests/toml_parser/generate_toml_getset_comment.cpp b/tests/unit_tests/toml_parser/getset_comment.cpp similarity index 83% rename from tests/unit_tests/toml_parser/generate_toml_getset_comment.cpp rename to tests/unit_tests/toml_parser/getset_comment.cpp index 8597a17..eb432b9 100644 --- a/tests/unit_tests/toml_parser/generate_toml_getset_comment.cpp +++ b/tests/unit_tests/toml_parser/getset_comment.cpp @@ -12,15 +12,15 @@ ********************************************************************************/ #include - +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/parser_node_toml.h" #include "../../../sdv_services/core/toml_parser/parser_toml.h" -TEST(GenerateTOML, GetCommentRoot) +TEST(GetSetComment, GetCommentRoot) { toml_parser::CParser parser; - std::string ssTOML = R"code(# This is a comment)code"; + std::string ssTOML = R"toml(# This is a comment)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -34,17 +34,17 @@ TEST(GenerateTOML, GetCommentRoot) EXPECT_EQ(ssComment, "This is a comment"); } -TEST(GenerateTOML, GetMultiLineCommentRoot) +TEST(GetSetComment, GetMultiLineCommentRoot) { toml_parser::CParser parser; - std::string ssTOML = R"code( + std::string ssTOML = R"toml( # This is a comment that stretches # more than one line. # And here's another comment of more # than one line. -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); @@ -55,23 +55,23 @@ TEST(GenerateTOML, GetMultiLineCommentRoot) ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before); EXPECT_TRUE(ssComment.empty()); ssComment = parser.Root().GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind); - EXPECT_EQ(ssComment, R"code(This is a comment that stretches more than one line. -And here's another comment of more than one line.)code"); + EXPECT_EQ(ssComment, R"toml(This is a comment that stretches more than one line. +And here's another comment of more than one line.)toml"); } -TEST(GenerateTOML, SetCommentRoot) +TEST(GetSetComment, SetCommentRoot) { toml_parser::CParser parser; // This will test the SetComment function and overwriting the existing comment. - std::string ssTOMLInput = R"code(# This is a double line comment -# This is line two of the double line comment)code"; - std::string ssTOMLOutput = R"code(# This is comment way before + std::string ssTOMLInput = R"toml(# This is a double line comment +# This is line two of the double line comment)toml"; + std::string ssTOMLOutput = R"toml(# This is comment way before # This is comment before # This is comment way behind -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -93,13 +93,13 @@ TEST(GenerateTOML, SetCommentRoot) EXPECT_EQ(ssGenerated, ssTOMLOutput); } -TEST(GenerateTOML, SetMultiLineCommentRoot) +TEST(GetSetComment, SetMultiLineCommentRoot) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# This is a double line comment -# This is line two of the double line comment)code"; - std::string ssTOMLOutput = R"code(# This is comment way before + std::string ssTOMLInput = R"toml(# This is a double line comment +# This is line two of the double line comment)toml"; + std::string ssTOMLOutput = R"toml(# This is comment way before # And surprise, also a second line @@ -109,7 +109,7 @@ TEST(GenerateTOML, SetMultiLineCommentRoot) # This is comment way behind # And final, also a second line -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -139,13 +139,13 @@ And final, also a second line)"); EXPECT_EQ(ssGenerated, ssTOMLOutput); } -TEST(GenerateTOML, RemoveCommentRoot) +TEST(GetSetComment, RemoveCommentRoot) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# This is a double line comment -# This is line two of the double line comment)code"; - std::string ssTOMLOutput = R"code()code"; + std::string ssTOMLInput = R"toml(# This is a double line comment +# This is line two of the double line comment)toml"; + std::string ssTOMLOutput = R"toml()toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -155,16 +155,16 @@ TEST(GenerateTOML, RemoveCommentRoot) EXPECT_EQ(ssGenerated, ssTOMLOutput); } -TEST(GenerateTOML, GetCommentRootValue) +TEST(GetSetComment, GetCommentRootValue) { toml_parser::CParser parser; - std::string ssTOML = R"code(# This is a separate comment + std::string ssTOML = R"toml(# This is a separate comment # This is a comment before the value value = "this is the value text" # Comment following the value -# This is also a separate comment)code"; +# This is also a separate comment)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); sdv::TInterfaceAccessPtr ptrValue = parser.Root().GetNodeDirect("value"); @@ -182,11 +182,11 @@ value = "this is the value text" # Comment following the value EXPECT_EQ(ssComment, "This is also a separate comment"); } -TEST(GenerateTOML, GetMultiLineCommentRootValue) +TEST(GetSetComment, GetMultiLineCommentRootValue) { toml_parser::CParser parser; - std::string ssTOML = R"code( + std::string ssTOML = R"toml( # This is a separate comment with several line-breaks before. @@ -209,7 +209,7 @@ value = "this is the value text" # Comment following the value. # This is also a separate comment. # Followed by this text on the same line. -# And another text on a separate line.)code"; +# And another text on a separate line.)toml"; EXPECT_NO_THROW(parser.Process(ssTOML)); sdv::TInterfaceAccessPtr ptrValue = parser.Root().GetNodeDirect("value"); @@ -218,32 +218,32 @@ value = "this is the value text" # Comment following the value. ASSERT_NE(pComment, nullptr); std::string ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::comment_before); - EXPECT_EQ(ssComment, R"code(This is a comment before the value. And another comment before the value at the same line. -Note: there was a space after the empty comment line. But that should not influence the comment lines.)code"); + EXPECT_EQ(ssComment, R"toml(This is a comment before the value. And another comment before the value at the same line. +Note: there was a space after the empty comment line. But that should not influence the comment lines.)toml"); ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::comment_behind); - EXPECT_EQ(ssComment, R"code(Comment following the value. More comment following the value. This becomes one line. -But this is a separate line.)code"); + EXPECT_EQ(ssComment, R"toml(Comment following the value. More comment following the value. This becomes one line. +But this is a separate line.)toml"); ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_before); - EXPECT_EQ(ssComment, R"code(This is a separate comment with several line-breaks before. Followed by this text on the same line. -Note: there was a space after the empty comment line. And another separate comment on a next line.)code"); + EXPECT_EQ(ssComment, R"toml(This is a separate comment with several line-breaks before. Followed by this text on the same line. +Note: there was a space after the empty comment line. And another separate comment on a next line.)toml"); ssComment = pComment->GetComment(sdv::toml::INodeInfo::ECommentType::out_of_scope_comment_behind); - EXPECT_EQ(ssComment, R"code(This is also a separate comment. Followed by this text on the same line. -And another text on a separate line.)code"); + EXPECT_EQ(ssComment, R"toml(This is also a separate comment. Followed by this text on the same line. +And another text on a separate line.)toml"); } -TEST(GenerateTOML, SetCommentRootValue) +TEST(GetSetComment, SetCommentRootValue) { toml_parser::CParser parser; // This will test the SetComment function and overwriting the existing comment. - std::string ssTOMLInput = R"code(# This is a double line comment -# This is line two of the double line comment)code"; - std::string ssTOMLOutput = R"code(# This is comment #3 + std::string ssTOMLInput = R"toml(# This is a double line comment +# This is line two of the double line comment)toml"; + std::string ssTOMLOutput = R"toml(# This is comment #3 # This is comment #1 # This is comment #4 -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -265,13 +265,13 @@ TEST(GenerateTOML, SetCommentRootValue) EXPECT_EQ(ssGenerated, ssTOMLOutput); } -TEST(GenerateTOML, SetMultiLineCommentRootValue) +TEST(GetSetComment, SetMultiLineCommentRootValue) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# This is a double line comment -# This is line two of the double line comment)code"; - std::string ssTOMLOutput = R"code(# This is comment #3 + std::string ssTOMLInput = R"toml(# This is a double line comment +# This is line two of the double line comment)toml"; + std::string ssTOMLOutput = R"toml(# This is comment #3 # And surprise, also a second line @@ -281,7 +281,7 @@ TEST(GenerateTOML, SetMultiLineCommentRootValue) # This is comment #4 # And final, also a second line -)code"; +)toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); @@ -311,13 +311,13 @@ And final, also a second line)"); EXPECT_EQ(ssGenerated, ssTOMLOutput); } -TEST(GenerateTOML, RemoveCommentRootValue) +TEST(GetSetComment, RemoveCommentRootValue) { toml_parser::CParser parser; - std::string ssTOMLInput = R"code(# This is a double line comment -# This is line two of the double line comment)code"; - std::string ssTOMLOutput = R"code()code"; + std::string ssTOMLInput = R"toml(# This is a double line comment +# This is line two of the double line comment)toml"; + std::string ssTOMLOutput = R"toml()toml"; EXPECT_NO_THROW(parser.Process(ssTOMLInput)); diff --git a/tests/unit_tests/toml_parser/indexer_tests.cpp b/tests/unit_tests/toml_parser/indexer_tests.cpp new file mode 100644 index 0000000..6639f4c --- /dev/null +++ b/tests/unit_tests/toml_parser/indexer_tests.cpp @@ -0,0 +1,119 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Martin Stimpfl - initial API and implementation + * Erik Verhoeven - writing TOML and whitespace preservation + ********************************************************************************/ + +#include +#include "../../../global/localmemmgr.h" +#include "../../../sdv_services/core/toml_parser/parser_node_indexer.h" + +TEST(IndexerTest, InitializeSingle) +{ + toml_parser::CNodeIndexer indexer; + + EXPECT_NO_THROW(toml_parser::CNodeIndex index = indexer.CreateIndex()); +} + +TEST(IndexerTest, CompareOrder) +{ + toml_parser::CNodeIndexer indexer; + auto index1 = indexer.CreateIndex(); + auto index2 = indexer.CreateIndex(); + auto index3 = indexer.CreateIndex(); + EXPECT_GT(index2, index1); + EXPECT_GT(index3, index1); + EXPECT_GT(index3, index2); +} + +TEST(IndexerTest, SwitchOrder) +{ + toml_parser::CNodeIndexer indexer; + auto index1 = indexer.CreateIndex(); + auto index2 = indexer.CreateIndex(); + auto index3 = indexer.CreateIndex(); + index3.MoveBefore(index1); + EXPECT_GT(index2, index1); + EXPECT_LT(index3, index1); + EXPECT_LT(index3, index2); +} + +TEST(IndexerTest, SwitchOrderMultipleIndexer) +{ + toml_parser::CNodeIndexer indexer1; + toml_parser::CNodeIndexer indexer2; + toml_parser::CNodeIndexer indexer3; + auto index1 = indexer1.CreateIndex(); + auto index2 = indexer2.CreateIndex(); + auto index3 = indexer3.CreateIndex(); + EXPECT_GT(index2, index1); + EXPECT_GT(index3, index1); + EXPECT_GT(index3, index2); + index3.MoveBefore(index1); + EXPECT_GT(index2, index1); + EXPECT_LT(index3, index1); + EXPECT_LT(index3, index2); + index2.MoveBefore(index3); + EXPECT_LT(index2, index1); + EXPECT_LT(index3, index1); + EXPECT_GT(index3, index2); +} + +TEST(IndexerTest, IndexLifetime) +{ + toml_parser::CNodeIndexer indexer; + + auto index1 = indexer.CreateIndex(); + size_t nCurrentCnt = indexer.Count(); + { + auto index2 = indexer.CreateIndex(); + EXPECT_GT(index2, index1); + EXPECT_EQ(indexer.Count(), nCurrentCnt + 1); + } + EXPECT_EQ(indexer.Count(), nCurrentCnt); + auto index3 = indexer.CreateIndex(); + EXPECT_GT(index3, index1); +} + +TEST(IndexerTest, CopyIndex) +{ + toml_parser::CNodeIndexer indexer; + + auto index1 = indexer.CreateIndex(); + auto index2 = indexer.CreateIndex(); + auto index2b = index2; + EXPECT_TRUE(index2b); + EXPECT_TRUE(index2); + EXPECT_GT(index2b, index1); + EXPECT_GT(index2, index1); + EXPECT_EQ(index2, index2b); + auto index3 = indexer.CreateIndex(); + EXPECT_GT(index3, index2); + EXPECT_GT(index3, index1); +} + +TEST(IndexerTest, MoveIndex) +{ + toml_parser::CNodeIndexer indexer; + + auto index1 = indexer.CreateIndex(); + auto index2 = indexer.CreateIndex(); + auto index2b = std::move(index2); + EXPECT_TRUE(index2b); + EXPECT_FALSE(index2); + EXPECT_GT(index2b, index1); + EXPECT_GT(index2, index1); // index 2 is not initialized any more. + EXPECT_GT(index2, index2b); + auto index3 = indexer.CreateIndex(); + EXPECT_GT(index3, index2b); + EXPECT_GT(index3, index1); + EXPECT_GT(index2, index3); +} diff --git a/tests/unit_tests/toml_parser/insert_node.cpp b/tests/unit_tests/toml_parser/insert_node.cpp new file mode 100644 index 0000000..a0d9682 --- /dev/null +++ b/tests/unit_tests/toml_parser/insert_node.cpp @@ -0,0 +1,990 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include "../../../global/localmemmgr.h" +#include "../../../sdv_services/core/toml_parser/parser_node_toml.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" +#include + +// Test TODO: +// - Inserted and straight away deleted +// - Inserted with false/deleted reference --error +// - Inserted values before (okay) and behind (error) tables +// - Inserted duplicate value -- error +// - Smart insert (comments/whitespace around) +// Insert as TOML, but only partly correct. + +TEST(InsertNode, InsertValuesRoot) +{ + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertValue("", "value_int", 10)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_int = 10)toml"); + EXPECT_TRUE(root.InsertValue("", "value_str", u8"abc")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_int = 10 +value_str = "abc")toml"); + EXPECT_NE(parser.Root().InsertValue(root.GetNodeNameByIndex(0), "value_float", 123.456), nullptr); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_float = 123.456 +value_int = 10 +value_str = "abc")toml"); +} + +TEST(InsertNode, InsertTableRoot) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTable("", "table1", false)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([table1])toml"); + + // Insert an inline table before + EXPECT_TRUE(root.InsertTable(root.GetNodeNameByIndex(0), "table2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table2 = {} +[table1])toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_TRUE(root.InsertTable("", "table3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table2 = {} +table3 = {} +[table1])toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_TRUE(root.InsertTable(root.GetNodeNameByIndex(0), "table4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table2 = {} +table3 = {} +[table4] +[table1])toml"); +} + +TEST(InsertNode, InsertArrayRoot) +{ + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertArray("", "value_array1")); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_array1 = [])toml"); + EXPECT_TRUE(root.InsertArray("", "value_array2")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_array1 = [] +value_array2 = [])toml"); + EXPECT_TRUE(root.InsertArray(root.GetNodeNameByIndex(0), "value_array3")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_array3 = [] +value_array1 = [] +value_array2 = [])toml"); +} + +TEST(InsertNode, InsertTableArrayRoot) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_TRUE(root.InsertTableArray("", "table_array1", false)); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array1]])toml"); + + // Insert an inline table array before + EXPECT_TRUE(root.InsertTableArray(root.GetNodeNameByIndex(0), "table_array2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] +[[table_array1]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_TRUE(root.InsertTableArray("", "table_array3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] +table_array3 = [{}] +[[table_array1]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_TRUE(root.InsertTableArray(root.GetNodeNameByIndex(0), "table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] +table_array3 = [{}] +[[table_array4]] +[[table_array1]])toml"); + + // Add an additional table array entry for table array #4 + EXPECT_TRUE(root.InsertTableArray("", "table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{}] +table_array3 = [{}] +[[table_array4]] +[[table_array1]] +[[table_array4]])toml"); +} + +TEST(InsertNode, InsertValueInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection tableStandard = root.InsertTable("", "standard_table", false); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert the values into the table + EXPECT_TRUE(tableStandard.InsertValue("", "value_int", 10)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_int = 10)toml"); + EXPECT_TRUE(tableStandard.InsertValue("", "value_str", u8"abc")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_int = 10 +value_str = "abc")toml"); + EXPECT_TRUE(tableStandard.InsertValue(tableStandard.GetNodeNameByIndex(0), "value_float", 123.456)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_float = 123.456 +value_int = 10 +value_str = "abc")toml"); +} + +TEST(InsertNode, InsertTableInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection tableStandard = root.InsertTable("", "standard_table", false); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert a standard table + EXPECT_TRUE(tableStandard.InsertTable("", "table1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table.table1])toml"); + + // Insert an inline table before + EXPECT_TRUE(tableStandard.InsertTable(tableStandard.GetNodeNameByIndex(0), "table2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table2 = {} +[standard_table.table1])toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_TRUE(tableStandard.InsertTable("", "table3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table2 = {} +table3 = {} +[standard_table.table1])toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table relative to the elements of the + // standard_table. + EXPECT_TRUE(tableStandard.InsertTable(tableStandard.GetNodeNameByIndex(0), "table4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table2 = {} +table3 = {} +[standard_table.table4] +[standard_table.table1])toml"); +} + +TEST(InsertNode, InsertArrayInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection tableStandard = root.InsertTable("", "standard_table", false); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert arrays + EXPECT_TRUE(tableStandard.InsertArray("", "value_array1")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_array1 = [])toml"); + EXPECT_TRUE(tableStandard.InsertArray("", "value_array2")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_array1 = [] +value_array2 = [])toml"); + EXPECT_TRUE(tableStandard.InsertArray(tableStandard.GetNodeNameByIndex(0), "value_array3")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +value_array3 = [] +value_array1 = [] +value_array2 = [])toml"); +} + +TEST(InsertNode, InsertTableArrayInStandardTable) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection tableStandard = root.InsertTable("", "standard_table", false); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); + + // Insert standard table array + EXPECT_TRUE(tableStandard.InsertTableArray("", "table_array1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[standard_table.table_array1]])toml"); + + // Insert an inline table array before + EXPECT_TRUE(tableStandard.InsertTableArray(tableStandard.GetNodeNameByIndex(0), "table_array2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +[[standard_table.table_array1]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_TRUE(tableStandard.InsertTableArray("", "table_array3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +table_array3 = [{}] +[[standard_table.table_array1]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array relative to the elements + // of the standard_table. + EXPECT_TRUE(tableStandard.InsertTableArray(tableStandard.GetNodeNameByIndex(0), "table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +table_array3 = [{}] +[[standard_table.table_array4]] +[[standard_table.table_array1]])toml"); + + // Add an additional table array entry for table array #4 + EXPECT_TRUE(tableStandard.InsertTableArray("", "table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([standard_table] +table_array2 = [{}] +table_array3 = [{}] +[[standard_table.table_array4]] +[[standard_table.table_array1]] +[[standard_table.table_array4]])toml"); +} + +TEST(InsertNode, InsertValueInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection tableInline = root.InsertTable("", "inline_table", true); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert the values into the table + EXPECT_TRUE(tableInline.InsertValue("", "value_int", 10)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_int = 10})toml"); + EXPECT_TRUE(tableInline.InsertValue("", "value_str", u8"abc")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_int = 10, value_str = "abc"})toml"); + EXPECT_TRUE(tableInline.InsertValue(tableInline.GetNodeNameByIndex(0), "value_float", 123.456)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_float = 123.456, value_int = 10, value_str = "abc"})toml"); +} + +TEST(InsertNode, InsertTableInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection tableInline = root.InsertTable("", "inline_table", true); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert a standard table + EXPECT_TRUE(tableInline.InsertTable("", "table1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table1 = {}})toml"); + + // Insert an inline table before + EXPECT_TRUE(tableInline.InsertTable(tableInline.GetNodeNameByIndex(0), "table2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table2 = {}, table1 = {}})toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_TRUE(tableInline.InsertTable("", "table3", + true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table2 = {}, table1 = {}, table3 = {}})toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_TRUE(tableInline.InsertTable(tableInline.GetNodeNameByIndex(0), "table4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table4 = {}, table2 = {}, table1 = {}, table3 = {}})toml"); +} + +TEST(InsertNode, InsertArrayInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection tableInline = root.InsertTable("", "inline_table", true); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert arrays + EXPECT_TRUE(tableInline.InsertArray("", "value_array1")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array1 = []})toml"); + EXPECT_TRUE(tableInline.InsertArray("", "value_array2")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array1 = [], value_array2 = []})toml"); + EXPECT_TRUE(tableInline.InsertArray(tableInline.GetNodeNameByIndex(0), "value_array3")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {value_array3 = [], value_array1 = [], value_array2 = []})toml"); +} + +TEST(InsertNode, InsertTableArrayInInlineTable) +{ + // Insert an inline table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection tableInline = root.InsertTable("", "inline_table", true); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {})toml"); + + // Insert standard table array + EXPECT_TRUE(tableInline.InsertTableArray("", "table_array1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array1 = [{}]})toml"); + + // Insert an inline table array before + EXPECT_TRUE(tableInline.InsertTableArray(tableInline.GetNodeNameByIndex(0), "table_array2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array2 = [{}], table_array1 = [{}]})toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_TRUE(tableInline.InsertTableArray("", "table_array3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_table = {table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_TRUE(tableInline.InsertTableArray(tableInline.GetNodeNameByIndex(0), "table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, + R"toml(inline_table = {table_array4 = [{}], table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); + + // Add an additional table array entry for table array #4 + EXPECT_TRUE(tableInline.InsertTableArray("", "table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, + R"toml(inline_table = {table_array4 = [{}, {}], table_array2 = [{}], table_array1 = [{}], table_array3 = [{}]})toml"); +} + +TEST(InsertNode, InsertValueInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection arrayInline = root.InsertArray("", "inline_array"); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert the values into the table (with or without name) + EXPECT_TRUE(arrayInline.InsertValue("", "", 10)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [10])toml"); + EXPECT_TRUE(arrayInline.InsertValue("", "", u8"abc")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [10, "abc"])toml"); + EXPECT_TRUE(arrayInline.InsertValue(arrayInline.GetNodeNameByIndex(0), "", 123.456)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [123.456, 10, "abc"])toml"); +} + +TEST(InsertNode, InsertTableInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection arrayInline = root.InsertArray("", "inline_array"); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert a standard table + sdv::toml::CNodeCollection table = arrayInline.InsertTable("", "", false); + EXPECT_TRUE(table); + table.InsertValue("", "a", 10); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{a = 10}])toml"); + + // Insert an inline table before + table = arrayInline.InsertTable(arrayInline.GetNodeNameByIndex(0), "", true); + EXPECT_TRUE(table); + table.InsertValue("", "b", 20); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{b = 20}, {a = 10}])toml"); + + // Insert an inline table behind + table = arrayInline.InsertTable("", "", true); + EXPECT_TRUE(table); + table.InsertValue("", "c", 30); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{b = 20}, {a = 10}, {c = 30}])toml"); + + // Insert a standard table in front + table = arrayInline.InsertTable(arrayInline.GetNodeNameByIndex(0), "", false); + EXPECT_TRUE(table); + table.InsertValue("", "d", 40); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [{d = 40}, {b = 20}, {a = 10}, {c = 30}])toml"); +} + +TEST(InsertNode, InsertArrayInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection arrayInline = root.InsertArray("", "inline_array"); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert arrays + sdv::toml::CNodeCollection array = arrayInline.InsertArray("", ""); + EXPECT_TRUE(array); + array.InsertValue("", "", 10); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[10]])toml"); + array = arrayInline.InsertArray("", ""); + EXPECT_TRUE(array); + array.InsertValue("", "", 20); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[10], [20]])toml"); + array = arrayInline.InsertArray(arrayInline.GetNodeNameByIndex(0), ""); + EXPECT_TRUE(array); + array.InsertValue("", "", 30); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[30], [10], [20]])toml"); +} + +TEST(InsertNode, InsertTableArrayInArray) +{ + // Insert an array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection arrayInline = root.InsertArray("", "inline_array"); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [])toml"); + + // Insert standard table array + sdv::toml::CNodeCollection table = arrayInline.InsertTableArray("", "", false); + EXPECT_TRUE(table); + table.InsertValue("", "a", 10); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{a = 10}]])toml"); + + // Insert an inline table array before + table = arrayInline.InsertTableArray(arrayInline.GetNodeNameByIndex(0), "", true); + EXPECT_TRUE(table); + table.InsertValue("", "b", 20); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{b = 20}], [{a = 10}]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + table = arrayInline.InsertTableArray("", "", true); + EXPECT_TRUE(table); + table.InsertValue("", "c", 30); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{b = 20}], [{a = 10}], [{c = 30}]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + table = arrayInline.InsertTableArray(arrayInline.GetNodeNameByIndex(0), "", false); + EXPECT_TRUE(table); + table.InsertValue("", "d", 40); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{d = 40}], [{b = 20}], [{a = 10}], [{c = 30}]])toml"); + + // Add an additional table array entry for table array #4 + sdv::toml::CNodeCollection array = arrayInline.Get(0); + EXPECT_TRUE(array); + table = array.InsertTable("", "", false); + EXPECT_TRUE(table); + table.InsertValue("", "e", 50); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(inline_array = [[{d = 40}, {e = 50}], [{b = 20}], [{a = 10}], [{c = 30}]])toml"); +} + +TEST(InsertNode, InsertValueInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection table = root.InsertTableArray("", "table_array", false); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert the values into the table + EXPECT_TRUE(table.InsertValue("", "value_int", 10)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_int = 10)toml"); + EXPECT_TRUE(table.InsertValue("", "value_str", u8"abc")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_int = 10 +value_str = "abc")toml"); + EXPECT_TRUE(table.InsertValue(table.GetNodeNameByIndex(0), "value_float", 123.456)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_float = 123.456 +value_int = 10 +value_str = "abc")toml"); +} + +TEST(InsertNode, InsertTableInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection table = root.InsertTableArray("", "table_array", false); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert a standard table + EXPECT_TRUE(table.InsertTable("", "table1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +[table_array.table1])toml"); + + // Insert an inline table before + EXPECT_TRUE(table.InsertTable(table.GetNodeNameByIndex(0), "table2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table2 = {} +[table_array.table1])toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_TRUE(table.InsertTable("", "table3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table2 = {} +table3 = {} +[table_array.table1])toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_TRUE(table.InsertTable(table.GetNodeNameByIndex(0), "table4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table2 = {} +table3 = {} +[table_array.table4] +[table_array.table1])toml"); +} + +TEST(InsertNode, InsertArrayInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection table = root.InsertTableArray("", "table_array", false); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert arrays + EXPECT_TRUE(table.InsertArray("", "value_array1")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_array1 = [])toml"); + EXPECT_TRUE(table.InsertArray("", "value_array2")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_array1 = [] +value_array2 = [])toml"); + EXPECT_TRUE(table.InsertArray(table.GetNodeNameByIndex(0), "value_array3")); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +value_array3 = [] +value_array1 = [] +value_array2 = [])toml"); +} + +TEST(InsertNode, InsertTableArrayInTableArray) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + sdv::toml::CNodeCollection table = root.InsertTableArray("", "table_array", false); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]])toml"); + + // Insert standard table array + EXPECT_TRUE(table.InsertTableArray("", "table_array1", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +[[table_array.table_array1]])toml"); + + // Insert an inline table array before + EXPECT_TRUE(table.InsertTableArray(table.GetNodeNameByIndex(0), "table_array2", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +[[table_array.table_array1]])toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_TRUE(table.InsertTableArray("", "table_array3", true)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +table_array3 = [{}] +[[table_array.table_array1]])toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_TRUE(table.InsertTableArray(table.GetNodeNameByIndex(0), "table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +table_array3 = [{}] +[[table_array.table_array4]] +[[table_array.table_array1]])toml"); + + // Add an additional table array entry for table array #4 + EXPECT_TRUE(table.InsertTableArray("", "table_array4", false)); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array]] +table_array2 = [{}] +table_array3 = [{}] +[[table_array.table_array4]] +[[table_array.table_array1]] +[[table_array.table_array4]])toml"); +} + +TEST(InsertNode, InsertValuesRootAsTOML) +{ + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_EQ(root.InsertTOML("", R"toml(# This is the first value +value_int = 10)toml", true), 1); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is the first value +value_int = 10)toml"); + EXPECT_EQ(root.InsertTOML("", R"toml(value_str = "abc" # This is the second value + +# Comment in between + +# And the third value +value_float = 123.456 + +# Some +# Final +# Words :-) +)toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is the first value +value_int = 10 +value_str = "abc" # This is the second value + +# Comment in between + +# And the third value +value_float = 123.456 + +# Some +# Final +# Words :-) +)toml"); +} + +TEST(InsertNode, InsertTableRootAsTOML) +{ + // Insert a standard table + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_EQ(root.InsertTOML("", R"toml([table1] # This is table 1 +a = 10 +b = 20.30)toml", true), 1); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([table1] # This is table 1 +a = 10 +b = 20.30)toml"); + + // Insert an inline table before + EXPECT_EQ(root.InsertTOML(root.GetNodeNameByIndex(0), R"toml(# This is table 2 +table2 = {c = 40, d = "50"})toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is table 2 +table2 = {c = 40, d = "50"} +[table1] # This is table 1 +a = 10 +b = 20.30)toml"); + + // Insert an inline table behind -> this will have to be printed before the standard table + EXPECT_EQ(root.InsertTOML("", R"toml(table3 = {e = '60'} # This is table 3 +)toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is table 2 +table2 = {c = 40, d = "50"} +table3 = {e = '60'} # This is table 3 +[table1] # This is table 1 +a = 10 +b = 20.30)toml"); + + // Insert a standard table in front -> this will have to be printed behind the inline table + EXPECT_EQ(root.InsertTOML(root.GetNodeNameByIndex(0), R"toml( + +# And this is table 4 +[table4] +f = 70 +g = 80.90)toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is table 2 +table2 = {c = 40, d = "50"} +table3 = {e = '60'} # This is table 3 + + +# And this is table 4 +[table4] +f = 70 +g = 80.90 +[table1] # This is table 1 +a = 10 +b = 20.30)toml"); +} + +TEST(InsertNode, InsertArrayRootAsTOML) +{ + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_EQ(root.InsertTOML("", R"toml(value_array1 = [10, 20, 30] # This is array 1)toml", true), + 1); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(value_array1 = [10, 20, 30] # This is array 1)toml"); + EXPECT_EQ(root.InsertTOML(root.GetNodeNameByIndex(0), R"toml(# This is array 2 +value_array2 = [ + 40, # This is an integer value + 50.60, # This is a float value + "70", # This is a string value + ])toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is array 2 +value_array2 = [ + 40, # This is an integer value + 50.60, # This is a float value + "70", # This is a string value + ] +value_array1 = [10, 20, 30] # This is array 1)toml"); + EXPECT_EQ(root.InsertTOML(root.GetNodeNameByIndex(1), R"toml(# And finally array 3 +value_array3 = [])toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(# This is array 2 +value_array2 = [ + 40, # This is an integer value + 50.60, # This is a float value + "70", # This is a string value + ] +# And finally array 3 +value_array3 = [] +value_array1 = [10, 20, 30] # This is array 1)toml"); +} + +TEST(InsertNode, InsertTableArrayRootAsTOML) +{ + // Insert a standard table array + toml_parser::CParser parser; + sdv::toml::CNodeCollection root(&parser.Root()); + EXPECT_EQ(root.InsertTOML("", R"toml([[table_array1]] +a = 10 +b = 20)toml", true), 1); + std::string ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml([[table_array1]] +a = 10 +b = 20)toml"); + + // Insert an inline table array before + EXPECT_EQ(root.InsertTOML(root.GetNodeNameByIndex(0), R"toml(table_array2 = [{c = 30}, {d = 40}])toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +[[table_array1]] +a = 10 +b = 20)toml"); + + // Insert an inline table array behind -> this will have to be printed before the standard table array + EXPECT_EQ(root.InsertTOML("", R"toml(table_array3 = +[ + { e = 50 } +])toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +table_array3 = +[ + { e = 50 } +] +[[table_array1]] +a = 10 +b = 20)toml"); + + // Insert a standard table array in front -> this will have to be printed behind the inline table array + EXPECT_EQ(root.InsertTOML(root.GetNodeNameByIndex(0), R"toml([[table_array4]] +f = 60 +g = 70)toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +table_array3 = +[ + { e = 50 } +] +[[table_array4]] +f = 60 +g = 70 +[[table_array1]] +a = 10 +b = 20)toml"); + + // Add an additional table array entry for table array #4 (even provided as inline, must be added as standard). + EXPECT_EQ(root.InsertTOML("", R"toml(table_array4 = [{h = 80}])toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +table_array3 = +[ + { e = 50 } +] +[[table_array4]] +f = 60 +g = 70 +[[table_array1]] +a = 10 +b = 20 +[[table_array4]] +h = 80)toml"); + + // Add an additional table array entry for table array #4 (even provided as inline, must be added as standard). + EXPECT_EQ(root.InsertTOML("", R"toml([[table_array3]] +i = 90 +[[table_array3]] +j = 100 +)toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(root.InsertTOML(root.GetNodeNameByIndex(0), R"toml([[table_array3]] +k = 110)toml", true), 1); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +table_array3 = +[{k = 110}, + { e = 50 }, +{i = 90}, {j = 100}] +[[table_array4]] +f = 60 +g = 70 +[[table_array1]] +a = 10 +b = 20 +[[table_array4]] +h = 80)toml"); + + root.AutomaticFormat(true); + ssTOML = parser.GenerateTOML(); + EXPECT_EQ(ssTOML, R"toml(table_array2 = [{c = 30}, {d = 40}] +table_array3 = [{k = 110}, {e = 50}, {i = 90}, {j = 100}] +[[table_array4]] +f = 60 +g = 70 +[[table_array1]] +a = 10 +b = 20 +[[table_array4]] +h = 80)toml"); +} + +TEST(InsertNode, DISABLED_TestInsertValueInStandardTableAsTOML) +{ + //// Insert a standard table + //toml_parser::CParser parser; + //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(root.InsertTable( + // sdv::toml::npos, "standard_table", false)). + // GetInterface(); + //ASSERT_NE(pStandardTable, nullptr); + //std::string ssTOML = parser.GenerateTOML(); + //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); +} + +TEST(InsertNode, DISABLED_TestInsertTableInStandardTableAsTOML) +{ + //// Insert a standard table + //toml_parser::CParser parser; + //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(root.InsertTable( + // sdv::toml::npos, "standard_table", false)). + // GetInterface(); + //ASSERT_NE(pStandardTable, nullptr); + //std::string ssTOML = parser.GenerateTOML(); + //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); +} + +TEST(InsertNode, DISABLED_TestInsertArrayInStandardTableAsTOML) +{ + //// Insert a standard table + //toml_parser::CParser parser; + //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(root.InsertTable( + // sdv::toml::npos, "standard_table", false)). + // GetInterface(); + //ASSERT_NE(pStandardTable, nullptr); + //std::string ssTOML = parser.GenerateTOML(); + //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); +} + +TEST(InsertNode, DISABLED_TestInsertTableArrayInStandardTableAsTOML) +{ + //// Insert a standard table + //toml_parser::CParser parser; + //sdv::toml::INodeCollectionInsert* pStandardTable = sdv::TInterfaceAccessPtr(root.InsertTable( + // sdv::toml::npos, "standard_table", false)). + // GetInterface(); + //ASSERT_NE(pStandardTable, nullptr); + //std::string ssTOML = parser.GenerateTOML(); + //EXPECT_EQ(ssTOML, R"toml([standard_table])toml"); +} + +TEST(InsertNode, DISABLED_TestInsertValueInInlineTableAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertTableInInlineTableAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertArrayInInlineTableAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertTableArrayInInlineTableAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertValueInArrayAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertTableInArrayAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertArrayInArrayAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertTableArrayInArrayAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertValueInTableArrayAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertTableInTableArrayAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertArrayInTableArrayAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertTableArrayInTableArrayAsTOML) +{} + +TEST(InsertNode, DISABLED_TestInsertMixed) +{} + +TEST(InsertNode, DISABLED_TestInsertMixedWithDelete) +{} diff --git a/tests/unit_tests/toml_parser/lexer_tests.cpp b/tests/unit_tests/toml_parser/lexer_tests.cpp index 1d5c85d..347dc68 100644 --- a/tests/unit_tests/toml_parser/lexer_tests.cpp +++ b/tests/unit_tests/toml_parser/lexer_tests.cpp @@ -15,6 +15,7 @@ #include #include #include +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/lexer_toml.h" #include "../../../sdv_services/core/toml_parser/exception.h" @@ -114,7 +115,7 @@ * + Exceptions thrown by the CharacterReaderare caught and result in a token_terminated-Token and no further lexing will be done */ -TEST(TOMLLexerTest, Keys) +TEST(Lexer, Keys) { using namespace std::string_literals; // U+1F92B is the Finger-On-Lips Shushing emoji with UTF-8 byte representation 0xF09FA4AB @@ -169,7 +170,7 @@ TEST(TOMLLexerTest, Keys) EXPECT_EQ("1234", key8.StringValue()); } -TEST(TOMLLexerTest, SyntaxToken_NewLine) +TEST(Lexer, SyntaxToken_NewLine) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -187,7 +188,7 @@ TEST(TOMLLexerTest, SyntaxToken_NewLine) EXPECT_EQ(toml_parser::ETokenCategory::token_syntax_new_line, newLine2.Category()); } -TEST(TOMLLexerTest, SyntaxToken_Bracket) +TEST(Lexer, SyntaxToken_Bracket) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -240,7 +241,7 @@ TEST(TOMLLexerTest, SyntaxToken_Bracket) EXPECT_EQ(toml_parser::ETokenCategory::token_syntax_table_array_close, tableArrayBracketClose.Category()); } -TEST(TOMLLexerTest, SyntaxToken_Assignment) +TEST(Lexer, SyntaxToken_Assignment) { using namespace std::string_literals; toml_parser::CLexer lexerNormal(R"( @@ -279,7 +280,7 @@ TEST(TOMLLexerTest, SyntaxToken_Assignment) EXPECT_EQ(rValueNoSpace.StringValue(), "value"); } -TEST(TOMLLexerTest, Comments) +TEST(Lexer, Comments) { using namespace std::string_literals; toml_parser::CLexer lexerNormal(R"( @@ -414,7 +415,7 @@ TEST(TOMLLexerTest, Comments) EXPECT_EQ(toml_parser::ETokenCategory::token_comment, rNoSpaceTableClose.Next().Category()); } -TEST(TOMLLexerTest, SyntaxToken_Dot) +TEST(Lexer, SyntaxToken_Dot) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -451,7 +452,7 @@ TEST(TOMLLexerTest, SyntaxToken_Dot) EXPECT_EQ(toml_parser::ETokenCategory::token_syntax_dot, dot6.Category()); } -TEST(TOMLLexerTest, SyntaxToken_Braces) +TEST(Lexer, SyntaxToken_Braces) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -511,7 +512,7 @@ TEST(TOMLLexerTest, SyntaxToken_Braces) EXPECT_EQ(toml_parser::ETokenCategory::token_key, emptyTable.Category()); } -TEST(TOMLLexerTest, SyntaxToken_ArrayTable) +TEST(Lexer, SyntaxToken_ArrayTable) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -588,7 +589,7 @@ TEST(TOMLLexerTest, SyntaxToken_ArrayTable) EXPECT_EQ(toml_parser::ETokenCategory::token_syntax_table_array_close, tableArrayClose5.Category()); } -TEST(TOMLLexerTest, Datatype_Integer) +TEST(Lexer, Datatype_Integer) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -758,7 +759,7 @@ TEST(TOMLLexerTest, Datatype_Integer) EXPECT_EQ(toml_parser::ETokenCategory::token_error, intErr10.Category()); } -TEST(TOMLLexerTest, Datatype_Float) +TEST(Lexer, Datatype_Float) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -945,7 +946,7 @@ TEST(TOMLLexerTest, Datatype_Float) EXPECT_EQ(toml_parser::ETokenCategory::token_error, errFloat28.Category()); } -TEST(TOMLLexerTest, Datatype_Boolean) +TEST(Lexer, Datatype_Boolean) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -966,7 +967,7 @@ TEST(TOMLLexerTest, Datatype_Boolean) EXPECT_FALSE(falseToken.BooleanValue()); } -TEST(TOMLLexerTest, Datatype_String_BasicString) +TEST(Lexer, Datatype_String_BasicString) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1045,7 +1046,7 @@ TEST(TOMLLexerTest, Datatype_String_BasicString) EXPECT_EQ("Musical eighth note: 𝅘𝅥𝅮", string_esc9.StringValue()); } -TEST(TOMLLexerTest, Datatype_String_BasicStringMultiline) +TEST(Lexer, Datatype_String_BasicStringMultiline) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1170,7 +1171,7 @@ multiline string""" EXPECT_EQ("Musical eighth note: 𝅘𝅥𝅮 multiline", string_esc9.StringValue()); } -TEST(TOMLLexerTest, Datatype_String_LiteralString) +TEST(Lexer, Datatype_String_LiteralString) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1208,7 +1209,7 @@ TEST(TOMLLexerTest, Datatype_String_LiteralString) EXPECT_EQ("<\\i\\c*\\s*>", regex.StringValue()); } -TEST(TOMLLexerTest, Datatype_String_LiteralStringMultiline) +TEST(Lexer, Datatype_String_LiteralStringMultiline) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1257,27 +1258,27 @@ str = ''''That,' she said, 'is still pointless.'''' EXPECT_EQ("'That,' she said, 'is still pointless.'", str.StringValue()); } -// TEST(TOMLLexerTest, Datatype_OffsetDateTime) +// TEST(Lexer, Datatype_OffsetDateTime) // { // ASSERT_TRUE(false); // } -// TEST(TOMLLexerTest, Datatype_DateTime) +// TEST(Lexer, Datatype_DateTime) // { // ASSERT_TRUE(false); // } -// TEST(TOMLLexerTest, Datatype_LocalDate) +// TEST(Lexer, Datatype_LocalDate) // { // ASSERT_TRUE(false); // } -// TEST(TOMLLexerTest, Datatype_LocalTime) +// TEST(Lexer, Datatype_LocalTime) // { // ASSERT_TRUE(false); // } -TEST(TOMLLexerTest, Invalid_Key) +TEST(Lexer, Invalid_Key) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1306,7 +1307,7 @@ TEST(TOMLLexerTest, Invalid_Key) EXPECT_EQ(toml_parser::ETokenCategory::token_error, invQuotedKey3.Category()); } -TEST(TOMLLexerTest, Invalid_String) +TEST(Lexer, Invalid_String) { using namespace std::string_literals; toml_parser::CLexer lexer1(R"(key1 = "invalid escape sequence \h)"s); @@ -1332,7 +1333,7 @@ TEST(TOMLLexerTest, Invalid_String) EXPECT_EQ(toml_parser::ETokenCategory::token_error, invString4.Category()); } -TEST(TOMLLexerTest, Invalid_Integer) +TEST(Lexer, Invalid_Integer) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1406,7 +1407,7 @@ TEST(TOMLLexerTest, Invalid_Integer) EXPECT_EQ(toml_parser::ETokenCategory::token_error, invInteger15.Category()); } -TEST(TOMLLexerTest, Invalid_Float) +TEST(Lexer, Invalid_Float) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1440,7 +1441,7 @@ TEST(TOMLLexerTest, Invalid_Float) EXPECT_EQ(toml_parser::ETokenCategory::token_error, invFloat5.Category()); } -TEST(TOMLLexerTest, Invalid_Boolean) +TEST(Lexer, Invalid_Boolean) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1459,27 +1460,27 @@ TEST(TOMLLexerTest, Invalid_Boolean) EXPECT_EQ(toml_parser::ETokenCategory::token_error, invBool2.Category()); } -// TEST(TOMLLexerTest, Invalid_OffsetDateTime) +// TEST(Lexer, Invalid_OffsetDateTime) // { // ASSERT_TRUE(false); // } -// TEST(TOMLLexerTest, Invalid_LocalDateTime) +// TEST(Lexer, Invalid_LocalDateTime) // { // ASSERT_TRUE(false); // } -// TEST(TOMLLexerTest, Invalid_LocalDate) +// TEST(Lexer, Invalid_LocalDate) // { // ASSERT_TRUE(false); // } -// TEST(TOMLLexerTest, Invalid_LocalTime) +// TEST(Lexer, Invalid_LocalTime) // { // ASSERT_TRUE(false); // } -TEST(TOMLLexerTest, Peek_NoAdvance) +TEST(Lexer, Peek_NoAdvance) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1500,7 +1501,7 @@ TEST(TOMLLexerTest, Peek_NoAdvance) EXPECT_EQ(toml_parser::ETokenCategory::token_key, lexer.Peek(1).Category()); } -TEST(TOMLLexerTest, Consume_Advance) +TEST(Lexer, Consume_Advance) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1525,7 +1526,7 @@ TEST(TOMLLexerTest, Consume_Advance) EXPECT_EQ(toml_parser::ETokenCategory::token_syntax_assignment, ptr.Category()); } -TEST(TOMLLexerTest, PeekConsume_BoundsCheck) +TEST(Lexer, PeekConsume_BoundsCheck) { using namespace std::string_literals; toml_parser::CLexer lexer(R"( @@ -1544,7 +1545,7 @@ TEST(TOMLLexerTest, PeekConsume_BoundsCheck) EXPECT_FALSE(lexer.Consume(0)); } -TEST(TOMLLexerTest, PeekConsume_EmptyInput) +TEST(Lexer, PeekConsume_EmptyInput) { using namespace std::string_literals; toml_parser::CLexer lexer(R"()"s); @@ -1555,7 +1556,7 @@ TEST(TOMLLexerTest, PeekConsume_EmptyInput) EXPECT_FALSE(lexer.Consume(1)); } -TEST(TOMLLexerTest, ExceptionHandling) +TEST(Lexer, ExceptionHandling) { using namespace std::string_literals; toml_parser::CLexer lexer; @@ -1577,7 +1578,7 @@ TEST(TOMLLexerTest, ExceptionHandling) EXPECT_FALSE(lexer.Consume(0)); } -TEST(TOMLLexerTest, DISABLED_RegenerateTOML) +TEST(Lexer, DISABLED_RegenerateTOML) { using namespace std::string_literals; std::string ssOrginal = R"( diff --git a/tests/unit_tests/toml_parser/main.cpp b/tests/unit_tests/toml_parser/main.cpp index 91e99c3..ecec02e 100644 --- a/tests/unit_tests/toml_parser/main.cpp +++ b/tests/unit_tests/toml_parser/main.cpp @@ -14,6 +14,7 @@ #include #include "../../../global/process_watchdog.h" +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../../sdv_services/core/toml_parser/lexer_toml.cpp" #include "../../../sdv_services/core/toml_parser/lexer_toml_token.cpp" @@ -21,6 +22,8 @@ #include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp" #include "../../../sdv_services/core/toml_parser/miscellaneous.cpp" #include "../../../sdv_services/core/toml_parser/code_snippet.cpp" +#include "../../../sdv_services/core/toml_parser/parser_node_indexer.cpp" + #if defined(_WIN32) && defined(_UNICODE) extern "C" int wmain(int argc, wchar_t* argv[]) @@ -30,6 +33,7 @@ extern "C" int main(int argc, char* argv[]) { CProcessWatchdog watchdog; + CLocalMemMgr memmgr; testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/tests/unit_tests/toml_parser/make_inline_standard.cpp b/tests/unit_tests/toml_parser/make_inline_standard.cpp new file mode 100644 index 0000000..30386be --- /dev/null +++ b/tests/unit_tests/toml_parser/make_inline_standard.cpp @@ -0,0 +1,289 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial implementation + ********************************************************************************/ + +#include +#include +#include +#include "../../../global/localmemmgr.h" +#include "../../../sdv_services/core/toml_parser/exception.h" +#include "../../../sdv_services/core/toml_parser/miscellaneous.h" +#include "../../../sdv_services/core/toml_parser/parser_toml.h" +#include + +TEST(MakeInline, Value) +{ + std::string ssTomlIn = R"toml(value1 = 1)toml"; + std::string ssTomlOut = R"toml(value1 = 1)toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeInline()); + EXPECT_TRUE(parser.Root().MakeInline()); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeStandard, Value) +{ + std::string ssTomlIn = R"toml(value1 = 1)toml"; + std::string ssTomlOut = R"toml(value1 = 1)toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_FALSE(parser.Root().CanMakeStandard()); + EXPECT_FALSE(parser.Root().MakeStandard(true)); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeInline, Array) +{ + std::string ssTomlIn = R"toml(value1 = ["abc", "def", "ghi"])toml"; + std::string ssTomlOut = R"toml(value1 = ["abc", "def", "ghi"])toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeInline()); + EXPECT_TRUE(parser.Root().MakeInline()); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeStandard, Array) +{ + std::string ssTomlIn = R"toml(value1 = ["abc", "def", "ghi"])toml"; + std::string ssTomlOut = R"toml(value1 = ["abc", "def", "ghi"])toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_FALSE(parser.Root().CanMakeStandard()); + EXPECT_FALSE(parser.Root().MakeStandard(true)); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeInline, StandardTable) +{ + std::string ssTomlIn = R"toml([table1] +value = 1)toml"; + std::string ssTomlOut = R"toml(table1 = {value = 1})toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeInline()); + EXPECT_TRUE(parser.Root().MakeInline()); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeStandard, StandardTable) +{ + std::string ssTomlIn = R"toml([table1] +value = 1)toml"; + std::string ssTomlOut = R"toml([table1] +value = 1)toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeStandard()); + EXPECT_TRUE(parser.Root().MakeStandard(true)); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeInline, InlineTable) +{ + std::string ssTomlIn = R"toml(table1 = {value = 1})toml"; + std::string ssTomlOut = R"toml(table1 = {value = 1})toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeInline()); + EXPECT_TRUE(parser.Root().MakeInline()); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeStandard, InlineTable) +{ + std::string ssTomlIn = R"toml(table1 = {value = 1})toml"; + std::string ssTomlOut = R"toml([table1] +value = 1)toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeStandard()); + EXPECT_TRUE(parser.Root().MakeStandard(true)); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeInline, StandardTableArray) +{ + std::string ssTomlIn = R"toml([[table_array1]] +value = 1)toml"; + std::string ssTomlOut = R"toml(table_array1 = [{value = 1}])toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeInline()); + EXPECT_TRUE(parser.Root().MakeInline()); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeStandard, StandardTableArray) +{ + std::string ssTomlIn = R"toml([[table_array1]] +value = 1)toml"; + std::string ssTomlOut = R"toml([[table_array1]] +value = 1)toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeStandard()); + EXPECT_TRUE(parser.Root().MakeStandard(true)); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeInline, InlineTableArray) +{ + std::string ssTomlIn = R"toml(table_array1 = [{value = 1}])toml"; + std::string ssTomlOut = R"toml(table_array1 = [{value = 1}])toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeInline()); + EXPECT_TRUE(parser.Root().MakeInline()); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeStandard, InlineTableArray) +{ + std::string ssTomlIn = R"toml(table_array1 = [{value = 1}])toml"; + std::string ssTomlOut = R"toml([[table_array1]] +value = 1)toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeStandard()); + EXPECT_TRUE(parser.Root().MakeStandard(true)); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeInline, StandardTableImplicit) +{ + std::string ssTomlIn = R"toml([root_table.table1] +value = 1)toml"; + std::string ssTomlOut = R"toml(root_table.table1 = {value = 1})toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeInline()); + EXPECT_TRUE(parser.Root().MakeInline()); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeStandard, StandardTableImplicit) +{ + std::string ssTomlIn = R"toml([root_table.table1] +value = 1)toml"; + std::string ssTomlOut = R"toml([root_table.table1] +value = 1)toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeStandard()); + EXPECT_TRUE(parser.Root().MakeStandard(true)); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeInline, InlineTableImplicit) +{ + std::string ssTomlIn = R"toml(root_table.table1 = {value = 1})toml"; + std::string ssTomlOut = R"toml(root_table.table1 = {value = 1})toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeInline()); + EXPECT_TRUE(parser.Root().MakeInline()); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeStandard, InlineTableImplicit) +{ + std::string ssTomlIn = R"toml(root_table.table1 = {value = 1})toml"; + std::string ssTomlOut = R"toml([root_table.table1] +value = 1)toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeStandard()); + EXPECT_TRUE(parser.Root().MakeStandard(true)); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeInline, StandardTableNested) +{ + std::string ssTomlIn = R"toml([table1] +value1 = 1 +[table1.table2] +value2 = 2 +[table1.table3.table4] +value3 = 3 +value4 = 4)toml"; + std::string ssTomlOut = R"toml(table1 = {value1 = 1, table2 = {value2 = 2}, table3.table4 = {value3 = 3, value4 = 4}})toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeInline()); + EXPECT_TRUE(parser.Root().MakeInline()); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeStandard, StandardTableNested) +{ + std::string ssTomlIn = R"toml([table1] +value = 1 +[table1.table2] +value = 2)toml"; + std::string ssTomlOut = R"toml([table1] +value = 1 +[table1.table2] +value = 2)toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeStandard()); + EXPECT_TRUE(parser.Root().MakeStandard(true)); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeInline, InlineTableNested) +{ + std::string ssTomlIn = R"toml(table1 = {value = 1, table2 = {value = 2}})toml"; + std::string ssTomlOut = R"toml(table1 = {value = 1, table2 = {value = 2}})toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeInline()); + EXPECT_TRUE(parser.Root().MakeInline()); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + +TEST(MakeStandard, InlineTableNested) +{ + std::string ssTomlIn = R"toml(table1 = {value = 1, table2 = {value = 2}})toml"; + std::string ssTomlOut = R"toml([table1] +value = 1 +[table1.table2] +value = 2)toml"; + + toml_parser::CParser parser(ssTomlIn); + EXPECT_TRUE(parser.Root().CanMakeStandard()); + EXPECT_TRUE(parser.Root().MakeStandard(true)); + std::string ssOut = parser.Root().GetTOML(); + EXPECT_EQ(ssTomlOut, ssOut); +} + diff --git a/tests/unit_tests/toml_parser/miscellaneous_tests.cpp b/tests/unit_tests/toml_parser/miscellaneous.cpp similarity index 96% rename from tests/unit_tests/toml_parser/miscellaneous_tests.cpp rename to tests/unit_tests/toml_parser/miscellaneous.cpp index e1d85c8..52868a5 100644 --- a/tests/unit_tests/toml_parser/miscellaneous_tests.cpp +++ b/tests/unit_tests/toml_parser/miscellaneous.cpp @@ -15,11 +15,16 @@ #include #include #include - +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/exception.h" #include "../../../sdv_services/core/toml_parser/miscellaneous.h" -TEST(TOMLMiscellaneousTests, Hex2Dec) +// Test TODO: +// Shift nodes up and down within one container +// Remove formatting from node +// Compare TOMLs + +TEST(Miscellaneous, Hex2Dec) { // No string std::string ssEmpty; @@ -47,7 +52,7 @@ TEST(TOMLMiscellaneousTests, Hex2Dec) EXPECT_EQ(toml_parser::HexadecimalToDecimal("10xyz"), 16u); } -TEST(TOMLMiscellaneousTests, Dec2Dec) +TEST(Miscellaneous, Dec2Dec) { // No string std::string ssEmpty; @@ -75,7 +80,7 @@ TEST(TOMLMiscellaneousTests, Dec2Dec) EXPECT_EQ(toml_parser::DecimalToDecimal("10xyz"), 10u); } -TEST(TOMLMiscellaneousTests, Oct2Dec) +TEST(Miscellaneous, Oct2Dec) { // No string std::string ssEmpty; @@ -103,7 +108,7 @@ TEST(TOMLMiscellaneousTests, Oct2Dec) EXPECT_EQ(toml_parser::OctalToDecimal("12xyz"), 10u); } -TEST(TOMLMiscellaneousTests, Bin2Dec) +TEST(Miscellaneous, Bin2Dec) { // No string std::string ssEmpty; @@ -131,7 +136,7 @@ TEST(TOMLMiscellaneousTests, Bin2Dec) EXPECT_EQ(toml_parser::BinaryToDecimal("1010yz"), 10u); } -TEST(TOMLMiscellaneousTests, UnicodeCharacter) +TEST(Miscellaneous, UnicodeCharacter) { // No string std::string ssEmpty; @@ -147,7 +152,7 @@ TEST(TOMLMiscellaneousTests, UnicodeCharacter) EXPECT_EQ(toml_parser::EscapedUnicodeCharacterToUTF8("0001F600"), u8"\U0001F600"); } -TEST(TOMLMiscellaneousTests, SplitKeyStringEmpty) +TEST(Miscellaneous, SplitKeyStringEmpty) { std::string ssKeyEmpty; auto prSplittedKey = toml_parser::SplitNodeKey(ssKeyEmpty); @@ -155,7 +160,7 @@ TEST(TOMLMiscellaneousTests, SplitKeyStringEmpty) EXPECT_TRUE(prSplittedKey.second.empty()); } -TEST(TOMLMiscellaneousTests, SplitKeyFirstPartOnly) +TEST(Miscellaneous, SplitKeyFirstPartOnly) { std::string ssKey = "abc"; auto prSplittedKey = toml_parser::SplitNodeKey(ssKey); @@ -196,7 +201,7 @@ TEST(TOMLMiscellaneousTests, SplitKeyFirstPartOnly) EXPECT_TRUE(prSplittedKey.second.empty()); } -TEST(TOMLMiscellaneousTests, SplitStandardBareKey) +TEST(Miscellaneous, SplitStandardBareKey) { std::string ssKey = "abc.def"; auto prSplittedKey = toml_parser::SplitNodeKey(ssKey); @@ -236,7 +241,7 @@ TEST(TOMLMiscellaneousTests, SplitStandardBareKey) EXPECT_TRUE(prSplittedKey.second.empty()); } -TEST(TOMLMiscellaneousTests, SplitBareKeyWithSpace) +TEST(Miscellaneous, SplitBareKeyWithSpace) { std::string ssKey = " abc.def"; auto prSplittedKey = toml_parser::SplitNodeKey(ssKey); @@ -265,7 +270,7 @@ TEST(TOMLMiscellaneousTests, SplitBareKeyWithSpace) EXPECT_TRUE(prSplittedKey.second.empty()); } -TEST(TOMLMiscellaneousTests, SplitLiteralKey) +TEST(Miscellaneous, SplitLiteralKey) { std::string ssKey = "'abc'"; auto prSplittedKey = toml_parser::SplitNodeKey(ssKey); @@ -309,7 +314,7 @@ TEST(TOMLMiscellaneousTests, SplitLiteralKey) EXPECT_TRUE(prSplittedKey.second.empty()); } -TEST(TOMLMiscellaneousTests, SplitQuotedKey) +TEST(Miscellaneous, SplitQuotedKey) { std::string ssKey = "\"abc\""; auto prSplittedKey = toml_parser::SplitNodeKey(ssKey); @@ -348,7 +353,7 @@ TEST(TOMLMiscellaneousTests, SplitQuotedKey) EXPECT_TRUE(prSplittedKey.second.empty()); } -TEST(TOMLMiscellaneousTests, SplitEscapedQuotedKey) +TEST(Miscellaneous, SplitEscapedQuotedKey) { std::string ssKey = "\"abc\\bdef\""; auto prSplittedKey = toml_parser::SplitNodeKey(ssKey); @@ -413,7 +418,7 @@ TEST(TOMLMiscellaneousTests, SplitEscapedQuotedKey) EXPECT_TRUE(prSplittedKey.second.empty()); } -TEST(TOMLMiscellaneousTests, SplitTableKey) +TEST(Miscellaneous, SplitTableKey) { std::string ssKey = "abc.def"; auto prSplittedKey = toml_parser::SplitNodeKey(ssKey); @@ -431,7 +436,7 @@ TEST(TOMLMiscellaneousTests, SplitTableKey) EXPECT_EQ(prSplittedKey.second, "def.ghi"); } -TEST(TOMLMiscellaneousTests, SplitArrayKey) +TEST(Miscellaneous, SplitArrayKey) { std::string ssKey = "abc[1]"; auto prSplittedKey = toml_parser::SplitNodeKey(ssKey); @@ -493,7 +498,7 @@ TEST(TOMLMiscellaneousTests, SplitArrayKey) EXPECT_TRUE(prSplittedKey.second.empty()); } -TEST(TOMLMiscellaneousTests, SmartQuoteBareKeys) +TEST(Miscellaneous, SmartQuoteBareKeys) { EXPECT_EQ(toml_parser::QuoteText("abc", toml_parser::EQuoteRequest::smart_key), "abc"); EXPECT_EQ(toml_parser::QuoteText("123", toml_parser::EQuoteRequest::smart_key), "123"); @@ -502,7 +507,7 @@ TEST(TOMLMiscellaneousTests, SmartQuoteBareKeys) EXPECT_EQ(toml_parser::QuoteText("ABC-DEF", toml_parser::EQuoteRequest::smart_key), "ABC-DEF"); } -TEST(TOMLMiscellaneousTests, SmartQuoteSpecialCharsKeys) +TEST(Miscellaneous, SmartQuoteSpecialCharsKeys) { EXPECT_EQ(toml_parser::QuoteText("", toml_parser::EQuoteRequest::smart_key), "\"\""); EXPECT_EQ(toml_parser::QuoteText("abc def", toml_parser::EQuoteRequest::smart_key), "\"abc def\""); @@ -515,7 +520,7 @@ TEST(TOMLMiscellaneousTests, SmartQuoteSpecialCharsKeys) EXPECT_EQ(toml_parser::QuoteText("abc/", toml_parser::EQuoteRequest::smart_key), "\"abc/\""); } -TEST(TOMLMiscellaneousTests, SmartQuoteEscapeCharsKeys) +TEST(Miscellaneous, SmartQuoteEscapeCharsKeys) { EXPECT_EQ(toml_parser::QuoteText("abc\tdef", toml_parser::EQuoteRequest::smart_key), "\"abc\\tdef\""); EXPECT_EQ(toml_parser::QuoteText("abc\\def", toml_parser::EQuoteRequest::smart_key), "\"abc\\\\def\""); @@ -527,7 +532,7 @@ TEST(TOMLMiscellaneousTests, SmartQuoteEscapeCharsKeys) EXPECT_EQ(toml_parser::QuoteText("abc\rdef", toml_parser::EQuoteRequest::smart_key), "\"abc\\rdef\""); } -TEST(TOMLMiscellaneousTests, SmartQuoteControlCharsKeys) +TEST(Miscellaneous, SmartQuoteControlCharsKeys) { EXPECT_EQ(toml_parser::QuoteText(std::string(1, '\0'), toml_parser::EQuoteRequest::smart_key), "\"\\u0000\""); EXPECT_EQ(toml_parser::QuoteText("\u0001", toml_parser::EQuoteRequest::smart_key), "\"\\u0001\""); @@ -567,7 +572,7 @@ TEST(TOMLMiscellaneousTests, SmartQuoteControlCharsKeys) // 0080... and higher are treated as unicode character (quotation needed) } -TEST(TOMLMiscellaneousTests, SmartQuoteText) +TEST(Miscellaneous, SmartQuoteText) { EXPECT_EQ(toml_parser::QuoteText("abc", toml_parser::EQuoteRequest::smart_text), "\"abc\""); EXPECT_EQ(toml_parser::QuoteText("123", toml_parser::EQuoteRequest::smart_text), "\"123\""); @@ -576,7 +581,7 @@ TEST(TOMLMiscellaneousTests, SmartQuoteText) EXPECT_EQ(toml_parser::QuoteText("ABC-DEF", toml_parser::EQuoteRequest::smart_text), "\"ABC-DEF\""); } -TEST(TOMLMiscellaneousTests, SmartQuoteSpecialCharsText) +TEST(Miscellaneous, SmartQuoteSpecialCharsText) { EXPECT_EQ(toml_parser::QuoteText("", toml_parser::EQuoteRequest::smart_text), "\"\""); EXPECT_EQ(toml_parser::QuoteText("abc def", toml_parser::EQuoteRequest::smart_text), "\"abc def\""); @@ -589,7 +594,7 @@ TEST(TOMLMiscellaneousTests, SmartQuoteSpecialCharsText) EXPECT_EQ(toml_parser::QuoteText("abc/", toml_parser::EQuoteRequest::smart_text), "\"abc/\""); } -TEST(TOMLMiscellaneousTests, SmartQuoteEscapeCharsText) +TEST(Miscellaneous, SmartQuoteEscapeCharsText) { EXPECT_EQ(toml_parser::QuoteText("abc\tdef", toml_parser::EQuoteRequest::smart_text), "\"abc\\tdef\""); EXPECT_EQ(toml_parser::QuoteText("abc\\def", toml_parser::EQuoteRequest::smart_text), "'abc\\def'"); @@ -601,7 +606,7 @@ TEST(TOMLMiscellaneousTests, SmartQuoteEscapeCharsText) EXPECT_EQ(toml_parser::QuoteText("abc\rdef", toml_parser::EQuoteRequest::smart_text), "\"abc\\rdef\""); } -TEST(TOMLMiscellaneousTests, SmartQuoteControlCharsText) +TEST(Miscellaneous, SmartQuoteControlCharsText) { EXPECT_EQ(toml_parser::QuoteText(std::string(1, '\0'), toml_parser::EQuoteRequest::smart_text), "\"\\u0000\""); EXPECT_EQ(toml_parser::QuoteText("\u0001", toml_parser::EQuoteRequest::smart_text), "\"\\u0001\""); @@ -641,7 +646,7 @@ TEST(TOMLMiscellaneousTests, SmartQuoteControlCharsText) // 0080... and higher are treated as unicode character (quotation needed) } -TEST(TOMLMiscellaneousTests, QuotedText) +TEST(Miscellaneous, QuotedText) { EXPECT_EQ(toml_parser::QuoteText("abc", toml_parser::EQuoteRequest::quoted_text), "\"abc\""); EXPECT_EQ(toml_parser::QuoteText("123", toml_parser::EQuoteRequest::quoted_text), "\"123\""); @@ -650,7 +655,7 @@ TEST(TOMLMiscellaneousTests, QuotedText) EXPECT_EQ(toml_parser::QuoteText("ABC-DEF", toml_parser::EQuoteRequest::quoted_text), "\"ABC-DEF\""); } -TEST(TOMLMiscellaneousTests, QuotedSpecialCharsText) +TEST(Miscellaneous, QuotedSpecialCharsText) { EXPECT_EQ(toml_parser::QuoteText("", toml_parser::EQuoteRequest::quoted_text), "\"\""); EXPECT_EQ(toml_parser::QuoteText("abc def", toml_parser::EQuoteRequest::quoted_text), "\"abc def\""); @@ -663,7 +668,7 @@ TEST(TOMLMiscellaneousTests, QuotedSpecialCharsText) EXPECT_EQ(toml_parser::QuoteText("abc/", toml_parser::EQuoteRequest::quoted_text), "\"abc/\""); } -TEST(TOMLMiscellaneousTests, QuotedEscapeCharsText) +TEST(Miscellaneous, QuotedEscapeCharsText) { EXPECT_EQ(toml_parser::QuoteText("abc\tdef", toml_parser::EQuoteRequest::quoted_text), "\"abc\\tdef\""); EXPECT_EQ(toml_parser::QuoteText("abc\\def", toml_parser::EQuoteRequest::quoted_text), "\"abc\\\\def\""); @@ -675,7 +680,7 @@ TEST(TOMLMiscellaneousTests, QuotedEscapeCharsText) EXPECT_EQ(toml_parser::QuoteText("abc\rdef", toml_parser::EQuoteRequest::quoted_text), "\"abc\\rdef\""); } -TEST(TOMLMiscellaneousTests, QuotedControlCharsText) +TEST(Miscellaneous, QuotedControlCharsText) { EXPECT_EQ(toml_parser::QuoteText(std::string(1, '\0'), toml_parser::EQuoteRequest::quoted_text), "\"\\u0000\""); EXPECT_EQ(toml_parser::QuoteText("\u0001", toml_parser::EQuoteRequest::quoted_text), "\"\\u0001\""); @@ -715,7 +720,7 @@ TEST(TOMLMiscellaneousTests, QuotedControlCharsText) // 0080... and higher are treated as unicode character (quotation needed) } -TEST(TOMLMiscellaneousTests, LiteralText) +TEST(Miscellaneous, LiteralText) { EXPECT_EQ(toml_parser::QuoteText("abc", toml_parser::EQuoteRequest::literal_text), "'abc'"); EXPECT_EQ(toml_parser::QuoteText("123", toml_parser::EQuoteRequest::literal_text), "'123'"); @@ -724,7 +729,7 @@ TEST(TOMLMiscellaneousTests, LiteralText) EXPECT_EQ(toml_parser::QuoteText("ABC-DEF", toml_parser::EQuoteRequest::literal_text), "'ABC-DEF'"); } -TEST(TOMLMiscellaneousTests, LiteralSpecialCharsText) +TEST(Miscellaneous, LiteralSpecialCharsText) { EXPECT_EQ(toml_parser::QuoteText("", toml_parser::EQuoteRequest::literal_text), "''"); EXPECT_EQ(toml_parser::QuoteText("abc def", toml_parser::EQuoteRequest::literal_text), "'abc def'"); @@ -737,7 +742,7 @@ TEST(TOMLMiscellaneousTests, LiteralSpecialCharsText) EXPECT_EQ(toml_parser::QuoteText("abc/", toml_parser::EQuoteRequest::literal_text), "'abc/'"); } -TEST(TOMLMiscellaneousTests, LiteralEscapeCharsText) +TEST(Miscellaneous, LiteralEscapeCharsText) { EXPECT_EQ(toml_parser::QuoteText("abc\tdef", toml_parser::EQuoteRequest::literal_text), "\"abc\\tdef\""); // Becomes quoted EXPECT_EQ(toml_parser::QuoteText("abc\\def", toml_parser::EQuoteRequest::literal_text), "'abc\\def'"); @@ -749,7 +754,7 @@ TEST(TOMLMiscellaneousTests, LiteralEscapeCharsText) EXPECT_EQ(toml_parser::QuoteText("abc\rdef", toml_parser::EQuoteRequest::literal_text), "\"abc\\rdef\""); // Becomes quoted } -TEST(TOMLMiscellaneousTests, LiteralControlCharsText) +TEST(Miscellaneous, LiteralControlCharsText) { // All become quoted insteda of literal (due to control character). EXPECT_EQ(toml_parser::QuoteText(std::string(1, '\0'), toml_parser::EQuoteRequest::literal_text), "\"\\u0000\""); @@ -790,7 +795,7 @@ TEST(TOMLMiscellaneousTests, LiteralControlCharsText) // 0080... and higher are treated as unicode character (quotation needed) } -TEST(TOMLMiscellaneousTests, MultiLineQuotedText) +TEST(Miscellaneous, MultiLineQuotedText) { EXPECT_EQ(toml_parser::QuoteText("abc", toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"abc\"\"\""); EXPECT_EQ(toml_parser::QuoteText("123", toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"123\"\"\""); @@ -799,7 +804,7 @@ TEST(TOMLMiscellaneousTests, MultiLineQuotedText) EXPECT_EQ(toml_parser::QuoteText("ABC-DEF", toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"ABC-DEF\"\"\""); } -TEST(TOMLMiscellaneousTests, MultiLineQuotedSpecialCharsText) +TEST(Miscellaneous, MultiLineQuotedSpecialCharsText) { EXPECT_EQ(toml_parser::QuoteText("", toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"\"\"\""); EXPECT_EQ(toml_parser::QuoteText("abc def", toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"abc def\"\"\""); @@ -812,7 +817,7 @@ TEST(TOMLMiscellaneousTests, MultiLineQuotedSpecialCharsText) EXPECT_EQ(toml_parser::QuoteText("abc/", toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"abc/\"\"\""); } -TEST(TOMLMiscellaneousTests, MultiLineQuotedEscapeCharsText) +TEST(Miscellaneous, MultiLineQuotedEscapeCharsText) { EXPECT_EQ(toml_parser::QuoteText("abc\tdef", toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"abc\\tdef\"\"\""); EXPECT_EQ(toml_parser::QuoteText("abc\\def", toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"abc\\\\def\"\"\""); @@ -826,7 +831,7 @@ TEST(TOMLMiscellaneousTests, MultiLineQuotedEscapeCharsText) EXPECT_EQ(toml_parser::QuoteText("abc\rdef", toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"abc\rdef\"\"\""); } -TEST(TOMLMiscellaneousTests, MultiLineQuotedControlCharsText) +TEST(Miscellaneous, MultiLineQuotedControlCharsText) { EXPECT_EQ(toml_parser::QuoteText(std::string(1, '\0'), toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"\\u0000\"\"\""); EXPECT_EQ(toml_parser::QuoteText("\u0001", toml_parser::EQuoteRequest::multi_line_quoted_text), "\"\"\"\\u0001\"\"\""); @@ -866,7 +871,7 @@ TEST(TOMLMiscellaneousTests, MultiLineQuotedControlCharsText) // 0080... and higher are treated as unicode character (quotation needed) } -TEST(TOMLMiscellaneousTests, MultiLineLiteralText) +TEST(Miscellaneous, MultiLineLiteralText) { EXPECT_EQ(toml_parser::QuoteText("abc", toml_parser::EQuoteRequest::multi_line_literal_text), "'''abc'''"); EXPECT_EQ(toml_parser::QuoteText("123", toml_parser::EQuoteRequest::multi_line_literal_text), "'''123'''"); @@ -875,7 +880,7 @@ TEST(TOMLMiscellaneousTests, MultiLineLiteralText) EXPECT_EQ(toml_parser::QuoteText("ABC-DEF", toml_parser::EQuoteRequest::multi_line_literal_text), "'''ABC-DEF'''"); } -TEST(TOMLMiscellaneousTests, MultiLineLiteralSpecialCharsText) +TEST(Miscellaneous, MultiLineLiteralSpecialCharsText) { EXPECT_EQ(toml_parser::QuoteText("", toml_parser::EQuoteRequest::multi_line_literal_text), "''''''"); EXPECT_EQ(toml_parser::QuoteText("abc def", toml_parser::EQuoteRequest::multi_line_literal_text), "'''abc def'''"); @@ -888,7 +893,7 @@ TEST(TOMLMiscellaneousTests, MultiLineLiteralSpecialCharsText) EXPECT_EQ(toml_parser::QuoteText("abc/", toml_parser::EQuoteRequest::multi_line_literal_text), "'''abc/'''"); } -TEST(TOMLMiscellaneousTests, MultiLineLiteralEscapeCharsText) +TEST(Miscellaneous, MultiLineLiteralEscapeCharsText) { EXPECT_EQ(toml_parser::QuoteText("abc\tdef", toml_parser::EQuoteRequest::multi_line_literal_text), "\"\"\"abc\\tdef\"\"\""); // Becomes quoted EXPECT_EQ(toml_parser::QuoteText("abc\\def", toml_parser::EQuoteRequest::multi_line_literal_text), "'''abc\\def'''"); @@ -901,7 +906,7 @@ TEST(TOMLMiscellaneousTests, MultiLineLiteralEscapeCharsText) EXPECT_EQ(toml_parser::QuoteText("abc\rdef", toml_parser::EQuoteRequest::multi_line_literal_text), "'''abc\rdef'''"); } -TEST(TOMLMiscellaneousTests, MultiLineLiteralControlCharsText) +TEST(Miscellaneous, MultiLineLiteralControlCharsText) { // All become quoted insteda of literal (due to control character). EXPECT_EQ(toml_parser::QuoteText(std::string(1, '\0'), toml_parser::EQuoteRequest::multi_line_literal_text), "\"\"\"\\u0000\"\"\""); @@ -942,7 +947,7 @@ TEST(TOMLMiscellaneousTests, MultiLineLiteralControlCharsText) // 0080... and higher are treated as unicode character (quotation needed) } -TEST(TOMLMiscellaneousTests, ExtractBareKeyName) +TEST(Miscellaneous, ExtractBareKeyName) { EXPECT_EQ(toml_parser::ExtractKeyName(""), ""); EXPECT_EQ(toml_parser::ExtractKeyName("abc.def"), "def"); @@ -955,7 +960,7 @@ TEST(TOMLMiscellaneousTests, ExtractBareKeyName) EXPECT_EQ(toml_parser::ExtractKeyName("1234"), "1234"); } -TEST(TOMLMiscellaneousTests, ExtractQuotedKeyName) +TEST(Miscellaneous, ExtractQuotedKeyName) { EXPECT_EQ(toml_parser::ExtractKeyName("\"\""), ""); EXPECT_EQ(toml_parser::ExtractKeyName("\"abc\""), "abc"); @@ -968,7 +973,7 @@ TEST(TOMLMiscellaneousTests, ExtractQuotedKeyName) EXPECT_EQ(toml_parser::ExtractKeyName("abc\"def\""), ""); // Failure } -TEST(TOMLMiscellaneousTests, ExtractLiteralKeyName) +TEST(Miscellaneous, ExtractLiteralKeyName) { EXPECT_EQ(toml_parser::ExtractKeyName("''"), ""); EXPECT_EQ(toml_parser::ExtractKeyName("'abc'"), "abc"); @@ -982,7 +987,7 @@ TEST(TOMLMiscellaneousTests, ExtractLiteralKeyName) EXPECT_EQ(toml_parser::ExtractKeyName("abc'def"), ""); // Failure } -TEST(TOMLMiscellaneousTests, ExtractIndexKeyName) +TEST(Miscellaneous, ExtractIndexKeyName) { EXPECT_EQ(toml_parser::ExtractKeyName("[0]"), "0"); EXPECT_EQ(toml_parser::ExtractKeyName("[0][1]"), "1"); diff --git a/tests/unit_tests/toml_parser/parser_tests.cpp b/tests/unit_tests/toml_parser/parser_tests.cpp index 3e20625..e16ee84 100644 --- a/tests/unit_tests/toml_parser/parser_tests.cpp +++ b/tests/unit_tests/toml_parser/parser_tests.cpp @@ -13,6 +13,7 @@ ********************************************************************************/ #include +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/parser_toml.h" #include "../../../sdv_services/core/toml_parser/parser_node_toml.h" @@ -444,32 +445,32 @@ TEST(NestedContent, InlineTable) TEST(NestedContent, InlineTableBreakLine) { // The following are not allowed - EXPECT_THROW(toml_parser::CParser(R"code( + EXPECT_THROW(toml_parser::CParser(R"toml( table = { a = 1, b = 2, c = 3, d = 4 } -)code"), sdv::toml::XTOMLParseException); - EXPECT_THROW(toml_parser::CParser(R"code( +)toml"), sdv::toml::XTOMLParseException); + EXPECT_THROW(toml_parser::CParser(R"toml( table = { a = 1, b = 2 ,c = 3, d = 4 } -)code"), sdv::toml::XTOMLParseException); +)toml"), sdv::toml::XTOMLParseException); // Line breaks are allowed when part of an array or have multi-line strings - EXPECT_NO_THROW(toml_parser::CParser(R"code( + EXPECT_NO_THROW(toml_parser::CParser(R"toml( array = [{ a = 1, b = 2}, {c = 3, d = 4}] -)code")); - EXPECT_NO_THROW(toml_parser::CParser(R"code( +)toml")); + EXPECT_NO_THROW(toml_parser::CParser(R"toml( table = { a = 1, b = [2, 3, 4, 5], c = 6, d = 7} -)code")); - EXPECT_NO_THROW(toml_parser::CParser(R"code( +)toml")); + EXPECT_NO_THROW(toml_parser::CParser(R"toml( table = { x = "abc", y = """def- ghi""", z = "jkl" } -)code")); - EXPECT_NO_THROW(toml_parser::CParser(R"code( +)toml")); + EXPECT_NO_THROW(toml_parser::CParser(R"toml( table = { x = 'abc', y = '''def- ghi''', z = 'jkl' } -)code")); +)toml")); } TEST(SpecialCases, Keys) @@ -865,6 +866,60 @@ TEST(Ordering, TableAray) EXPECT_EQ(ptrArray->Get(uiIndex)->Cast()->Direct("a")->GetValue(), (int64_t) uiIndex); } +TEST(Ordering, TableArayWithTables) +{ + using namespace std::string_literals; + toml_parser::CParser parser(R"( + [topTable] + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 0 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 1 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 2 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 3 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 4 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 5 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 6 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 7 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 8 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 9 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 10 + [[topTable.tableArray]] + [topTable.tableArray.MyTable] + a = 11 + )"s); + + auto tableArray = parser.Root().Direct("topTable.tableArray"); + + ASSERT_NE(tableArray, nullptr); + auto ptrArray = tableArray->Cast(); + EXPECT_EQ(ptrArray->GetCount(), 12u); + for (uint32_t uiIndex = 0; uiIndex < ptrArray->GetCount(); uiIndex++) + { + EXPECT_EQ(ptrArray->Get(uiIndex)->Cast()->Direct("MyTable.a")->GetValue(), (int64_t)uiIndex); + } +} + TEST(Ordering, NodeGetDirect) { using namespace std::string_literals; diff --git a/tests/unit_tests/toml_parser/statement_boundary_detection.cpp b/tests/unit_tests/toml_parser/statement_boundary_detection.cpp index 873a30e..695f6c2 100644 --- a/tests/unit_tests/toml_parser/statement_boundary_detection.cpp +++ b/tests/unit_tests/toml_parser/statement_boundary_detection.cpp @@ -14,6 +14,7 @@ #include #include #include +#include "../../../global/localmemmgr.h" #include "../../../sdv_services/core/toml_parser/lexer_toml.h" #include "../../../sdv_services/core/toml_parser/exception.h" #include "../../../sdv_services/core/toml_parser/miscellaneous.h" @@ -445,7 +446,7 @@ bool FindAndExtendToken(toml_parser::CLexer& rlexer, const std::string& rssKey, return false; // When coming here, the key was not found } -TEST(TOMLLexerStatementBoundaryTests, CheckEmptyRange) +TEST(StatementBoundary, CheckEmptyRange) { std::string ssEmpty; @@ -459,11 +460,11 @@ TEST(TOMLLexerStatementBoundaryTests, CheckEmptyRange) EXPECT_THROW(lexerEmpty.SmartExtendNodeRange(rangeEmpty), sdv::toml::XTOMLParseException); } -TEST(TOMLLexerStatementBoundaryTests, CheckInvalidRange) +TEST(StatementBoundary, CheckInvalidRange) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_a = 10 -)code"; +)toml"; std::string ssOther = ssCode; // Process the code @@ -477,11 +478,11 @@ token_a = 10 EXPECT_THROW(lexerCode.SmartExtendNodeRange(rangeOther), sdv::toml::XTOMLParseException); } -TEST(TOMLLexerStatementBoundaryTests, StandardIntegerAssignment) +TEST(StatementBoundary, StandardIntegerAssignment) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_a = 10 -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -497,11 +498,11 @@ token_a = 10 toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, StandardStringAssignment) +TEST(StatementBoundary, StandardStringAssignment) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_b = "abc" -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -516,16 +517,16 @@ token_b = "abc" toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, AssignmentWithIndependentComment) +TEST(StatementBoundary, AssignmentWithIndependentComment) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_c = 30.1 # middle followed by double lines token_d = "def" -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -541,9 +542,9 @@ token_d = "def" toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, AssignmentCommentsNotPartOfIt) +TEST(StatementBoundary, AssignmentCommentsNotPartOfIt) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_c = 30.1 # middle followed by double lines @@ -554,7 +555,7 @@ token_d = "def" # before # more before token_e = [10, 20, 30] -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -570,9 +571,9 @@ token_e = [10, 20, 30] toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, ArrayAssignmentWithPreceedingComment) +TEST(StatementBoundary, ArrayAssignmentWithPreceedingComment) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_d = "def" # before @@ -581,7 +582,7 @@ token_e = [10, 20, 30] token_f = "ghi" # after # more after -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -609,14 +610,14 @@ token_f = "ghi" # after toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, AssignmentWithFollowingComment) +TEST(StatementBoundary, AssignmentWithFollowingComment) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_f = "ghi" # after # more after # belonging to next [token_g] -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -636,9 +637,9 @@ token_f = "ghi" # after toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, AssignmentWithFollowingComment2) +TEST(StatementBoundary, AssignmentWithFollowingComment2) { - std::string ssTOML = R"code( + std::string ssTOML = R"toml( # This is a separate comment with several line-breaks before. @@ -661,7 +662,7 @@ value = "this is the value text" # Comment following the value. # This is also a separate comment. # Followed by this text on the same line. -# And another text on a separate line.)code"; +# And another text on a separate line.)toml"; // Process the code toml_parser::CLexer lexerCode(ssTOML); @@ -701,15 +702,15 @@ value = "this is the value text" # Comment following the value. toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, TableAssignmentWithDedicatedComment) +TEST(StatementBoundary, TableAssignmentWithDedicatedComment) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_f = "ghi" # after # more after # belonging to next [token_g] -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -725,14 +726,14 @@ token_f = "ghi" # after toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, AssignmentWithFollowingCommentWithoutIndentation) +TEST(StatementBoundary, AssignmentWithFollowingCommentWithoutIndentation) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_h = "jkl"#after without whitespace # more after due to following empty line [token_i] -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -751,16 +752,16 @@ token_h = "jkl"#after without whitespace toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, AssignmentCommentsExcluded) +TEST(StatementBoundary, AssignmentCommentsExcluded) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_h = "jkl"#after without whitespace # more after due to following empty line [token_i] # not after token_j = 20 -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -773,13 +774,13 @@ token_j = 20 toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, SmartExtendTokenRange) +TEST(StatementBoundary, SmartExtendTokenRange) { - std::string ssCode = R"code( + std::string ssCode = R"toml( [token_i] # not after token_j = 20 -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -796,12 +797,12 @@ token_j = 20 toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, StandardAssignmentWithIndentation) +TEST(StatementBoundary, StandardAssignmentWithIndentation) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_k = 30 -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -818,11 +819,11 @@ TEST(TOMLLexerStatementBoundaryTests, StandardAssignmentWithIndentation) toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, ArrayOfStringsAssignment) +TEST(StatementBoundary, ArrayOfStringsAssignment) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_l = ["abc", "def", "ghi"] -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -846,16 +847,16 @@ TEST(TOMLLexerStatementBoundaryTests, ArrayOfStringsAssignment) toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, InlineTableAssignmentWithIndependentComment) +TEST(StatementBoundary, InlineTableAssignmentWithIndependentComment) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_m = {x = 10, str = "gfh"} # middle followed by double newlines token_n = 100 -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -885,9 +886,9 @@ TEST(TOMLLexerStatementBoundaryTests, InlineTableAssignmentWithIndependentCommen toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, IndentedAssignmentNoComments) +TEST(StatementBoundary, IndentedAssignmentNoComments) { - std::string ssCode = R"code( + std::string ssCode = R"toml( # begin followed by double newlines @@ -896,7 +897,7 @@ TEST(TOMLLexerStatementBoundaryTests, IndentedAssignmentNoComments) # before # more before token_o = 123.456 -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -913,15 +914,15 @@ TEST(TOMLLexerStatementBoundaryTests, IndentedAssignmentNoComments) toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, IndentedAssignmentWithCommentsBefore) +TEST(StatementBoundary, IndentedAssignmentWithCommentsBefore) { - std::string ssCode = R"code( + std::string ssCode = R"toml( # before # more before token_o = 123.456 -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -944,15 +945,15 @@ TEST(TOMLLexerStatementBoundaryTests, IndentedAssignmentWithCommentsBefore) toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, IndentedBooleanArrayAssignmentWithDedicatedComments) +TEST(StatementBoundary, IndentedBooleanArrayAssignmentWithDedicatedComments) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_p = [true, false] # after # more after # belonging to next token_q = "next" -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -978,15 +979,15 @@ TEST(TOMLLexerStatementBoundaryTests, IndentedBooleanArrayAssignmentWithDedicate toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, IndentedAssignmentWithDedicatedCommentsBefore) +TEST(StatementBoundary, IndentedAssignmentWithDedicatedCommentsBefore) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_p = [true, false] # after # more after # belonging to next token_q = "next" -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -1006,14 +1007,14 @@ TEST(TOMLLexerStatementBoundaryTests, IndentedAssignmentWithDedicatedCommentsBef toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, IndentedAssignmentFollowedByCommentWithoutSpace) +TEST(StatementBoundary, IndentedAssignmentFollowedByCommentWithoutSpace) { - std::string ssCode = R"code( + std::string ssCode = R"toml( token_r =987#after without whitespace # more after due to following empty line [token_s] -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -1033,13 +1034,13 @@ TEST(TOMLLexerStatementBoundaryTests, IndentedAssignmentFollowedByCommentWithout toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, IndentedTableWithoutComments) +TEST(StatementBoundary, IndentedTableWithoutComments) { - std::string ssCode = R"code( + std::string ssCode = R"toml( [token_s] # not after [token_t] -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -1053,16 +1054,16 @@ TEST(TOMLLexerStatementBoundaryTests, IndentedTableWithoutComments) toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, IndentedTableWithAndWithoutComments) +TEST(StatementBoundary, IndentedTableWithAndWithoutComments) { - std::string ssCode = R"code( + std::string ssCode = R"toml( [token_s] # not after [token_t] # comment before a table member (belongs to token_bb) token_u.token_aa.token_bb = 10 # table token_u has a table token_aa which has a value token_bb -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -1080,12 +1081,12 @@ token_u.token_aa.token_bb = 10 # table token_u has a table token_aa which ha toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, InlineTableWithParentChildAssignment) +TEST(StatementBoundary, InlineTableWithParentChildAssignment) { - std::string ssCode = R"code( + std::string ssCode = R"toml( # comment before a table member (belongs to token_bb) token_u.token_aa.token_bb = 10 # table token_u has a table token_aa which has a value token_bb -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -1116,9 +1117,9 @@ token_u.token_aa.token_bb = 10 # table token_u has a table token_aa which ha toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, ComplexInlineTableWithParentChildAssignmentOfTablesAndMultiDimensionalArrays) +TEST(StatementBoundary, ComplexInlineTableWithParentChildAssignmentOfTablesAndMultiDimensionalArrays) { - std::string ssCode = R"code( + std::string ssCode = R"toml( # Super inline table with sub-tables and arrays token_u.token_aa.token_cc = { dd = { ee = 10, ff = 11 }, # this is the comment for dd gg = [{hh = 1, ii = 2}, @@ -1128,7 +1129,7 @@ token_u.token_aa.token_cc = { dd = { ee = 10, ff = 11 }, # this is the commen # and this as well jj = [["abc", "def"], [1, 2, 3], []]} -)code"; +)toml"; // NOTE EVE 22.10.2025: the value token_u.token_aa.token_cc.jj is the last value in the table. This means that the scope of // the value included the comma before the value jj (following value gg). This has the consequence, that the comments, which @@ -1292,9 +1293,9 @@ token_u.token_aa.token_cc = { dd = { ee = 10, ff = 11 }, # this is the commen toml_parser::ETokenCategory::token_syntax_array_close})); } -TEST(TOMLLexerStatementBoundaryTests, ArrayOfTables) +TEST(StatementBoundary, ArrayOfTables) { - std::string ssCode = R"code( + std::string ssCode = R"toml( [[token_v]] token_kk = 10 token_ll = 20 @@ -1303,7 +1304,7 @@ token_mm = 30 [[token_v]] token_kk = 110 token_ll = 120 -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); @@ -1316,15 +1317,15 @@ token_ll = 120 toml_parser::ETokenCategory::token_syntax_new_line})); } -TEST(TOMLLexerStatementBoundaryTests, ParentChildTable) +TEST(StatementBoundary, ParentChildTable) { - std::string ssCode = R"code( + std::string ssCode = R"toml( [[token_v]] token_kk = 110 token_ll = 120 # Comments before (belongs ot child only) [token_x.token_nn] # Comments following (belongs to child only) -)code"; +)toml"; // Process the code toml_parser::CLexer lexerCode(ssCode); diff --git a/tests/unit_tests/trace_fifo/fifo_test.cpp b/tests/unit_tests/trace_fifo/fifo_test.cpp index 034b1c3..66a87d9 100644 --- a/tests/unit_tests/trace_fifo/fifo_test.cpp +++ b/tests/unit_tests/trace_fifo/fifo_test.cpp @@ -417,7 +417,7 @@ TEST(TraceFifoTest, Simple_Stream_Monitor) { std::stringstream sstreamWriter; CTraceFifoStreamBuffer fifoWriterStreamBuf(9999); - fifoWriterStreamBuf.Open(1000, static_cast(ETraceFifoOpenFlags::force_create)); + fifoWriterStreamBuf.Open(5000, static_cast(ETraceFifoOpenFlags::force_create)); EXPECT_TRUE(fifoWriterStreamBuf.IsOpened()); fifoWriterStreamBuf.InterceptStream(sstreamWriter); CTraceFifoReader fifoReader(9999); diff --git a/tests/unit_tests/unique_id/CMakeLists.txt b/tests/unit_tests/unique_id/CMakeLists.txt new file mode 100644 index 0000000..c61600c --- /dev/null +++ b/tests/unit_tests/unique_id/CMakeLists.txt @@ -0,0 +1,41 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 ZF Friedrichshafen AG +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Erik Verhoeven - initial API and implementation +#******************************************************************************* + +# Define project +project(UniqueIDTests VERSION 1.0 LANGUAGES CXX) + +# Define target +add_executable(UnitTest_UniqueID unique_id_test.cpp) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_libraries(UnitTest_UniqueID GTest::GTest) + if (WIN32) + target_link_libraries(UnitTest_UniqueID Ws2_32 Winmm Rpcrt4.lib) + else() + target_link_libraries(UnitTest_UniqueID ${CMAKE_DL_LIBS} rt) + endif() +else() + target_link_libraries(UnitTest_UniqueID GTest::GTest Rpcrt4.lib) +endif() + +# Add the test +add_test(NAME UnitTest_UniqueID COMMAND UnitTest_UniqueID WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + +# Execute the test +add_custom_command(TARGET UnitTest_UniqueID POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UniqueID.xml + VERBATIM +) + +# The unit-test project depends on a proper compilation of sdv components before +add_dependencies(UnitTest_UniqueID dependency_sdv_components) diff --git a/tests/unit_tests/unique_id/unique_id_test.cpp b/tests/unit_tests/unique_id/unique_id_test.cpp new file mode 100644 index 0000000..71d35ec --- /dev/null +++ b/tests/unit_tests/unique_id/unique_id_test.cpp @@ -0,0 +1,85 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Erik Verhoeven - initial API and implementation + ********************************************************************************/ + +#include +#include +#include "../../../global/process_watchdog.h" +#include "../../../global/unique_id.h" + +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain(int argc, wchar_t* argv[]) +#else +extern "C" int main(int argc, char* argv[]) +#endif +{ + CProcessWatchdog watchdog; + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +TEST(UniqueID, TestSmallID) +{ + CUniqueID generator; + + // Max 15 ids possible + std::set setUsed; + int iNumber; + for (size_t n = 0; n < 15; n++) + { + iNumber = generator.Generate(); + EXPECT_NE(iNumber, 0); + EXPECT_EQ(setUsed.find(iNumber), setUsed.end()); + setUsed.insert(iNumber); + } + + // One more ID should fail + iNumber = generator.Generate(); + EXPECT_EQ(iNumber, 0); +} + +TEST(UniqueID, TestSmallID2) +{ + CUniqueID generator; + + // Max 255 ids possible + std::set setUsed; + uint32_t uiNumber = 0; + for (size_t n = 0; n < 255; n++) + { + uiNumber = generator.Generate(); + EXPECT_NE(uiNumber, 0u); + EXPECT_EQ(setUsed.find(uiNumber), setUsed.end()); + setUsed.insert(uiNumber); + } + + // One more ID should fail + uiNumber = generator.Generate(); + EXPECT_EQ(uiNumber, 0u); +} + +TEST(UniqueID, TestLargeID) +{ + CUniqueID generator; + + // Run at the most 20000 times and check whether the number occurs multiple times + std::set setUsed; + uint64_t uiNumber; + for (size_t n = 0; n < 20000; n++) + { + uiNumber = generator.Generate(); + EXPECT_NE(uiNumber, 0u); + EXPECT_EQ(setUsed.find(uiNumber), setUsed.end()); + setUsed.insert(uiNumber); + } +} diff --git a/tests/unit_tests/unix_sockets/CMakeLists.txt b/tests/unit_tests/unix_sockets/CMakeLists.txt index 65c71a0..09d4315 100644 --- a/tests/unit_tests/unix_sockets/CMakeLists.txt +++ b/tests/unit_tests/unix_sockets/CMakeLists.txt @@ -33,7 +33,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") ${CMAKE_THREAD_LIBS_INIT} stdc++fs ${CMAKE_DL_LIBS} rt - uds_unix_sockets + uds_unix_sockets_static ) else() target_link_libraries(UnitTest_UnixSocketConnectTests @@ -41,7 +41,7 @@ else() Ws2_32 Winmm Rpcrt4.lib - uds_unix_sockets + uds_unix_sockets_static ) endif() @@ -50,13 +50,11 @@ add_test(NAME UnitTest_UnixSocketConnectTests add_dependencies(UnitTest_UnixSocketConnectTests dependency_sdv_components) -if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) add_custom_command(TARGET UnitTest_UnixSocketConnectTests POST_BUILD COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixSocketConnectTests.xml VERBATIM ) -endif() endif() \ No newline at end of file diff --git a/tests/unit_tests/unix_sockets/uds_connect_tests.cpp b/tests/unit_tests/unix_sockets/uds_connect_tests.cpp index a035fbe..3f2110c 100644 --- a/tests/unit_tests/unix_sockets/uds_connect_tests.cpp +++ b/tests/unit_tests/unix_sockets/uds_connect_tests.cpp @@ -204,6 +204,25 @@ private: // Small helper: convert server connectString to client connectString static std::string MakeClientCS(std::string cs) { + const std::string providerKey = "ConnectString = \""; + auto p = cs.find(providerKey); + if (p != std::string::npos) + { + p += providerKey.size(); + auto e = cs.find('"', p); + if (e != std::string::npos) + { + std::string inner = cs.substr(p, e - p); + const std::string fromIn = "role=server"; + const std::string toIn = "role=client"; + auto posIn = inner.find(fromIn); + if (posIn != std::string::npos) + inner.replace(posIn, fromIn.size(), toIn); + cs.replace(p, e - p, inner); + return cs; + } + } + const std::string from = "role=server"; const std::string to = "role=client"; auto pos = cs.find(from); @@ -215,6 +234,27 @@ static std::string MakeClientCS(std::string cs) static std::string ExtractPathFromCS(const std::string& cs) { + const std::string providerKey = "ConnectString = \""; + auto pProvider = cs.find(providerKey); + if (pProvider != std::string::npos) + { + pProvider += providerKey.size(); + auto eProvider = cs.find('"', pProvider); + if (eProvider != std::string::npos) + { + std::string inner = cs.substr(pProvider, eProvider - pProvider); + const std::string keyInner = "path="; + auto pInner = inner.find(keyInner); + if (pInner != std::string::npos) + { + auto startInner = pInner + keyInner.size(); + auto endInner = inner.find(';', startInner); + if (endInner == std::string::npos) endInner = inner.size(); + return inner.substr(startInner, endInner - startInner); + } + } + } + // search "path=" in connect-string const std::string key = "path="; auto p = cs.find(key); @@ -225,6 +265,21 @@ static std::string ExtractPathFromCS(const std::string& cs) return cs.substr(start, end - start); } +static std::string MakeServerCS(const std::string& cs) +{ + const std::string path = ExtractPathFromCS(cs); + if (!path.empty()) + return "proto=uds;role=server;path=" + path + ";"; + + std::string raw = cs; + const std::string from = "role=client"; + const std::string to = "role=server"; + auto pos = raw.find(from); + if (pos != std::string::npos) + raw.replace(pos, from.size(), to); + return raw; +} + static std::string MakeRandomSuffix() { std::mt19937_64 rng{std::random_device{}()}; @@ -241,7 +296,7 @@ TEST(UnixSocketIPC, Instantiate) ASSERT_TRUE(appcontrol.Startup("")); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); @@ -258,7 +313,7 @@ TEST(UnixSocketIPC, ChannelConfigString) ASSERT_TRUE(appcontrol.Startup("")); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); @@ -275,7 +330,7 @@ TEST(UnixSocketIPC, CreateRandomEndpoint) CUnixDomainSocketsChannelMgnt mgr; // Create an endpoint. - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); sdv::ipc::SChannelEndpoint sChannelEndpoint = mgr.CreateEndpoint(""); EXPECT_NE(sChannelEndpoint.pConnection, nullptr); @@ -297,7 +352,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect) // Create and initialize UDS manager CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); @@ -305,7 +360,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect) auto ep = mgr.CreateEndpoint(""); ASSERT_FALSE(ep.ssConnectString.empty()); - std::string serverCS = ep.ssConnectString; + std::string serverCS = MakeServerCS(ep.ssConnectString); std::string clientCS = MakeClientCS(serverCS); // SERVER SIDE @@ -321,7 +376,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect) // CLIENT SIDE (thread) std::atomic clientResult{0}; std::atomic allowClientDisconnect{false}; - std::thread clientThread([&]{ + sdv::core::secure_thread clientThread([&]{ sdv::TObjectPtr clientObj = mgr.Access(clientCS); if (!clientObj) { clientResult = 1; return; } auto* clientConn = clientObj.GetInterface(); @@ -354,6 +409,9 @@ TEST(UnixSocketIPC, BasicConnectDisconnect) serverConn->Disconnect(); EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected); + // Release object wrappers before shutting down the manager/framework. + serverObj.Clear(); + // Shutdown Manager / Framework EXPECT_NO_THROW(mgr.Shutdown()); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); @@ -372,7 +430,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) //UDS manager CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); @@ -380,7 +438,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) auto ep = mgr.CreateEndpoint(""); ASSERT_FALSE(ep.ssConnectString.empty()); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); // SESSION 1 @@ -397,7 +455,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) std::atomic clientResult{0}; std::atomic allowClientDisconnect{false}; - std::thread clientThread([&]{ + sdv::core::secure_thread clientThread([&]{ sdv::TObjectPtr clientObj = mgr.Access(clientCS); if (!clientObj) { clientResult = 1; return; } auto* clientConn = clientObj.GetInterface(); @@ -444,7 +502,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) std::atomic clientResult2{0}; std::atomic allowClientDisconnect2{false}; - std::thread clientThread2([&]{ + sdv::core::secure_thread clientThread2([&]{ sdv::TObjectPtr clientObj2 = mgr.Access(clientCS); if (!clientObj2) { clientResult2 = 1; return; } auto* clientConn2 = clientObj2.GetInterface(); @@ -490,7 +548,7 @@ TEST(UnixSocketIPC, OperationModeTransitions) sdv::app::CAppControl app; ASSERT_TRUE(app.Startup("")); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); // configuring and then running @@ -514,7 +572,7 @@ TEST(UnixSocketIPC, CreateEndpoint_WithConfigAndPathClamping) app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); @@ -531,7 +589,7 @@ TEST(UnixSocketIPC, CreateEndpoint_WithConfigAndPathClamping) ASSERT_FALSE(ep.ssConnectString.empty()); // Checking if endpoint is server type and has an ok path - std::string serverCS = ep.ssConnectString; + std::string serverCS = MakeServerCS(ep.ssConnectString); std::string clientCS = MakeClientCS(serverCS); auto clampedPath = ExtractPathFromCS(serverCS); ASSERT_FALSE(clampedPath.empty()); @@ -572,7 +630,7 @@ TEST(UnixSocketIPC, Access_DefaultPath_ServerClientConnect) app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); @@ -613,13 +671,13 @@ TEST(UnixSocketIPC, WaitForConnection_InfiniteWait_SlowClient) app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); auto ep = mgr.CreateEndpoint(""); ASSERT_FALSE(ep.ssConnectString.empty()); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); sdv::TObjectPtr serverObj = mgr.Access(serverCS); @@ -629,7 +687,7 @@ TEST(UnixSocketIPC, WaitForConnection_InfiniteWait_SlowClient) CUDSConnectReceiver sRcvr; ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); - std::thread delayedClient([&]{ + sdv::core::secure_thread delayedClient([&]{ std::this_thread::sleep_for(std::chrono::milliseconds(1000)); sdv::TObjectPtr clientObj = mgr.Access(clientCS); auto* clientConn = clientObj.GetInterface(); @@ -658,13 +716,13 @@ TEST(UnixSocketIPC, WaitForConnection_ZeroTimeout_BeforeAndAfter) app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); auto ep = mgr.CreateEndpoint(""); ASSERT_FALSE(ep.ssConnectString.empty()); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); sdv::TObjectPtr serverObj = mgr.Access(serverCS); @@ -700,7 +758,7 @@ ViewFilter = "Fatal")toml")); app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); @@ -734,12 +792,12 @@ TEST(UnixSocketIPC, ServerDisconnectPropagatesToClient) app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); auto ep = mgr.CreateEndpoint(""); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); sdv::TObjectPtr serverObj = mgr.Access(serverCS); @@ -776,12 +834,12 @@ TEST(UnixSocketIPC, ReconnectOnSameServerInstance) app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); auto ep = mgr.CreateEndpoint(""); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); sdv::TObjectPtr serverObj = mgr.Access(serverCS); @@ -827,14 +885,14 @@ TEST(UnixSocketIPC, DataPath_SimpleHello) app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); // Endpoint auto ep = mgr.CreateEndpoint(""); ASSERT_FALSE(ep.ssConnectString.empty()); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); // Server @@ -894,13 +952,13 @@ TEST(UnixSocketIPC, DataPath_MultiChunk_TwoBuffers) app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); auto ep = mgr.CreateEndpoint(""); ASSERT_FALSE(ep.ssConnectString.empty()); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); sdv::TObjectPtr serverObj = mgr.Access(serverCS); @@ -955,14 +1013,14 @@ TEST(UnixSocketIPC, DataPath_LargePayload_Fragmentation_Reassembly) ASSERT_TRUE(app.Startup("")); app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); // Endpoint auto ep = mgr.CreateEndpoint(""); ASSERT_FALSE(ep.ssConnectString.empty()); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); // Server @@ -1023,12 +1081,12 @@ TEST(UnixSocketIPC, DataPath_ZeroLengthChunks_ArePreserved) ASSERT_TRUE(app.Startup("")); app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); auto ep = mgr.CreateEndpoint(""); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); sdv::TObjectPtr serverObj = mgr.Access(serverCS); @@ -1086,12 +1144,12 @@ TEST(UnixSocketIPC, PeerCloseMidTransfer_ClientSeesDisconnected_AndSendMayFailOr app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - ASSERT_NO_THROW(mgr.Initialize("")); + ASSERT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); ASSERT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); auto ep = mgr.CreateEndpoint(""); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); sdv::TObjectPtr serverObj = mgr.Access(serverCS); @@ -1119,7 +1177,7 @@ TEST(UnixSocketIPC, PeerCloseMidTransfer_ClientSeesDisconnected_AndSendMayFailOr ASSERT_NE(pSend, nullptr); std::atomic sendResult{true}; - std::thread t([&]{ + sdv::core::secure_thread t([&]{ sendResult.store(pSend->SendData(seq)); }); @@ -1148,7 +1206,7 @@ TEST(UnixSocketIPC, ClientCancelConnect_NoServer_CleansUpPromptly) ASSERT_TRUE(app.Startup("")); app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); @@ -1181,12 +1239,12 @@ TEST(UnixSocketIPC, ServerStartThenImmediateDisconnect_NoClient) ViewFilter = "Fatal")toml")); app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); auto ep = mgr.CreateEndpoint(""); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); sdv::TObjectPtr serverObj = mgr.Access(serverCS); ASSERT_TRUE(serverObj); @@ -1212,12 +1270,12 @@ TEST(UnixSocketIPC, CallbackThrowsInSetConnectState_DoesNotCrashTransport) ASSERT_TRUE(app.Startup("")); app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); auto ep = mgr.CreateEndpoint(""); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); sdv::TObjectPtr serverObj = mgr.Access(serverCS); @@ -1253,13 +1311,13 @@ TEST(UnixSocketIPC, RegisterStateEventCallback_MultipleCallbacksReceiveState) app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - ASSERT_NO_THROW(mgr.Initialize("")); + ASSERT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); ASSERT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); // --- Setup server endpoint --- auto ep = mgr.CreateEndpoint(""); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); sdv::TObjectPtr serverObj = mgr.Access(serverCS); @@ -1312,13 +1370,13 @@ TEST(UnixSocketIPC, UnregisterStateEventCallback_RemovedListenerStopsReceiving) app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; - ASSERT_NO_THROW(mgr.Initialize("")); + ASSERT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); ASSERT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); // Endpoint auto ep = mgr.CreateEndpoint(""); - const std::string serverCS = ep.ssConnectString; + const std::string serverCS = MakeServerCS(ep.ssConnectString); const std::string clientCS = MakeClientCS(serverCS); // Server @@ -1376,4 +1434,60 @@ TEST(UnixSocketIPC, UnregisterStateEventCallback_RemovedListenerStopsReceiving) app.Shutdown(); } +// Reconnect race regression: a second AsyncConnect while connect worker is still initializing +// must return quickly and must not deadlock by joining an in-progress worker. +TEST(UnixSocketIPC, AsyncConnect_DoubleCallWhileInitializing_NoDeadlock) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixDomainSocketsChannelMgnt mgr; + ASSERT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); + ASSERT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + ASSERT_FALSE(ep.ssConnectString.empty()); + + const std::string serverCS = MakeServerCS(ep.ssConnectString); + const std::string clientCS = MakeClientCS(serverCS); + + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + ASSERT_TRUE(clientObj); + auto* clientConn = clientObj.GetInterface(); + ASSERT_NE(clientConn, nullptr); + + CUDSConnectReceiver sRcvr; + CUDSConnectReceiver cRcvr; + + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + auto t0 = std::chrono::steady_clock::now(); + bool secondAsync = serverConn->AsyncConnect(&sRcvr); + auto elapsedMs = std::chrono::duration_cast( + std::chrono::steady_clock::now() - t0).count(); + + EXPECT_FALSE(secondAsync); + EXPECT_LT(elapsedMs, 200); + + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + EXPECT_TRUE(serverConn->WaitForConnection(5000)); + EXPECT_TRUE(clientConn->WaitForConnection(5000)); + + // Once connected, repeated AsyncConnect should be a no-op success. + EXPECT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + clientConn->Disconnect(); + serverConn->Disconnect(); + + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + #endif // defined __unix__ \ No newline at end of file diff --git a/tests/unit_tests/unix_tunnel/CMakeLists.txt b/tests/unit_tests/unix_tunnel/CMakeLists.txt index 2ca7a0d..c51ae33 100644 --- a/tests/unit_tests/unix_tunnel/CMakeLists.txt +++ b/tests/unit_tests/unix_tunnel/CMakeLists.txt @@ -35,7 +35,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") ${CMAKE_DL_LIBS} rt uds_unix_tunnel - uds_unix_sockets + uds_unix_sockets_static ) else() target_link_libraries(UnitTest_UnixTunnelConnectTests @@ -43,7 +43,7 @@ else() Ws2_32 Winmm Rpcrt4.lib uds_unix_tunnel - uds_unix_sockets + uds_unix_sockets_static ) endif() @@ -52,14 +52,12 @@ add_test(NAME UnitTest_UnixTunnelConnectTests add_dependencies(UnitTest_UnixTunnelConnectTests dependency_sdv_components) -if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) add_custom_command(TARGET UnitTest_UnixTunnelConnectTests POST_BUILD COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixTunnelConnectTests.xml VERBATIM ) -endif() add_executable(UnitTest_UnixTunnelChannelMgntTests unix_tunnel_channel_mgnt_tests.cpp @@ -77,7 +75,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") ${CMAKE_DL_LIBS} rt uds_unix_tunnel - uds_unix_sockets + uds_unix_sockets_static ) else() target_link_libraries(UnitTest_UnixTunnelChannelMgntTests @@ -86,7 +84,7 @@ else() Winmm Rpcrt4.lib uds_unix_tunnel - uds_unix_sockets + uds_unix_sockets_static ) endif() @@ -95,13 +93,11 @@ add_test(NAME UnitTest_UnixTunnelChannelMgntTests add_dependencies(UnitTest_UnixTunnelChannelMgntTests dependency_sdv_components) -if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) add_custom_command(TARGET UnitTest_UnixTunnelChannelMgntTests POST_BUILD COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixTunnelChannelMgntTests.xml VERBATIM ) -endif() endif() \ No newline at end of file diff --git a/tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp b/tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp index 9568e0c..3547b15 100644 --- a/tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp +++ b/tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp @@ -12,6 +12,10 @@ #include #include #include +#include +#include +#include +#include #include "../sdv_services/uds_unix_tunnel/channel_mgnt.h" #include "../sdv_services/uds_unix_tunnel/connection.h" @@ -81,6 +85,109 @@ private: sdv::sequence> m_lastData; bool m_received{ false }; }; +// Helper namespace +namespace tunnel_utils +{ + +inline std::string Expand(const std::string& in) +{ + // Simple expand for $HOME / $TMPDIR etc. + std::string out = in; + + auto replace_env = [&](const std::string& key, const char* env) + { + const char* val = std::getenv(env); + if (!val) return; + + auto pos = out.find(key); + if (pos != std::string::npos) + { + out.replace(pos, key.size(), val); + } + }; + + replace_env("$HOME", "HOME"); + replace_env("$TMPDIR", "TMPDIR"); + + return out; +} + +inline void EnsureParentDir(const std::string& full) +{ + auto p = full.find_last_of('/'); + if (p == std::string::npos) + return; + + std::filesystem::create_directories(full.substr(0, p)); +} + +inline std::string MakeShortUdsPath(const char* name) +{ + std::string base = "/tmp/sdv/"; + + EnsureParentDir(base); + + return base + name; +} + +inline std::string RandomHex() +{ + std::mt19937_64 r{std::random_device{}()}; + std::uniform_int_distribution d; + std::ostringstream oss; + oss << std::hex << d(r); + return oss.str(); +} + +inline std::string Unique(const char* prefix) +{ + return MakeShortUdsPath((std::string(prefix) + "_" + RandomHex() + ".sock").c_str()); +} + +inline std::string UniqueTunnel() +{ + return "t_" + RandomHex(); +} + +inline void SpinUntilServerArmed(sdv::ipc::IConnect* server) +{ + using namespace std::chrono; + const auto deadline = steady_clock::now() + milliseconds(500); + while (server->GetConnectState() == sdv::ipc::EConnectState::uninitialized && + steady_clock::now() < deadline) + { + std::this_thread::sleep_for(milliseconds(2)); + } +} + +} // namespace tunnel_utils + +using namespace tunnel_utils; + +struct EndpointClientPair +{ + sdv::ipc::IConnect* server = nullptr; // from ep.pConnection + sdv::TObjectPtr clientObj; + sdv::ipc::IConnect* client = nullptr; +}; + +static EndpointClientPair CreateEndpointClientPair( + CUnixTunnelChannelMgnt& mgr, + const sdv::ipc::SChannelEndpoint& ep) +{ + EndpointClientPair out; + + out.server = ep.pConnection + ? ep.pConnection->GetInterface() + : nullptr; + + out.clientObj = mgr.Access(ep.ssConnectString); + out.client = out.clientObj + ? out.clientObj.GetInterface() + : nullptr; + + return out; +} //Manager instantiate + lifecycle TEST(UnixTunnelChannelMgnt, InstantiateAndLifecycle) @@ -89,7 +196,7 @@ TEST(UnixTunnelChannelMgnt, InstantiateAndLifecycle) ASSERT_TRUE(app.Startup("")); CUnixTunnelChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::configuring)); @@ -105,6 +212,7 @@ TEST(UnixTunnelChannelMgnt, InstantiateAndLifecycle) } // CreateEndpoint -> Access(server/client) -> AsyncConnect -> Wait -> Disconnect + TEST(UnixTunnelChannelMgnt, BasicConnectDisconnect) { sdv::app::CAppControl app; @@ -112,63 +220,41 @@ TEST(UnixTunnelChannelMgnt, BasicConnectDisconnect) app.SetRunningMode(); CUnixTunnelChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); - EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); - ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + mgr.Initialize(sdv::SObjectInfo()); + mgr.SetOperationMode(sdv::EOperationMode::running); - // Create a tunnel endpoint (server) - auto ep = mgr.CreateEndpoint(""); - ASSERT_NE(ep.pConnection, nullptr); + const std::string tunnel = "t_" + RandomHex(); + const std::string cs = + "proto=tunnel;path=" + Unique("tunnel_mgr_basic") + + ";tunnel=" + tunnel + ";"; + + auto ep = mgr.CreateEndpoint(cs); ASSERT_FALSE(ep.ssConnectString.empty()); - const std::string serverCS = ep.ssConnectString; - // Convert to client by role=client - std::string clientCS = serverCS; - { - const std::string from = "role=server"; - const std::string to = "role=client"; - auto pos = clientCS.find(from); - if (pos != std::string::npos) - clientCS.replace(pos, from.size(), to); - } + auto pair = CreateEndpointClientPair(mgr, ep); - // SERVER - sdv::TObjectPtr serverObj = mgr.Access(serverCS); - ASSERT_TRUE(serverObj); - auto* serverConn = serverObj.GetInterface(); - ASSERT_NE(serverConn, nullptr); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); - CTunnelMgrTestReceiver sRcvr; - ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + CTunnelMgrTestReceiver sr, cr; - // CLIENT (thread) - std::atomic clientResult{0}; - std::thread clientThread([&]{ - sdv::TObjectPtr clientObj = mgr.Access(clientCS); - if (!clientObj) { clientResult = 1; return; } - auto* clientConn = clientObj.GetInterface(); - if (!clientConn) { clientResult = 2; return; } - CTunnelMgrTestReceiver cRcvr; - if (!clientConn->AsyncConnect(&cRcvr)) { clientResult = 3; return; } - if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; } - if (clientConn->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult = 5; return; } - clientConn->Disconnect(); - clientResult = 0; - }); + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); - EXPECT_TRUE(serverConn->WaitForConnection(5000)); - EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected); + pair.client->AsyncConnect(&cr); - clientThread.join(); - EXPECT_EQ(clientResult.load(), 0); + EXPECT_TRUE(pair.server->WaitForConnection(5000)); + EXPECT_TRUE(pair.client->WaitForConnection(5000)); - serverConn->Disconnect(); + pair.client->Disconnect(); + pair.server->Disconnect(); - EXPECT_NO_THROW(mgr.Shutdown()); + mgr.Shutdown(); app.Shutdown(); } // Data path: "hello" via channel manager (using proto=tunnel) +// Simple hello (header stripped) TEST(UnixTunnelChannelMgnt, DataPath_SimpleHello_ViaManager) { sdv::app::CAppControl app; @@ -176,65 +262,57 @@ TEST(UnixTunnelChannelMgnt, DataPath_SimpleHello_ViaManager) app.SetRunningMode(); CUnixTunnelChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); - EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); - ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + mgr.Initialize(sdv::SObjectInfo()); + mgr.SetOperationMode(sdv::EOperationMode::running); - auto ep = mgr.CreateEndpoint(""); + const std::string tunnel = "t_" + RandomHex(); + const std::string cs = + "proto=tunnel;path=" + Unique("hello_mgr") + + ";tunnel=" + tunnel + ";"; + + auto ep = mgr.CreateEndpoint(cs); ASSERT_FALSE(ep.ssConnectString.empty()); - const std::string serverCS = ep.ssConnectString; - std::string clientCS = serverCS; - { - const std::string from = "role=server"; - const std::string to = "role=client"; - auto pos = clientCS.find(from); - if (pos != std::string::npos) - clientCS.replace(pos, from.size(), to); - } + auto pair = CreateEndpointClientPair(mgr, ep); - // Server - sdv::TObjectPtr serverObj = mgr.Access(serverCS); - ASSERT_TRUE(serverObj); - auto* serverConn = serverObj.GetInterface(); - ASSERT_NE(serverConn, nullptr); - CTunnelMgrTestReceiver sRcvr; - ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); - // Client - sdv::TObjectPtr clientObj = mgr.Access(clientCS); - ASSERT_TRUE(clientObj); - auto* clientConn = clientObj.GetInterface(); - ASSERT_NE(clientConn, nullptr); - CTunnelMgrTestReceiver cRcvr; - ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + CTunnelMgrTestReceiver sr, cr; - EXPECT_TRUE(serverConn->WaitForConnection(5000)); - EXPECT_TRUE(clientConn->WaitForConnection(5000)); + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); // Payload "hello" sdv::pointer p; p.resize(5); std::memcpy(p.get(), "hello", 5); + sdv::sequence> seq; seq.push_back(p); - auto* pSend = dynamic_cast(clientConn); - ASSERT_NE(pSend, nullptr); - EXPECT_TRUE(pSend->SendData(seq)); + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + EXPECT_TRUE(sender->SendData(seq)); - EXPECT_TRUE(sRcvr.WaitForData(3000)); - sdv::sequence> recv = sRcvr.GetLastData(); + ASSERT_TRUE(sr.WaitForData(3000)); + auto recv = sr.GetLastData(); ASSERT_EQ(recv.size(), 1u); ASSERT_EQ(recv[0].size(), 5u); EXPECT_EQ(std::memcmp(recv[0].get(), "hello", 5), 0); - clientConn->Disconnect(); - serverConn->Disconnect(); + pair.client->Disconnect(); + pair.server->Disconnect(); - EXPECT_NO_THROW(mgr.Shutdown()); + mgr.Shutdown(); app.Shutdown(); } + #endif // defined(__unix__) diff --git a/tests/unit_tests/win_sockets/CMakeLists.txt b/tests/unit_tests/win_sockets/CMakeLists.txt index 4e38239..9e25a34 100644 --- a/tests/unit_tests/win_sockets/CMakeLists.txt +++ b/tests/unit_tests/win_sockets/CMakeLists.txt @@ -44,13 +44,11 @@ add_test(NAME UnitTest_WinSocketConnectTests add_dependencies(UnitTest_WinSocketConnectTests dependency_sdv_components) -if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) add_custom_command(TARGET UnitTest_WinSocketConnectTests POST_BUILD COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinSocketConnectTests.xml VERBATIM ) -endif() endif() \ No newline at end of file diff --git a/tests/unit_tests/win_sockets/win_connect_tests.cpp b/tests/unit_tests/win_sockets/win_connect_tests.cpp index d9d24d6..14f8879 100644 --- a/tests/unit_tests/win_sockets/win_connect_tests.cpp +++ b/tests/unit_tests/win_sockets/win_connect_tests.cpp @@ -186,16 +186,25 @@ struct ServerClient sdv::ipc::IConnect* client = nullptr; }; -static ServerClient CreatePair(CSocketsChannelMgnt& mgr, const std::string& cs) +static ServerClient CreatePair( + CSocketsChannelMgnt& mgr, + const sdv::ipc::SChannelEndpoint& ep) { ServerClient out; - // Server - out.serverObj = mgr.Access(cs); - out.server = out.serverObj ? out.serverObj.GetInterface() : nullptr; - // Client - out.clientObj = mgr.Access(cs); - out.client = out.clientObj ? out.clientObj.GetInterface() : nullptr; + // Server: use the connection returned by CreateEndpoint(). + // Do not create another server with Access(... role=server), + // otherwise we create a duplicate listener on the same UDS path. + out.serverObj = ep.pConnection; + out.server = out.serverObj + ? out.serverObj.GetInterface() + : nullptr; + + // Client: connect through the endpoint connect string. + out.clientObj = mgr.Access(ep.ssConnectString); + out.client = out.clientObj + ? out.clientObj.GetInterface() + : nullptr; return out; } @@ -210,7 +219,7 @@ TEST(WindowsAFUnixIPC, Instantiate) ASSERT_TRUE(app.Startup("")); CSocketsChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgr.Shutdown()); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); @@ -226,7 +235,7 @@ TEST(WindowsAFUnixIPC, BasicConnectDisconnect) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string path = UniqueUds("basic"); @@ -237,7 +246,7 @@ TEST(WindowsAFUnixIPC, BasicConnectDisconnect) ASSERT_FALSE(ep.ssConnectString.empty()); // Create server + client objects - auto pair = CreatePair(mgr, ep.ssConnectString); + auto pair = CreatePair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -268,12 +277,12 @@ TEST(WindowsAFUnixIPC, DataPath_SimpleHello) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string cs = std::string("proto=uds;path=") + UniqueUds("hello") + ";"; auto ep = mgr.CreateEndpoint(cs); - auto pair = CreatePair(mgr, ep.ssConnectString); + auto pair = CreatePair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -322,13 +331,13 @@ TEST(WindowsAFUnixIPC, ServerDisconnectPropagates) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string cs = std::string("proto=uds;path=") + UniqueUds("disc") + ";"; auto ep = mgr.CreateEndpoint(cs); - auto pair = CreatePair(mgr, ep.ssConnectString); + auto pair = CreatePair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -360,13 +369,13 @@ TEST(WindowsAFUnixIPC, DataPath_MultiChunk) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string cs = std::string("proto=uds;path=") + UniqueUds("mc") + ";"; auto ep = mgr.CreateEndpoint(cs); - auto pair = CreatePair(mgr, ep.ssConnectString); + auto pair = CreatePair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -415,13 +424,13 @@ TEST(WindowsAFUnixIPC, DataPath_LargePayloadFragmentation) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string cs = std::string("proto=uds;path=") + UniqueUds("big") + ";"; auto ep = mgr.CreateEndpoint(cs); - auto pair = CreatePair(mgr, ep.ssConnectString); + auto pair = CreatePair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -471,12 +480,12 @@ TEST(WindowsAFUnixIPC, DataPath_ZeroLengthChunks) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string cs = std::string("proto=uds;path=") + UniqueUds("zlen") + ";"; auto ep = mgr.CreateEndpoint(cs); - auto pair = CreatePair(mgr, ep.ssConnectString); + auto pair = CreatePair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -530,7 +539,7 @@ TEST(WindowsAFUnixIPC, OperationModeTransitions) ASSERT_TRUE(app.Startup("")); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); mgr.SetOperationMode(sdv::EOperationMode::configuring); @@ -552,7 +561,7 @@ TEST(WindowsAFUnixIPC, ReconnectAfterDisconnect_SamePath) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string udsPath = MakeShortUdsPath(("vapi_win_reconn_" + RandomHex() + ".sock").c_str()); @@ -562,7 +571,7 @@ TEST(WindowsAFUnixIPC, ReconnectAfterDisconnect_SamePath) // ----- Session 1 ----- { auto ep = mgr.CreateEndpoint(cs); - auto pair = CreatePair(mgr, ep.ssConnectString); + auto pair = CreatePair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -572,7 +581,7 @@ TEST(WindowsAFUnixIPC, ReconnectAfterDisconnect_SamePath) std::atomic clientRes{0}; - std::thread ct([&]{ + sdv::core::secure_thread ct([&]{ if (!pair.clientObj) { clientRes = 1; return; } auto* c = pair.client; if (!c) { clientRes = 2; return; } @@ -594,7 +603,7 @@ TEST(WindowsAFUnixIPC, ReconnectAfterDisconnect_SamePath) // ----- Session 2 ----- { auto ep = mgr.CreateEndpoint(cs); - auto pair = CreatePair(mgr, ep.ssConnectString); + auto pair = CreatePair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -604,7 +613,7 @@ TEST(WindowsAFUnixIPC, ReconnectAfterDisconnect_SamePath) std::atomic clientRes{0}; - std::thread ct([&]{ + sdv::core::secure_thread ct([&]{ if (!pair.clientObj) { clientRes = 1; return; } auto* c = pair.client; if (!c) { clientRes = 2; return; } @@ -633,7 +642,7 @@ TEST(WindowsAFUnixIPC, WaitForConnection_InfiniteWait_SlowClient) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string cs = std::string("proto=uds;path=") + MakeShortUdsPath(("vapi_win_slow_" + RandomHex() + ".sock").c_str()) + ";"; @@ -647,7 +656,7 @@ TEST(WindowsAFUnixIPC, WaitForConnection_InfiniteWait_SlowClient) server->AsyncConnect(&sr); SpinUntilServerArmed(server); - std::thread delayedClient([&]{ + sdv::core::secure_thread delayedClient([&]{ std::this_thread::sleep_for(std::chrono::milliseconds(200)); sdv::TObjectPtr cObj = mgr.Access(ep.ssConnectString); auto* client = cObj.GetInterface(); @@ -673,7 +682,7 @@ TEST(WindowsAFUnixIPC, WaitForConnection_ZeroTimeout_BeforeAndAfter) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string cs = @@ -718,7 +727,7 @@ ViewFilter = "Fatal")toml")); app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string raw = MakeShortUdsPath(("vapi_win_nosrv_" + RandomHex() + ".sock").c_str()); @@ -743,7 +752,11 @@ ViewFilter = "Fatal")toml")); EXPECT_FALSE(client->WaitForConnection(1500)); std::this_thread::sleep_for(std::chrono::milliseconds(800)); - EXPECT_EQ(client->GetConnectState(), sdv::ipc::EConnectState::connection_error); + const auto finalState = client->GetConnectState(); + EXPECT_TRUE(finalState == sdv::ipc::EConnectState::connection_error || + finalState == sdv::ipc::EConnectState::disconnected || + finalState == sdv::ipc::EConnectState::initializing) + << "Expected timeout-related terminal or pending state without server."; client->Disconnect(); mgr.Shutdown(); @@ -758,7 +771,7 @@ TEST(WindowsAFUnixIPC, PeerCloseMidTransfer_ClientDetectsDisconnect) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string cs = @@ -767,7 +780,7 @@ TEST(WindowsAFUnixIPC, PeerCloseMidTransfer_ClientDetectsDisconnect) ";"; auto ep = mgr.CreateEndpoint(cs); - auto pair = CreatePair(mgr, ep.ssConnectString); + auto pair = CreatePair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -791,7 +804,7 @@ TEST(WindowsAFUnixIPC, PeerCloseMidTransfer_ClientDetectsDisconnect) ASSERT_NE(sender, nullptr); std::atomic sendOk{true}; - std::thread t([&]{ + sdv::core::secure_thread t([&]{ sendOk.store(sender->SendData(seq)); }); @@ -816,7 +829,7 @@ ViewFilter = "Fatal")toml")); app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string raw = MakeShortUdsPath(("vapi_win_cancel_" + RandomHex() + ".sock").c_str()); @@ -855,7 +868,7 @@ ViewFilter = "Fatal")toml")); app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string cs = @@ -889,13 +902,12 @@ TEST(WindowsAFUnixIPC, UnregisterStateEventCallback_SingleListenerSemantics) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); const std::string cs = std::string("proto=uds;path=") + - MakeShortUdsPath(("vapi_win_cb_" + RandomHex() + ".sock").c_str()) + - ";"; + MakeShortUdsPath(("vapi_win_cb_" + RandomHex() + ".sock").c_str()) + ";"; auto ep = mgr.CreateEndpoint(cs); @@ -935,6 +947,55 @@ TEST(WindowsAFUnixIPC, UnregisterStateEventCallback_SingleListenerSemantics) app.Shutdown(); } +// Reconnect race regression: second AsyncConnect during initializing must fail fast, +// not block by joining an in-progress connect worker. +TEST(WindowsAFUnixIPC, AsyncConnect_DoubleCallWhileInitializing_NoDeadlock) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsChannelMgnt mgr; + mgr.Initialize(sdv::SObjectInfo()); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = + std::string("proto=uds;path=") + + MakeShortUdsPath(("vapi_win_race_" + RandomHex() + ".sock").c_str()) + ";"; + + auto ep = mgr.CreateEndpoint(cs); + ASSERT_FALSE(ep.ssConnectString.empty()); + + auto pair = CreatePair(mgr, ep); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTestReceiver sr, cr; + + ASSERT_TRUE(pair.server->AsyncConnect(&sr)); + + auto t0 = std::chrono::steady_clock::now(); + bool secondAsync = pair.server->AsyncConnect(&sr); + auto elapsedMs = std::chrono::duration_cast( + std::chrono::steady_clock::now() - t0).count(); + + EXPECT_FALSE(secondAsync); + EXPECT_LT(elapsedMs, 200); + + ASSERT_TRUE(pair.client->AsyncConnect(&cr)); + EXPECT_TRUE(pair.server->WaitForConnection(5000)); + EXPECT_TRUE(pair.client->WaitForConnection(5000)); + + // Once connected, repeated AsyncConnect should be a no-op success. + EXPECT_TRUE(pair.server->AsyncConnect(&sr)); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + // CreateEndpoint with very long path → must be normalized to basename TEST(WindowsAFUnixIPC, CreateEndpoint_LongInputPath_Normalized) { @@ -943,7 +1004,7 @@ TEST(WindowsAFUnixIPC, CreateEndpoint_LongInputPath_Normalized) app.SetRunningMode(); CSocketsChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); std::string longName(160, 'A'); diff --git a/tests/unit_tests/win_tunnel/CMakeLists.txt b/tests/unit_tests/win_tunnel/CMakeLists.txt index 1263e5b..1582258 100644 --- a/tests/unit_tests/win_tunnel/CMakeLists.txt +++ b/tests/unit_tests/win_tunnel/CMakeLists.txt @@ -47,14 +47,12 @@ add_test(NAME UnitTest_WinTunnelConnectTests add_dependencies(UnitTest_WinTunnelConnectTests dependency_sdv_components) -if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) add_custom_command(TARGET UnitTest_WinTunnelConnectTests POST_BUILD COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelConnectTests.xml VERBATIM ) -endif() add_executable(UnitTest_WinTunnelChannelMgntTests win_tunnel_channel_mgnt_tests.cpp @@ -84,14 +82,12 @@ add_test(NAME UnitTest_WinTunnelChannelMgntTests add_dependencies(UnitTest_WinTunnelChannelMgntTests dependency_sdv_components) -if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) add_custom_command(TARGET UnitTest_WinTunnelChannelMgntTests POST_BUILD COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelChannelMgntTests.xml VERBATIM ) -endif() # Add negative/edge case tests add_executable(UnitTest_WinTunnelNegativeEdgeTests @@ -122,13 +118,11 @@ add_test(NAME UnitTest_WinTunnelNegativeEdgeTests add_dependencies(UnitTest_WinTunnelNegativeEdgeTests dependency_sdv_components) -if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) add_custom_command(TARGET UnitTest_WinTunnelNegativeEdgeTests POST_BUILD COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelNegativeEdgeTests.xml VERBATIM ) -endif() endif() diff --git a/tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp b/tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp index 6f0c638..bbd14d0 100644 --- a/tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp +++ b/tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp @@ -76,6 +76,12 @@ inline std::string Unique(const char* prefix) return MakeShortUdsPath((std::string(prefix) + "_" + RandomHex() + ".sock").c_str()); } +inline std::string UniqueTunnel() +{ + return "t_" + RandomHex(); +} + + inline void SpinUntilServerArmed(sdv::ipc::IConnect* server) { using namespace std::chrono; @@ -126,8 +132,10 @@ public: bool WaitForData(uint32_t ms = 2000) { std::unique_lock lk(m_mtx); - return m_cv.wait_for(lk, std::chrono::milliseconds(ms), - [&]{ return m_has; }); + bool bRet = m_has || m_cv.wait_for(lk, std::chrono::milliseconds(ms), + [&]{ return m_has ? true : false; }); + if (bRet) m_has = false; + return bRet; } sdv::sequence> Data() @@ -141,34 +149,35 @@ private: std::condition_variable m_cv; sdv::ipc::EConnectState m_state{sdv::ipc::EConnectState::uninitialized}; sdv::sequence> m_last; - bool m_has{false}; + std::atomic_bool m_has{false}; }; -// Helper to create server + client -struct TunnelPair +struct EndpointClientPair { - sdv::TObjectPtr serverObj; - sdv::ipc::IConnect* server = nullptr; - + sdv::ipc::IConnect* server = nullptr; // from ep.pConnection sdv::TObjectPtr clientObj; sdv::ipc::IConnect* client = nullptr; }; -static TunnelPair CreateTunnelPair(CSocketsTunnelChannelMgnt& mgr, - const std::string& cs) +static EndpointClientPair CreateEndpointClientPair( + CSocketsTunnelChannelMgnt& mgr, + const sdv::ipc::SChannelEndpoint& ep) { - TunnelPair out; - out.serverObj = mgr.Access(cs); - out.server = out.serverObj ? - out.serverObj.GetInterface() : nullptr; + EndpointClientPair out; - out.clientObj = mgr.Access(cs); - out.client = out.clientObj ? - out.clientObj.GetInterface() : nullptr; + out.server = ep.pConnection + ? ep.pConnection->GetInterface() + : nullptr; + + out.clientObj = mgr.Access(ep.ssConnectString); + out.client = out.clientObj + ? out.clientObj.GetInterface() + : nullptr; return out; } + // TESTS // Manager instantiate + lifecycle TEST(WinTunnelChannelMgnt, InstantiateAndLifecycle) @@ -178,7 +187,7 @@ TEST(WinTunnelChannelMgnt, InstantiateAndLifecycle) CSocketsTunnelChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); mgr.SetOperationMode(sdv::EOperationMode::configuring); @@ -194,23 +203,24 @@ TEST(WinTunnelChannelMgnt, InstantiateAndLifecycle) } // Basic connect/disconnect using manager (server + client) + TEST(WinTunnelChannelMgnt, BasicConnectDisconnect) { sdv::app::CAppControl app; ASSERT_TRUE(app.Startup("")); - app.SetRunningMode(); + CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - const std::string uds = Unique("tunnel_mgr_basic"); - const std::string cs = "proto=tunnel;path=" + uds + ";"; + const std::string tunnel = "t_" + RandomHex(); + const std::string cs = "proto=tunnel;path=" + Unique("tunnel_mgr_basic") + ";tunnel=" + tunnel + ";"; auto ep = mgr.CreateEndpoint(cs); ASSERT_FALSE(ep.ssConnectString.empty()); - auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + auto pair = CreateEndpointClientPair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -232,6 +242,7 @@ TEST(WinTunnelChannelMgnt, BasicConnectDisconnect) } // Simple hello (header stripped) + TEST(WinTunnelChannelMgnt, DataPath_SimpleHello_ViaManager) { sdv::app::CAppControl app; @@ -239,14 +250,16 @@ TEST(WinTunnelChannelMgnt, DataPath_SimpleHello_ViaManager) app.SetRunningMode(); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - const std::string cs = "proto=tunnel;path=" + Unique("hello_mgr") + ";"; + const std::string tunnel = "t_" + RandomHex(); + const std::string cs = "proto=tunnel;path=" + Unique("hello_mgr") + ";tunnel=" + tunnel + ";"; auto ep = mgr.CreateEndpoint(cs); - auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + ASSERT_FALSE(ep.ssConnectString.empty()); + auto pair = CreateEndpointClientPair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -256,27 +269,26 @@ TEST(WinTunnelChannelMgnt, DataPath_SimpleHello_ViaManager) SpinUntilServerArmed(pair.server); pair.client->AsyncConnect(&cr); - ASSERT_TRUE(pair.server->WaitForConnection(5000)); ASSERT_TRUE(pair.client->WaitForConnection(5000)); - sdv::pointer msg; - msg.resize(5); - memcpy(msg.get(), "hello", 5); + sdv::pointer p; + p.resize(5); + std::memcpy(p.get(), "hello", 5); sdv::sequence> seq; - seq.push_back(msg); + seq.push_back(p); auto* sender = dynamic_cast(pair.client); ASSERT_NE(sender, nullptr); - EXPECT_TRUE(sender->SendData(seq)); - ASSERT_TRUE(sr.WaitForData(3000)); + ASSERT_TRUE(sr.WaitForData(3000)); auto recv = sr.Data(); + ASSERT_EQ(recv.size(), 1u); ASSERT_EQ(recv[0].size(), 5u); - EXPECT_EQ(memcmp(recv[0].get(), "hello", 5), 0); + EXPECT_EQ(std::memcmp(recv[0].get(), "hello", 5), 0); pair.client->Disconnect(); pair.server->Disconnect(); @@ -286,6 +298,7 @@ TEST(WinTunnelChannelMgnt, DataPath_SimpleHello_ViaManager) } // Multi-chunk + TEST(WinTunnelChannelMgnt, DataPath_MultiChunk_ViaManager) { sdv::app::CAppControl app; @@ -293,19 +306,20 @@ TEST(WinTunnelChannelMgnt, DataPath_MultiChunk_ViaManager) app.SetRunningMode(); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - const std::string cs = "proto=tunnel;path=" + Unique("mc_mgr") + ";"; + const std::string tunnel = "t_" + RandomHex(); + const std::string cs = "proto=tunnel;path=" + Unique("mc_mgr") + ";tunnel=" + tunnel + ";"; auto ep = mgr.CreateEndpoint(cs); - auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + ASSERT_FALSE(ep.ssConnectString.empty()); + auto pair = CreateEndpointClientPair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); CTunnelMgrTestReceiver sr, cr; - pair.server->AsyncConnect(&sr); SpinUntilServerArmed(pair.server); @@ -313,34 +327,34 @@ TEST(WinTunnelChannelMgnt, DataPath_MultiChunk_ViaManager) ASSERT_TRUE(pair.server->WaitForConnection(5000)); ASSERT_TRUE(pair.client->WaitForConnection(5000)); - sdv::pointer a, b; - a.resize(3); - memcpy(a.get(), "sdv", 3); - b.resize(9); - memcpy(b.get(), "framework", 9); + sdv::pointer p1, p2; + p1.resize(3); std::memcpy(p1.get(), "sdv", 3); + p2.resize(9); std::memcpy(p2.get(), "framework", 9); sdv::sequence> seq; - seq.push_back(a); - seq.push_back(b); + seq.push_back(p1); + seq.push_back(p2); auto* sender = dynamic_cast(pair.client); ASSERT_NE(sender, nullptr); - EXPECT_TRUE(sender->SendData(seq)); - ASSERT_TRUE(sr.WaitForData(3000)); + ASSERT_TRUE(sr.WaitForData(3000)); auto recv = sr.Data(); + ASSERT_EQ(recv.size(), 2u); - EXPECT_EQ(memcmp(recv[0].get(), "sdv", 3), 0); - EXPECT_EQ(memcmp(recv[1].get(), "framework", 9), 0); + EXPECT_EQ(std::memcmp(recv[0].get(), "sdv", 3), 0); + EXPECT_EQ(std::memcmp(recv[1].get(), "framework", 9), 0); pair.client->Disconnect(); pair.server->Disconnect(); + mgr.Shutdown(); app.Shutdown(); } // Header stripping invariant + TEST(WinTunnelChannelMgnt, HeaderStrippedInvariant) { sdv::app::CAppControl app; @@ -348,13 +362,16 @@ TEST(WinTunnelChannelMgnt, HeaderStrippedInvariant) app.SetRunningMode(); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - const std::string cs ="proto=tunnel;path=" + Unique("hdr_mgr") + ";"; + const std::string tunnel = "t_" + RandomHex(); + const std::string cs = "proto=tunnel;path=" + Unique("hdr_mgr") + ";tunnel=" + tunnel + ";"; auto ep = mgr.CreateEndpoint(cs); - auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + ASSERT_FALSE(ep.ssConnectString.empty()); + + auto pair = CreateEndpointClientPair(mgr, ep); ASSERT_NE(pair.server, nullptr); ASSERT_NE(pair.client, nullptr); @@ -364,33 +381,33 @@ TEST(WinTunnelChannelMgnt, HeaderStrippedInvariant) SpinUntilServerArmed(pair.server); pair.client->AsyncConnect(&cr); - ASSERT_TRUE(pair.server->WaitForConnection(5000)); ASSERT_TRUE(pair.client->WaitForConnection(5000)); - const char* msg = "HDR_TEST"; - const size_t len = strlen(msg); + const char* msg = "HEADER_TEST"; + const size_t len = std::strlen(msg); - sdv::pointer buf; - buf.resize(len); - memcpy(buf.get(), msg, len); + sdv::pointer p; + p.resize(len); + std::memcpy(p.get(), msg, len); sdv::sequence> seq; - seq.push_back(buf); + seq.push_back(p); auto* sender = dynamic_cast(pair.client); ASSERT_NE(sender, nullptr); - EXPECT_TRUE(sender->SendData(seq)); - ASSERT_TRUE(sr.WaitForData(3000)); + ASSERT_TRUE(sr.WaitForData(3000)); auto recv = sr.Data(); + ASSERT_EQ(recv.size(), 1u); ASSERT_EQ(recv[0].size(), len); - EXPECT_EQ(memcmp(recv[0].get(), msg, len), 0); + EXPECT_EQ(std::memcmp(recv[0].get(), msg, len), 0); pair.client->Disconnect(); pair.server->Disconnect(); + mgr.Shutdown(); app.Shutdown(); } @@ -403,18 +420,20 @@ TEST(WinTunnelChannelMgnt, CreateEndpoint_LongPath_Normalized) app.SetRunningMode(); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); std::string veryLong(200, 'A'); const std::string raw ="C:\\Users\\" + veryLong + "\\AppData\\Local\\sdv\\tunnel_long_" + RandomHex() + ".sock"; - const std::string cs = "proto=tunnel;path=" + raw + ";"; + std::string tunnel = UniqueTunnel(); + const std::string cs = "proto=tunnel;path=" + raw + ";tunnel=" + tunnel + ";"; auto ep = mgr.CreateEndpoint(cs); ASSERT_FALSE(ep.ssConnectString.empty()); - // path must be normalized (basename only) - EXPECT_NE(ep.ssConnectString.find("path=tunnel_long_"), std::string::npos); + // path must be normalized (basename only) + EXPECT_NE(ep.ssConnectString.find("tunnel_long_"), std::string::npos); + EXPECT_NE(ep.ssConnectString.find("tunnel="), std::string::npos); sdv::TObjectPtr obj = mgr.Access(ep.ssConnectString); auto* server = obj.GetInterface(); diff --git a/tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp b/tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp index d646769..3704dad 100644 --- a/tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp +++ b/tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp @@ -77,6 +77,12 @@ inline std::string UniqueUds(const char* prefix) return MakeShortUdsPath((std::string(prefix) + "_" + RandomHex() + ".sock").c_str()); } +inline std::string UniqueTunnel() +{ + return "t_" + RandomHex(); +} + + inline void SpinUntilServerArmed(sdv::ipc::IConnect* server, uint32_t maxWaitMs = 300) { using namespace std::chrono; @@ -170,7 +176,12 @@ static TunnelPair CreateTunnelPair(CSocketsTunnelChannelMgnt& mgr, const std::st { TunnelPair out; - out.serverObj = mgr.Access(cs); + std::string serverCS = cs; + if (!serverCS.empty() && serverCS.back() != ';') + serverCS += ";"; + serverCS += "role=server;"; + + out.serverObj = mgr.Access(serverCS); out.server = out.serverObj ? out.serverObj.GetInterface() : nullptr; out.clientObj = mgr.Access(cs); @@ -187,7 +198,7 @@ TEST(WinTunnelIPC, InstantiateManager) ASSERT_TRUE(app.Startup("")); CSocketsTunnelChannelMgnt mgr; - EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.Initialize(sdv::SObjectInfo())); EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); EXPECT_NO_THROW(mgr.Shutdown()); @@ -204,11 +215,13 @@ TEST(WinTunnelIPC, BasicConnectDisconnect) app.SetRunningMode(); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - const std::string path = UniqueUds("tunnel_basic"); - const std::string cs = "proto=tunnel;path=" + path + ";"; + const std::string path = UniqueUds("tunnel_basic"); + const std::string tunnel = "test_" + RandomHex(); + const std::string cs = "proto=tunnel;path=" + path + ";tunnel=" + tunnel + ";"; + auto ep = mgr.CreateEndpoint(cs); ASSERT_FALSE(ep.ssConnectString.empty()); @@ -241,10 +254,12 @@ TEST(WinTunnelIPC, DataPath_SimpleHello_Tunnel) app.SetRunningMode(); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - const std::string cs = "proto=tunnel;path=" + UniqueUds("hello_t") + ";"; + const std::string tunnel = "test_" + RandomHex(); + const std::string cs = "proto=tunnel;path=" + UniqueUds("hello_t") + ";tunnel=" + tunnel + ";"; + auto ep = mgr.CreateEndpoint(cs); auto pair = CreateTunnelPair(mgr, ep.ssConnectString); ASSERT_NE(pair.server, nullptr); @@ -293,10 +308,11 @@ TEST(WinTunnelIPC, DataPath_MultiChunk_Tunnel) app.SetRunningMode(); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - const std::string cs = "proto=tunnel;path=" + UniqueUds("mc_t") + ";"; + const std::string tunnel = "test_" + RandomHex(); + const std::string cs = "proto=tunnel;path=" + UniqueUds("mc_t") + ";tunnel=" + tunnel + ";"; auto ep = mgr.CreateEndpoint(cs); auto pair = CreateTunnelPair(mgr, ep.ssConnectString); @@ -347,10 +363,12 @@ TEST(WinTunnelIPC, DataPath_LargePayload_Tunnel) app.SetRunningMode(); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - const std::string cs = "proto=tunnel;path=" + UniqueUds("big_t") + ";"; + const std::string tunnel = "test_" + RandomHex(); + const std::string cs = "proto=tunnel;path=" + UniqueUds("big_t") + ";tunnel=" + tunnel + ";"; + auto ep = mgr.CreateEndpoint(cs); auto pair = CreateTunnelPair(mgr, ep.ssConnectString); @@ -402,11 +420,11 @@ TEST(WinTunnelIPC, DataPath_HeaderStripped_Tunnel) app.SetRunningMode(); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - const std::string cs = - "proto=tunnel;path=" + UniqueUds("headerstrip") + ";"; + const std::string tunnel = "test_" + RandomHex(); + const std::string cs = "proto=tunnel;path=" + UniqueUds("headerstrip") + ";tunnel=" + tunnel + ";"; auto ep = mgr.CreateEndpoint(cs); auto pair = CreateTunnelPair(mgr, ep.ssConnectString); diff --git a/tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp b/tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp index ab3f40f..8696e1b 100644 --- a/tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp +++ b/tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp @@ -29,6 +29,20 @@ static std::string UniqueUds(const char* prefix) { return std::string("%LOCALAPPDATA%/sdv/") + buf; } +/*inline std::string RandomHex() +{ + std::mt19937_64 rng{std::random_device{}()}; + std::uniform_int_distribution dist; + std::ostringstream oss; + oss << std::hex << dist(rng); + return oss.str(); +} + +inline std::string UniqueTunnel() +{ + return "t_" + RandomHex(); +}*/ + // Negative: invalid connect string TEST(WinTunnelNegative, InvalidConnectString) { @@ -36,7 +50,7 @@ TEST(WinTunnelNegative, InvalidConnectString) ASSERT_TRUE(app.Startup(R"toml([LogHandler] ViewFilter = "Fatal")toml")); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); // Missing proto, missing path auto obj = mgr.Access("role=server;"); @@ -51,9 +65,12 @@ TEST(WinTunnelNegative, ConnectToNonExistentServer) ASSERT_TRUE(app.Startup(R"toml([LogHandler] ViewFilter = "Fatal")toml")); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - std::string cs = "proto=tunnel;path=" + UniqueUds("no_server") + ";"; + + std::string tunnel = "neg_" + std::to_string(rand()); + std::string cs = "proto=tunnel;path=" + UniqueUds("no_server") + ";tunnel=" + tunnel + ";"; + auto obj = mgr.Access(cs); if (!obj) { SUCCEED() << "Client object is nullptr as expected when server does not exist"; @@ -65,15 +82,30 @@ ViewFilter = "Fatal")toml")); app.Shutdown(); } +TEST(WinTunnelNegative, MissingTunnel) +{ + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(sdv::SObjectInfo()); + mgr.SetOperationMode(sdv::EOperationMode::running); + + // missing tunnel = invalid now + auto obj = mgr.Access("proto=tunnel;path=/tmp/x.sock;"); + EXPECT_EQ(obj, nullptr); +} + + // Edge: double disconnect TEST(WinTunnelEdge, DoubleDisconnect) { sdv::app::CAppControl app; ASSERT_TRUE(app.Startup("")); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - std::string cs = "proto=tunnel;path=" + UniqueUds("double_disc") + ";"; + + std::string tunnel = "neg_" + std::to_string(rand()); + std::string cs = "proto=tunnel;path=" + UniqueUds("double_disc") + ";tunnel=" + tunnel + ";"; + auto ep = mgr.CreateEndpoint(cs); auto obj = mgr.Access(ep.ssConnectString); auto* conn = obj->GetInterface(); @@ -91,9 +123,12 @@ TEST(WinTunnelEdge, RepeatedConnectDisconnect) ASSERT_TRUE(app.Startup(R"toml([LogHandler] ViewFilter = "Fatal")toml")); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - std::string cs = "proto=tunnel;path=" + UniqueUds("repeat") + ";"; + + std::string tunnel = "neg_" + std::to_string(rand()); + std::string cs = "proto=tunnel;path=" + UniqueUds("repeat") + ";tunnel=" + tunnel + ";"; + for (int i = 0; i < 3; ++i) { auto ep = mgr.CreateEndpoint(cs); // recreate endpoint every time sdv::TObjectPtr obj = mgr.Access(ep.ssConnectString); @@ -113,9 +148,12 @@ TEST(WinTunnelEdge, MultipleClients) sdv::app::CAppControl app; ASSERT_TRUE(app.Startup("")); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - std::string cs = "proto=tunnel;path=" + UniqueUds("multi_client") + ";"; + + std::string tunnel = "neg_" + std::to_string(rand()); + std::string cs = "proto=tunnel;path=" + UniqueUds("multi_client") + ";tunnel=" + tunnel + ";"; + auto ep = mgr.CreateEndpoint(cs); auto obj1 = mgr.Access(ep.ssConnectString); auto obj2 = mgr.Access(ep.ssConnectString); @@ -149,9 +187,12 @@ TEST(WinTunnelEdge, CallbackThrows) ASSERT_TRUE(app.Startup(R"toml([LogHandler] ViewFilter = "Fatal")toml")); CSocketsTunnelChannelMgnt mgr; - mgr.Initialize(""); + mgr.Initialize(sdv::SObjectInfo()); mgr.SetOperationMode(sdv::EOperationMode::running); - std::string cs = "proto=tunnel;path=" + UniqueUds("cb_throw") + ";"; + + std::string tunnel = "neg_" + std::to_string(rand()); + std::string cs = "proto=tunnel;path=" + UniqueUds("cb_throw") + ";tunnel=" + tunnel + ";"; + auto ep = mgr.CreateEndpoint(cs); auto obj = mgr.Access(ep.ssConnectString); auto* conn = obj->GetInterface();